Revert "Begin work on standalone websocket client"

This reverts commit 38cf3f535d.
This commit is contained in:
Neale Pickett 2024-10-20 17:51:51 -06:00
parent 048b4baf23
commit e7cfde1092
10 changed files with 359 additions and 199 deletions

10
.clangd Normal file
View File

@ -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"

5
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,5 @@
{
"clangd.arguments": [
"--enable-config"
]
}

132
adapter.cpp Normal file
View File

@ -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);
}
}

27
adapter.h Normal file
View File

@ -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);
};

15
install.sh Executable file
View File

@ -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!"

View File

@ -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;
} }

View File

@ -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);

11
touchbounce.cpp Normal file
View File

@ -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;
}

16
touchbounce.h Normal file
View File

@ -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;
};

View File

@ -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);
led(0xff0000);
}
} else {
if (txBegin) {
Serial.printf("%d %d\n", txBegin, now - txBegin);
txBegin = 0;
buzzer.NoTone(0);
digitalWrite(SOUNDER, LOW);
led();
} }
if (dah.update() || qt_dah.update()) {
bool pressed = !dah.read() || qt_dah.read();
adapter.HandlePaddle(PADDLE_DAH, pressed);
} }
} }