Revert "Begin work on standalone websocket client"
This reverts commit 38cf3f535d
.
This commit is contained in:
parent
048b4baf23
commit
e7cfde1092
|
@ -0,0 +1,10 @@
|
||||||
|
CompileFlags:
|
||||||
|
Add:
|
||||||
|
- "--include-directory=/opt/arduino/hardware/arduino/avr/cores/arduino"
|
||||||
|
- "--include-directory=/opt/arduino/hardware/arduino/avr/variants/standard"
|
||||||
|
- "--include-directory=/opt/arduino/hardware/arduino/avr/libraries/HID/src"
|
||||||
|
- "--include-directory=/opt/arduino/hardware/tools/avr/avr/include"
|
||||||
|
- "--include-directory=/opt/arduino/libraries/Keyboard/src"
|
||||||
|
- "--include-directory=/opt/arduino/libraries/HID/src"
|
||||||
|
- "--include-directory=/home/dartcatcher/Arduino/libraries/MIDIUSB/src"
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"clangd.arguments": [
|
||||||
|
"--enable-config"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <Keyboard.h>
|
||||||
|
#include <MIDIUSB.h>
|
||||||
|
#include <cstddef>
|
||||||
|
#include "keyers.h"
|
||||||
|
#include "adapter.h"
|
||||||
|
#include "polybuzzer.h"
|
||||||
|
|
||||||
|
#define MILLISECOND 1
|
||||||
|
#define SECOND (1000 * MILLISECOND)
|
||||||
|
|
||||||
|
VailAdapter::VailAdapter(unsigned int PiezoPin) {
|
||||||
|
this->buzzer = new PolyBuzzer(PiezoPin);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VailAdapter::KeyboardMode() {
|
||||||
|
return this->keyboardMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send a MIDI Key Event
|
||||||
|
void VailAdapter::midiKey(uint8_t key, bool down) {
|
||||||
|
midiEventPacket_t event = {uint8_t(down?9:8), uint8_t(down?0x90:0x80), key, 0x7f};
|
||||||
|
MidiUSB.sendMIDI(event);
|
||||||
|
MidiUSB.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send a keyboard key event
|
||||||
|
void VailAdapter::keyboardKey(uint8_t key, bool down) {
|
||||||
|
if (down) {
|
||||||
|
Keyboard.press(key);
|
||||||
|
} else {
|
||||||
|
Keyboard.release(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Begin transmitting
|
||||||
|
void VailAdapter::BeginTx() {
|
||||||
|
this->buzzer->Note(0, this->txNote);
|
||||||
|
if (this->keyboardMode) {
|
||||||
|
this->keyboardKey(KEY_LEFT_CTRL, true);
|
||||||
|
} else {
|
||||||
|
this->midiKey(0, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop transmitting
|
||||||
|
void VailAdapter::EndTx() {
|
||||||
|
this->buzzer->NoTone(0);
|
||||||
|
if (this->keyboardMode) {
|
||||||
|
this->keyboardKey(KEY_LEFT_CTRL, false);
|
||||||
|
} else {
|
||||||
|
this->midiKey(0, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle a paddle being pressed.
|
||||||
|
//
|
||||||
|
// The caller needs to debounce keys and deal with keys wired in parallel.
|
||||||
|
void VailAdapter::HandlePaddle(Paddle paddle, bool pressed) {
|
||||||
|
switch (paddle) {
|
||||||
|
case PADDLE_STRAIGHT:
|
||||||
|
if (pressed) {
|
||||||
|
this->BeginTx();
|
||||||
|
} else {
|
||||||
|
this->EndTx();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case PADDLE_DIT:
|
||||||
|
if (this->keyer) {
|
||||||
|
this->keyer->Key(paddle, pressed);
|
||||||
|
} else if (this->keyboardMode) {
|
||||||
|
this->keyboardKey(KEY_LEFT_CTRL, pressed);
|
||||||
|
} else {
|
||||||
|
this->midiKey(1, pressed);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PADDLE_DAH:
|
||||||
|
if (this->keyer) {
|
||||||
|
this->keyer->Key(paddle, pressed);
|
||||||
|
} else if (this->keyboardMode) {
|
||||||
|
this->keyboardKey(KEY_RIGHT_CTRL, pressed);
|
||||||
|
} else {
|
||||||
|
this->midiKey(2, pressed);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle a MIDI event.
|
||||||
|
//
|
||||||
|
// We act as a MIDI
|
||||||
|
void VailAdapter::HandleMIDI(midiEventPacket_t event) {
|
||||||
|
uint16_t msg = (event.byte1 << 8) | (event.byte2 << 0);
|
||||||
|
switch (event.byte1) {
|
||||||
|
case 0xB0: // Controller Change
|
||||||
|
switch (event.byte2) {
|
||||||
|
case 0: // turn keyboard mode on/off
|
||||||
|
this->keyboardMode = (event.byte3 > 0x3f);
|
||||||
|
MidiUSB.sendMIDI(event); // Send it back to acknowledge
|
||||||
|
break;
|
||||||
|
case 1: // set dit duration (0-254) *2ms
|
||||||
|
this->ditDuration = event.byte3 * 2 * MILLISECOND;
|
||||||
|
if (this->keyer) {
|
||||||
|
this->keyer->SetDitDuration(this->ditDuration);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2: // set tx note
|
||||||
|
this->txNote = event.byte3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0xC0: // Program Change
|
||||||
|
if (this->keyer) {
|
||||||
|
this->keyer->Release();
|
||||||
|
}
|
||||||
|
this->keyer = GetKeyerByNumber(event.byte2, this);
|
||||||
|
this->keyer->SetDitDuration(this->ditDuration);
|
||||||
|
break;
|
||||||
|
case 0x80: // Note off
|
||||||
|
this->buzzer->NoTone(1);
|
||||||
|
break;
|
||||||
|
case 0x90: // Note on
|
||||||
|
this->buzzer->Note(1, event.byte2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VailAdapter::Tick(unsigned millis) {
|
||||||
|
if (this->keyer) {
|
||||||
|
this->keyer->Tick(millis);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <MIDIUSB.h>
|
||||||
|
#include "keyers.h"
|
||||||
|
#include "polybuzzer.h"
|
||||||
|
|
||||||
|
class VailAdapter: public Transmitter {
|
||||||
|
private:
|
||||||
|
unsigned int txNote = 69;
|
||||||
|
unsigned int ditDuration = 100;
|
||||||
|
bool keyboardMode = true;
|
||||||
|
Keyer *keyer = NULL;
|
||||||
|
PolyBuzzer *buzzer = NULL;
|
||||||
|
|
||||||
|
void midiKey(uint8_t key, bool down);
|
||||||
|
void keyboardKey(uint8_t key, bool down);
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
VailAdapter(unsigned int PiezoPin);
|
||||||
|
bool KeyboardMode();
|
||||||
|
void HandlePaddle(Paddle key, bool pressed);
|
||||||
|
void HandleMIDI(midiEventPacket_t event);
|
||||||
|
void BeginTx();
|
||||||
|
void EndTx();
|
||||||
|
void Tick(unsigned millis);
|
||||||
|
};
|
|
@ -0,0 +1,15 @@
|
||||||
|
#! /bin/sh
|
||||||
|
|
||||||
|
src=$1
|
||||||
|
dst=$2
|
||||||
|
|
||||||
|
info=$dst/INFO_UF2.txt
|
||||||
|
echo -n "Waiting for $info to appear..."
|
||||||
|
while ! [ -f $info ]; do
|
||||||
|
echo -n "."
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
echo "👍"
|
||||||
|
|
||||||
|
cp $src $dst
|
||||||
|
echo "Installed!"
|
103
keyers.cpp
103
keyers.cpp
|
@ -47,24 +47,31 @@ public:
|
||||||
|
|
||||||
class StraightKeyer: public Keyer {
|
class StraightKeyer: public Keyer {
|
||||||
public:
|
public:
|
||||||
bool transmitting = false;
|
Transmitter *output;
|
||||||
unsigned int ditDuration = 100;
|
unsigned int ditDuration;
|
||||||
bool txRelays[2];
|
bool txRelays[2];
|
||||||
|
|
||||||
StraightKeyer() {
|
StraightKeyer() {
|
||||||
this->Reset();
|
this->Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
char *Name() {
|
void SetOutput(Transmitter *output) {
|
||||||
return "straight";
|
this->output = output;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reset() {
|
||||||
|
if (this->output) {
|
||||||
|
this->output->EndTx();
|
||||||
|
}
|
||||||
|
this->ditDuration = 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetDitDuration(unsigned int duration) {
|
void SetDitDuration(unsigned int duration) {
|
||||||
this->ditDuration = duration;
|
this->ditDuration = duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reset() {
|
void Release() {
|
||||||
this->transmitting = false;
|
this->Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TxClosed() {
|
bool TxClosed() {
|
||||||
|
@ -83,16 +90,22 @@ public:
|
||||||
void Tx(int relay, bool closed) {
|
void Tx(int relay, bool closed) {
|
||||||
bool wasClosed = this->TxClosed();
|
bool wasClosed = this->TxClosed();
|
||||||
this->txRelays[relay] = closed;
|
this->txRelays[relay] = closed;
|
||||||
this->transmitting = this->TxClosed();
|
bool nowClosed = this->TxClosed();
|
||||||
|
|
||||||
|
if (wasClosed != nowClosed) {
|
||||||
|
if (nowClosed) {
|
||||||
|
this->output->BeginTx();
|
||||||
|
} else {
|
||||||
|
this->output->EndTx();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Key(Paddle key, bool pressed) {
|
void Key(Paddle key, bool pressed) {
|
||||||
this->Tx(key, pressed);
|
this->Tx(key, pressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Tick(unsigned long now) {
|
void Tick(unsigned int millis) {};
|
||||||
return this->transmitting;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class BugKeyer: public StraightKeyer {
|
class BugKeyer: public StraightKeyer {
|
||||||
|
@ -102,10 +115,6 @@ public:
|
||||||
|
|
||||||
using StraightKeyer::StraightKeyer;
|
using StraightKeyer::StraightKeyer;
|
||||||
|
|
||||||
char *Name() {
|
|
||||||
return "bug";
|
|
||||||
}
|
|
||||||
|
|
||||||
void Reset() {
|
void Reset() {
|
||||||
StraightKeyer::Reset();
|
StraightKeyer::Reset();
|
||||||
this->nextPulse = 0;
|
this->nextPulse = 0;
|
||||||
|
@ -122,11 +131,10 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Tick(unsigned long now) {
|
void Tick(unsigned int millis) {
|
||||||
if (this->nextPulse && (now >= this->nextPulse)) {
|
if (this->nextPulse && (millis >= this->nextPulse)) {
|
||||||
this->pulse(now);
|
this->pulse(millis);
|
||||||
}
|
}
|
||||||
return this->transmitting;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void beginPulsing() {
|
void beginPulsing() {
|
||||||
|
@ -135,7 +143,7 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void pulse(unsigned int now) {
|
virtual void pulse(unsigned int millis) {
|
||||||
if (this->TxClosed(0)) {
|
if (this->TxClosed(0)) {
|
||||||
this->Tx(0, false);
|
this->Tx(0, false);
|
||||||
} else if (this->keyPressed[0]) {
|
} else if (this->keyPressed[0]) {
|
||||||
|
@ -144,7 +152,7 @@ public:
|
||||||
this->nextPulse = 0;
|
this->nextPulse = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->nextPulse = now + this->ditDuration;
|
this->nextPulse = millis + this->ditDuration;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -154,10 +162,6 @@ public:
|
||||||
|
|
||||||
using BugKeyer::BugKeyer;
|
using BugKeyer::BugKeyer;
|
||||||
|
|
||||||
char *Name() {
|
|
||||||
return "el bug";
|
|
||||||
}
|
|
||||||
|
|
||||||
void Reset() {
|
void Reset() {
|
||||||
BugKeyer::Reset();
|
BugKeyer::Reset();
|
||||||
this->nextRepeat = -1;
|
this->nextRepeat = -1;
|
||||||
|
@ -200,7 +204,7 @@ public:
|
||||||
return this->nextRepeat;
|
return this->nextRepeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void pulse(unsigned int now) {
|
virtual void pulse(unsigned int millis) {
|
||||||
int nextPulse = 0;
|
int nextPulse = 0;
|
||||||
if (this->TxClosed(0)) {
|
if (this->TxClosed(0)) {
|
||||||
// Pause if we're currently transmitting
|
// Pause if we're currently transmitting
|
||||||
|
@ -215,7 +219,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextPulse) {
|
if (nextPulse) {
|
||||||
this->nextPulse = now + nextPulse;
|
this->nextPulse = millis + nextPulse;
|
||||||
} else {
|
} else {
|
||||||
this->nextPulse = 0;
|
this->nextPulse = 0;
|
||||||
}
|
}
|
||||||
|
@ -228,10 +232,6 @@ public:
|
||||||
|
|
||||||
using ElBugKeyer::ElBugKeyer;
|
using ElBugKeyer::ElBugKeyer;
|
||||||
|
|
||||||
char *Name() {
|
|
||||||
return "ultimatic";
|
|
||||||
}
|
|
||||||
|
|
||||||
void Key(Paddle key, bool pressed) {
|
void Key(Paddle key, bool pressed) {
|
||||||
if (pressed) {
|
if (pressed) {
|
||||||
this->queue.add(key);
|
this->queue.add(key);
|
||||||
|
@ -254,10 +254,6 @@ public:
|
||||||
|
|
||||||
using ElBugKeyer::ElBugKeyer;
|
using ElBugKeyer::ElBugKeyer;
|
||||||
|
|
||||||
char *Name() {
|
|
||||||
return "single dot";
|
|
||||||
}
|
|
||||||
|
|
||||||
void Key(Paddle key, bool pressed) {
|
void Key(Paddle key, bool pressed) {
|
||||||
if (pressed && (key == PADDLE_DIT)) {
|
if (pressed && (key == PADDLE_DIT)) {
|
||||||
this->queue.add(key);
|
this->queue.add(key);
|
||||||
|
@ -278,11 +274,8 @@ public:
|
||||||
|
|
||||||
class IambicKeyer: public ElBugKeyer {
|
class IambicKeyer: public ElBugKeyer {
|
||||||
public:
|
public:
|
||||||
using ElBugKeyer::ElBugKeyer;
|
|
||||||
|
|
||||||
char *Name() {
|
using ElBugKeyer::ElBugKeyer;
|
||||||
return "iambic plain";
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual int nextTx() {
|
virtual int nextTx() {
|
||||||
int next = ElBugKeyer::nextTx();
|
int next = ElBugKeyer::nextTx();
|
||||||
|
@ -299,10 +292,6 @@ public:
|
||||||
|
|
||||||
using IambicKeyer::IambicKeyer;
|
using IambicKeyer::IambicKeyer;
|
||||||
|
|
||||||
char *Name() {
|
|
||||||
return "iambic a";
|
|
||||||
}
|
|
||||||
|
|
||||||
void Key(Paddle key, bool pressed) {
|
void Key(Paddle key, bool pressed) {
|
||||||
if (pressed && (key == PADDLE_DIT)) {
|
if (pressed && (key == PADDLE_DIT)) {
|
||||||
this->queue.add(key);
|
this->queue.add(key);
|
||||||
|
@ -323,21 +312,15 @@ public:
|
||||||
class IambicBKeyer: public IambicKeyer {
|
class IambicBKeyer: public IambicKeyer {
|
||||||
public:
|
public:
|
||||||
QSet queue;
|
QSet queue;
|
||||||
int sending;
|
|
||||||
|
|
||||||
using IambicKeyer::IambicKeyer;
|
using IambicKeyer::IambicKeyer;
|
||||||
|
|
||||||
char *Name() {
|
|
||||||
return "iambic b";
|
|
||||||
}
|
|
||||||
|
|
||||||
void Reset() {
|
void Reset() {
|
||||||
this->sending = -1;
|
|
||||||
IambicKeyer::Reset();
|
IambicKeyer::Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Key(Paddle key, bool pressed) {
|
void Key(Paddle key, bool pressed) {
|
||||||
if (pressed && (this->sending != key)) {
|
if (pressed) {
|
||||||
this->queue.add(key);
|
this->queue.add(key);
|
||||||
}
|
}
|
||||||
IambicKeyer::Key(key, pressed);
|
IambicKeyer::Key(key, pressed);
|
||||||
|
@ -350,8 +333,7 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->sending = this->queue.shift();
|
return this->queue.shift();
|
||||||
return this->sending;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -362,10 +344,6 @@ public:
|
||||||
|
|
||||||
using ElBugKeyer::ElBugKeyer;
|
using ElBugKeyer::ElBugKeyer;
|
||||||
|
|
||||||
char *Name() {
|
|
||||||
return "keyahead";
|
|
||||||
}
|
|
||||||
|
|
||||||
void Reset() {
|
void Reset() {
|
||||||
ElBugKeyer::Reset();
|
ElBugKeyer::Reset();
|
||||||
this->qlen = 0;
|
this->qlen = 0;
|
||||||
|
@ -403,7 +381,8 @@ IambicAKeyer iambicAKeyer = IambicAKeyer();
|
||||||
IambicBKeyer iambicBKeyer = IambicBKeyer();
|
IambicBKeyer iambicBKeyer = IambicBKeyer();
|
||||||
KeyaheadKeyer keyaheadKeyer = KeyaheadKeyer();
|
KeyaheadKeyer keyaheadKeyer = KeyaheadKeyer();
|
||||||
|
|
||||||
Keyer *AllKeyers[] = {
|
Keyer *keyers[] = {
|
||||||
|
NULL,
|
||||||
&straightKeyer,
|
&straightKeyer,
|
||||||
&bugKeyer,
|
&bugKeyer,
|
||||||
&elBugKeyer,
|
&elBugKeyer,
|
||||||
|
@ -415,10 +394,12 @@ Keyer *AllKeyers[] = {
|
||||||
&keyaheadKeyer,
|
&keyaheadKeyer,
|
||||||
};
|
};
|
||||||
|
|
||||||
Keyer *GetKeyerByNumber(int n) {
|
Keyer *GetKeyerByNumber(int n, Transmitter *output) {
|
||||||
if (n >= len(AllKeyers)) {
|
if (n >= len(keyers)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return AllKeyers[n];
|
Keyer *k = keyers[n];
|
||||||
|
k->SetOutput(output);
|
||||||
|
return k;
|
||||||
}
|
}
|
26
keyers.h
26
keyers.h
|
@ -5,18 +5,26 @@
|
||||||
typedef enum {
|
typedef enum {
|
||||||
PADDLE_DIT = 0,
|
PADDLE_DIT = 0,
|
||||||
PADDLE_DAH = 1,
|
PADDLE_DAH = 1,
|
||||||
|
PADDLE_STRAIGHT,
|
||||||
} Paddle;
|
} Paddle;
|
||||||
|
|
||||||
|
class Transmitter {
|
||||||
|
public:
|
||||||
|
virtual void BeginTx();
|
||||||
|
virtual void EndTx();
|
||||||
|
};
|
||||||
|
|
||||||
class Keyer {
|
class Keyer {
|
||||||
public:
|
public:
|
||||||
virtual char *Name() = 0;
|
virtual void SetOutput(Transmitter *output);
|
||||||
virtual void Reset() = 0;
|
virtual void Reset();
|
||||||
virtual void SetDitDuration(unsigned int d) = 0;
|
virtual void SetDitDuration(unsigned int d);
|
||||||
virtual void Key(Paddle key, bool pressed) = 0;
|
virtual void Release();
|
||||||
|
virtual bool TxClosed();
|
||||||
// Tick updates internal state,
|
virtual bool TxClosed(int relay);
|
||||||
// and returns whether the keyer is transmitting at time now.
|
virtual void Tx(int relay, bool closed);
|
||||||
virtual bool Tick(unsigned long now) = 0;
|
virtual void Key(Paddle key, bool pressed);
|
||||||
|
virtual void Tick(unsigned int millis);
|
||||||
};
|
};
|
||||||
|
|
||||||
Keyer *GetKeyerByNumber(int n);
|
Keyer *GetKeyerByNumber(int n, Transmitter *output);
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
#include "touchbounce.h"
|
||||||
|
|
||||||
|
void TouchBounce::attach(int pin) {
|
||||||
|
this->qt = Adafruit_FreeTouch(pin, OVERSAMPLE_2, RESISTOR_0, FREQ_MODE_SPREAD);
|
||||||
|
this->qt.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TouchBounce::readCurrentState() {
|
||||||
|
int val = this->qt.measure();
|
||||||
|
return val > QT_THRESHOLD;
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Adafruit_FreeTouch.h>
|
||||||
|
#include "bounce2.h"
|
||||||
|
|
||||||
|
#define QT_THRESHOLD 450
|
||||||
|
|
||||||
|
class TouchBounce: public Bounce {
|
||||||
|
public:
|
||||||
|
// attach a touch pin
|
||||||
|
void attach(int pin);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool readCurrentState();
|
||||||
|
Adafruit_FreeTouch qt;
|
||||||
|
};
|
205
vail-adapter.ino
205
vail-adapter.ino
|
@ -2,164 +2,119 @@
|
||||||
// Distributed under the MIT license
|
// Distributed under the MIT license
|
||||||
// Please see https://github.com/nealey/vail-adapter/
|
// Please see https://github.com/nealey/vail-adapter/
|
||||||
|
|
||||||
#include <Arduino.h>
|
// MIDIUSB - Version: Latest
|
||||||
#include <Adafruit_NeoPixel.h>
|
#include <MIDIUSB.h>
|
||||||
#include <Adafruit_GFX.h>
|
#include <Keyboard.h>
|
||||||
#include <Adafruit_SSD1306.h>
|
#include <Adafruit_FreeTouch.h>
|
||||||
#include <Fonts/FreeSans9pt7b.h>
|
|
||||||
#include "bounce2.h"
|
#include "bounce2.h"
|
||||||
#include "keyers.h"
|
#include "touchbounce.h"
|
||||||
#include "polybuzzer.h"
|
#include "adapter.h"
|
||||||
|
|
||||||
|
#define DIT_PIN 2
|
||||||
|
#define DAH_PIN 1
|
||||||
|
#define KEY_PIN 0
|
||||||
|
#define QT_DIT_PIN A6
|
||||||
|
#define QT_DAH_PIN A7
|
||||||
|
#define QT_KEY_PIN A8
|
||||||
|
#define PIEZO 10
|
||||||
|
#define LED_ON false // Xiao inverts this logic for some reason
|
||||||
|
#define LED_OFF (!LED_ON)
|
||||||
|
|
||||||
|
#define DIT_KEYBOARD_KEY KEY_LEFT_CTRL
|
||||||
|
#define DAH_KEYBOARD_KEY KEY_RIGHT_CTRL
|
||||||
|
#define TONE 3000
|
||||||
|
|
||||||
#define DIT_PIN 1
|
|
||||||
#define DAH_PIN 3
|
|
||||||
#define S1_PIN 4
|
|
||||||
#define S2_PIN 9
|
|
||||||
#define SPEAKER 7
|
|
||||||
#define SOUNDER 8
|
|
||||||
#define MILLISECOND 1
|
#define MILLISECOND 1
|
||||||
#define SECOND (1 * MILLISECOND)
|
#define SECOND (1 * MILLISECOND)
|
||||||
|
|
||||||
|
bool trs = false; // true if a TRS plug is in a TRRS jack
|
||||||
uint16_t iambicDelay = 80 * MILLISECOND;
|
uint16_t iambicDelay = 80 * MILLISECOND;
|
||||||
Bounce dit = Bounce();
|
Bounce dit = Bounce();
|
||||||
Bounce dah = Bounce();
|
Bounce dah = Bounce();
|
||||||
Bounce s1 = Bounce();
|
Bounce key = Bounce();
|
||||||
Bounce s2 = Bounce();
|
TouchBounce qt_dit = TouchBounce();
|
||||||
PolyBuzzer buzzer = PolyBuzzer(SPEAKER);
|
TouchBounce qt_dah = TouchBounce();
|
||||||
|
TouchBounce qt_key = TouchBounce();
|
||||||
Adafruit_NeoPixel pixels(1, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800);
|
VailAdapter adapter = VailAdapter(PIEZO);
|
||||||
Adafruit_SSD1306 display(128, 32, &Wire, -1);
|
|
||||||
|
|
||||||
// Tones default to a minor third
|
|
||||||
unsigned int txNote = 72; // C5
|
|
||||||
unsigned int rxNote = 69; // A4
|
|
||||||
|
|
||||||
int keyerCurrentN = 0;
|
|
||||||
Keyer *keyerCurrent = NULL;
|
|
||||||
|
|
||||||
void led(uint32_t color = 0) {
|
|
||||||
pixels.fill(color);
|
|
||||||
pixels.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// A reentrant doodad to blink out the letter V at startup
|
|
||||||
#define HELLO_BITS 0b0000101010111000
|
|
||||||
void hello(unsigned long now) {
|
|
||||||
static bool beeping = false;
|
|
||||||
int beat = now / iambicDelay;
|
|
||||||
|
|
||||||
if (beat < 16) {
|
|
||||||
bool on = HELLO_BITS & (1 << (15-beat));
|
|
||||||
if (on != beeping) {
|
|
||||||
if (on) {
|
|
||||||
buzzer.Note(0, rxNote);
|
|
||||||
led(0x220000);
|
|
||||||
} else {
|
|
||||||
buzzer.NoTone(0);
|
|
||||||
led(0);
|
|
||||||
}
|
|
||||||
beeping = on;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int loadKeyer(int n) {
|
|
||||||
keyerCurrent = GetKeyerByNumber(n);
|
|
||||||
if (NULL == keyerCurrent) {
|
|
||||||
n = 0;
|
|
||||||
keyerCurrent = GetKeyerByNumber(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
display.clearDisplay();
|
|
||||||
display.setCursor(1, 12);
|
|
||||||
display.println(keyerCurrent->Name());
|
|
||||||
display.display();
|
|
||||||
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
|
pinMode(LED_BUILTIN, OUTPUT);
|
||||||
dit.attach(DIT_PIN, INPUT_PULLUP);
|
dit.attach(DIT_PIN, INPUT_PULLUP);
|
||||||
dah.attach(DAH_PIN, INPUT_PULLUP);
|
dah.attach(DAH_PIN, INPUT_PULLUP);
|
||||||
s1.attach(S1_PIN, INPUT_PULLUP);
|
key.attach(KEY_PIN, INPUT_PULLUP);
|
||||||
s2.attach(S2_PIN, INPUT_PULLUP);
|
qt_dit.attach(QT_DIT_PIN);
|
||||||
pinMode(SOUNDER, OUTPUT);
|
qt_dah.attach(QT_DAH_PIN);
|
||||||
|
qt_key.attach(QT_KEY_PIN);
|
||||||
|
|
||||||
Serial.begin(115200);
|
Keyboard.begin();
|
||||||
Serial.println("Vail Client / " __DATE__ " " __TIME__);
|
|
||||||
|
|
||||||
pixels.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
|
|
||||||
pixels.setBrightness(20); // not so bright
|
|
||||||
|
|
||||||
Wire.begin();
|
|
||||||
display.begin(SSD1306_SWITCHCAPVCC); // This never returns false, in my testing
|
|
||||||
display.setFont(&FreeSans9pt7b);
|
|
||||||
display.setTextColor(SSD1306_WHITE);
|
|
||||||
|
|
||||||
display.clearDisplay();
|
|
||||||
display.setCursor(1, 12);
|
|
||||||
display.println(__DATE__);
|
|
||||||
display.display();
|
|
||||||
|
|
||||||
// To auto-sense a straight key in a TRRS jack,
|
// To auto-sense a straight key in a TRRS jack,
|
||||||
// check to see if DAH is closed.
|
// we just check to see if DAH is closed.
|
||||||
// The sleeve on the straight key's TRS plug
|
// The sleeve on the straight key's TRS plug
|
||||||
// will short the second ring to the sleeve.
|
// will short the second ring to the sleeve.
|
||||||
// In this case, set the keyer to straight.
|
|
||||||
// Since keyers are edge triggered,
|
|
||||||
// nothing will happen if the key stays closed.
|
|
||||||
// If it opens, it falls back to being a straight key.
|
|
||||||
for (int i = 0; i < 16; i++) {
|
for (int i = 0; i < 16; i++) {
|
||||||
hello(millis());
|
|
||||||
delay(20);
|
delay(20);
|
||||||
dah.update();
|
dah.update();
|
||||||
}
|
}
|
||||||
if (dah.read() == LOW) {
|
if (dah.read() == LOW) {
|
||||||
loadKeyer(0); // Straight
|
trs = true;
|
||||||
} else {
|
key = dit;
|
||||||
loadKeyer(7); // Iambic B
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A reentrant doodad to blink out the letter V at startup
|
||||||
|
// After startup, display the status of the keyboard
|
||||||
|
#define HELLO_BITS 0b0000101010111000
|
||||||
|
void setLED() {
|
||||||
|
static bool beepin = false;
|
||||||
|
int beat = millis() / iambicDelay;
|
||||||
|
bool on = adapter.KeyboardMode(); // If we're not in intro, display status of keyboard
|
||||||
|
|
||||||
|
if (beat < 16) {
|
||||||
|
on = HELLO_BITS & (1 << (15-beat));
|
||||||
|
if (on != beepin) {
|
||||||
|
if (on) {
|
||||||
|
tone(PIEZO, TONE);
|
||||||
|
} else {
|
||||||
|
noTone(PIEZO);
|
||||||
|
}
|
||||||
|
beepin = on;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
digitalWrite(LED_BUILTIN, on?LED_ON:LED_OFF);
|
||||||
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
static unsigned long txBegin = 0;
|
unsigned now = millis();
|
||||||
unsigned long now = millis();
|
midiEventPacket_t event = MidiUSB.read();
|
||||||
|
|
||||||
hello(now);
|
setLED();
|
||||||
|
adapter.Tick(now);
|
||||||
|
|
||||||
s1.update();
|
if (event.header) {
|
||||||
s2.update();
|
adapter.HandleMIDI(event);
|
||||||
if (s1.fell() || s2.fell()) {
|
|
||||||
keyerCurrentN = loadKeyer(keyerCurrentN + 1);
|
|
||||||
// XXX: menu
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dit.update()) {
|
// Monitor straight key pin
|
||||||
keyerCurrent->Key(PADDLE_DIT, !dit.read());
|
if (key.update() || qt_key.update()) {
|
||||||
|
bool pressed = !key.read() || qt_key.read();
|
||||||
|
adapter.HandlePaddle(PADDLE_STRAIGHT, pressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dah.update()) {
|
// If we made dit = dah, we have a straight key on the dit pin,
|
||||||
keyerCurrent->Key(PADDLE_DAH, !dah.read());
|
// so we skip other keys polling.
|
||||||
|
if (trs) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sounding = keyerCurrent->Tick(now);
|
if (dit.update() || qt_dit.update()) {
|
||||||
if (sounding) {
|
bool pressed = !dit.read() || qt_dit.read();
|
||||||
if (!txBegin) {
|
adapter.HandlePaddle(PADDLE_DIT, pressed);
|
||||||
txBegin = now;
|
}
|
||||||
buzzer.Note(0, txNote);
|
|
||||||
digitalWrite(SOUNDER, HIGH);
|
if (dah.update() || qt_dah.update()) {
|
||||||
led(0xff0000);
|
bool pressed = !dah.read() || qt_dah.read();
|
||||||
}
|
adapter.HandlePaddle(PADDLE_DAH, pressed);
|
||||||
} else {
|
|
||||||
if (txBegin) {
|
|
||||||
Serial.printf("%d %d\n", txBegin, now - txBegin);
|
|
||||||
txBegin = 0;
|
|
||||||
buzzer.NoTone(0);
|
|
||||||
digitalWrite(SOUNDER, LOW);
|
|
||||||
led();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue