Begin work on standalone websocket client
This commit is contained in:
parent
533dd49bdc
commit
38cf3f535d
10
.clangd
10
.clangd
|
@ -1,10 +0,0 @@
|
||||||
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"
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
{
|
|
||||||
"clangd.arguments": [
|
|
||||||
"--enable-config"
|
|
||||||
]
|
|
||||||
}
|
|
132
adapter.cpp
132
adapter.cpp
|
@ -1,132 +0,0 @@
|
||||||
#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
27
adapter.h
|
@ -1,27 +0,0 @@
|
||||||
#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
15
install.sh
|
@ -1,15 +0,0 @@
|
||||||
#! /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!"
|
|
107
keyers.cpp
107
keyers.cpp
|
@ -47,31 +47,24 @@ public:
|
||||||
|
|
||||||
class StraightKeyer: public Keyer {
|
class StraightKeyer: public Keyer {
|
||||||
public:
|
public:
|
||||||
Transmitter *output;
|
bool transmitting = false;
|
||||||
unsigned int ditDuration;
|
unsigned int ditDuration = 100;
|
||||||
bool txRelays[2];
|
bool txRelays[2];
|
||||||
|
|
||||||
StraightKeyer() {
|
StraightKeyer() {
|
||||||
this->Reset();
|
this->Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetOutput(Transmitter *output) {
|
char *Name() {
|
||||||
this->output = output;
|
return "straight";
|
||||||
}
|
|
||||||
|
|
||||||
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 Release() {
|
void Reset() {
|
||||||
this->Reset();
|
this->transmitting = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TxClosed() {
|
bool TxClosed() {
|
||||||
|
@ -90,22 +83,16 @@ 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;
|
||||||
bool nowClosed = this->TxClosed();
|
this->transmitting = 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tick(unsigned int millis) {};
|
bool Tick(unsigned long now) {
|
||||||
|
return this->transmitting;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class BugKeyer: public StraightKeyer {
|
class BugKeyer: public StraightKeyer {
|
||||||
|
@ -115,6 +102,10 @@ 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;
|
||||||
|
@ -131,10 +122,11 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tick(unsigned int millis) {
|
bool Tick(unsigned long now) {
|
||||||
if (this->nextPulse && (millis >= this->nextPulse)) {
|
if (this->nextPulse && (now >= this->nextPulse)) {
|
||||||
this->pulse(millis);
|
this->pulse(now);
|
||||||
}
|
}
|
||||||
|
return this->transmitting;
|
||||||
}
|
}
|
||||||
|
|
||||||
void beginPulsing() {
|
void beginPulsing() {
|
||||||
|
@ -143,7 +135,7 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void pulse(unsigned int millis) {
|
virtual void pulse(unsigned int now) {
|
||||||
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]) {
|
||||||
|
@ -152,7 +144,7 @@ public:
|
||||||
this->nextPulse = 0;
|
this->nextPulse = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->nextPulse = millis + this->ditDuration;
|
this->nextPulse = now + this->ditDuration;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -162,6 +154,10 @@ 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;
|
||||||
|
@ -204,7 +200,7 @@ public:
|
||||||
return this->nextRepeat;
|
return this->nextRepeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void pulse(unsigned int millis) {
|
virtual void pulse(unsigned int now) {
|
||||||
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
|
||||||
|
@ -219,7 +215,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextPulse) {
|
if (nextPulse) {
|
||||||
this->nextPulse = millis + nextPulse;
|
this->nextPulse = now + nextPulse;
|
||||||
} else {
|
} else {
|
||||||
this->nextPulse = 0;
|
this->nextPulse = 0;
|
||||||
}
|
}
|
||||||
|
@ -232,6 +228,10 @@ 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);
|
||||||
|
@ -253,7 +253,11 @@ public:
|
||||||
QSet queue;
|
QSet queue;
|
||||||
|
|
||||||
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);
|
||||||
|
@ -274,9 +278,12 @@ public:
|
||||||
|
|
||||||
class IambicKeyer: public ElBugKeyer {
|
class IambicKeyer: public ElBugKeyer {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
using ElBugKeyer::ElBugKeyer;
|
using ElBugKeyer::ElBugKeyer;
|
||||||
|
|
||||||
|
char *Name() {
|
||||||
|
return "iambic plain";
|
||||||
|
}
|
||||||
|
|
||||||
virtual int nextTx() {
|
virtual int nextTx() {
|
||||||
int next = ElBugKeyer::nextTx();
|
int next = ElBugKeyer::nextTx();
|
||||||
if (this->keyPressed[PADDLE_DIT] && this->keyPressed[PADDLE_DAH]) {
|
if (this->keyPressed[PADDLE_DIT] && this->keyPressed[PADDLE_DAH]) {
|
||||||
|
@ -292,6 +299,10 @@ 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);
|
||||||
|
@ -312,15 +323,21 @@ 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) {
|
if (pressed && (this->sending != key)) {
|
||||||
this->queue.add(key);
|
this->queue.add(key);
|
||||||
}
|
}
|
||||||
IambicKeyer::Key(key, pressed);
|
IambicKeyer::Key(key, pressed);
|
||||||
|
@ -333,7 +350,8 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this->queue.shift();
|
this->sending = this->queue.shift();
|
||||||
|
return this->sending;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -344,6 +362,10 @@ 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;
|
||||||
|
@ -381,8 +403,7 @@ IambicAKeyer iambicAKeyer = IambicAKeyer();
|
||||||
IambicBKeyer iambicBKeyer = IambicBKeyer();
|
IambicBKeyer iambicBKeyer = IambicBKeyer();
|
||||||
KeyaheadKeyer keyaheadKeyer = KeyaheadKeyer();
|
KeyaheadKeyer keyaheadKeyer = KeyaheadKeyer();
|
||||||
|
|
||||||
Keyer *keyers[] = {
|
Keyer *AllKeyers[] = {
|
||||||
NULL,
|
|
||||||
&straightKeyer,
|
&straightKeyer,
|
||||||
&bugKeyer,
|
&bugKeyer,
|
||||||
&elBugKeyer,
|
&elBugKeyer,
|
||||||
|
@ -394,12 +415,10 @@ Keyer *keyers[] = {
|
||||||
&keyaheadKeyer,
|
&keyaheadKeyer,
|
||||||
};
|
};
|
||||||
|
|
||||||
Keyer *GetKeyerByNumber(int n, Transmitter *output) {
|
Keyer *GetKeyerByNumber(int n) {
|
||||||
if (n >= len(keyers)) {
|
if (n >= len(AllKeyers)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Keyer *k = keyers[n];
|
return AllKeyers[n];
|
||||||
k->SetOutput(output);
|
}
|
||||||
return k;
|
|
||||||
}
|
|
26
keyers.h
26
keyers.h
|
@ -5,26 +5,18 @@
|
||||||
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 void SetOutput(Transmitter *output);
|
virtual char *Name() = 0;
|
||||||
virtual void Reset();
|
virtual void Reset() = 0;
|
||||||
virtual void SetDitDuration(unsigned int d);
|
virtual void SetDitDuration(unsigned int d) = 0;
|
||||||
virtual void Release();
|
virtual void Key(Paddle key, bool pressed) = 0;
|
||||||
virtual bool TxClosed();
|
|
||||||
virtual bool TxClosed(int relay);
|
// Tick updates internal state,
|
||||||
virtual void Tx(int relay, bool closed);
|
// and returns whether the keyer is transmitting at time now.
|
||||||
virtual void Key(Paddle key, bool pressed);
|
virtual bool Tick(unsigned long now) = 0;
|
||||||
virtual void Tick(unsigned int millis);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Keyer *GetKeyerByNumber(int n, Transmitter *output);
|
Keyer *GetKeyerByNumber(int n);
|
|
@ -1,11 +0,0 @@
|
||||||
#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;
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
#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;
|
|
||||||
};
|
|
209
vail-adapter.ino
209
vail-adapter.ino
|
@ -2,119 +2,164 @@
|
||||||
// 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/
|
||||||
|
|
||||||
// MIDIUSB - Version: Latest
|
#include <Arduino.h>
|
||||||
#include <MIDIUSB.h>
|
#include <Adafruit_NeoPixel.h>
|
||||||
#include <Keyboard.h>
|
#include <Adafruit_GFX.h>
|
||||||
#include <Adafruit_FreeTouch.h>
|
#include <Adafruit_SSD1306.h>
|
||||||
|
#include <Fonts/FreeSans9pt7b.h>
|
||||||
#include "bounce2.h"
|
#include "bounce2.h"
|
||||||
#include "touchbounce.h"
|
#include "keyers.h"
|
||||||
#include "adapter.h"
|
#include "polybuzzer.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 key = Bounce();
|
Bounce s1 = Bounce();
|
||||||
TouchBounce qt_dit = TouchBounce();
|
Bounce s2 = Bounce();
|
||||||
TouchBounce qt_dah = TouchBounce();
|
PolyBuzzer buzzer = PolyBuzzer(SPEAKER);
|
||||||
TouchBounce qt_key = TouchBounce();
|
|
||||||
VailAdapter adapter = VailAdapter(PIEZO);
|
Adafruit_NeoPixel pixels(1, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800);
|
||||||
|
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);
|
||||||
key.attach(KEY_PIN, INPUT_PULLUP);
|
s1.attach(S1_PIN, INPUT_PULLUP);
|
||||||
qt_dit.attach(QT_DIT_PIN);
|
s2.attach(S2_PIN, INPUT_PULLUP);
|
||||||
qt_dah.attach(QT_DAH_PIN);
|
pinMode(SOUNDER, OUTPUT);
|
||||||
qt_key.attach(QT_KEY_PIN);
|
|
||||||
|
|
||||||
Keyboard.begin();
|
Serial.begin(115200);
|
||||||
|
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,
|
||||||
// we just check to see if DAH is closed.
|
// 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) {
|
||||||
trs = true;
|
loadKeyer(0); // Straight
|
||||||
key = dit;
|
} else {
|
||||||
|
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() {
|
||||||
unsigned now = millis();
|
static unsigned long txBegin = 0;
|
||||||
midiEventPacket_t event = MidiUSB.read();
|
unsigned long now = millis();
|
||||||
|
|
||||||
setLED();
|
hello(now);
|
||||||
adapter.Tick(now);
|
|
||||||
|
|
||||||
if (event.header) {
|
s1.update();
|
||||||
adapter.HandleMIDI(event);
|
s2.update();
|
||||||
|
if (s1.fell() || s2.fell()) {
|
||||||
|
keyerCurrentN = loadKeyer(keyerCurrentN + 1);
|
||||||
|
// XXX: menu
|
||||||
}
|
}
|
||||||
|
|
||||||
// Monitor straight key pin
|
if (dit.update()) {
|
||||||
if (key.update() || qt_key.update()) {
|
keyerCurrent->Key(PADDLE_DIT, !dit.read());
|
||||||
bool pressed = !key.read() || qt_key.read();
|
|
||||||
adapter.HandlePaddle(PADDLE_STRAIGHT, pressed);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we made dit = dah, we have a straight key on the dit pin,
|
|
||||||
// so we skip other keys polling.
|
|
||||||
if (trs) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dit.update() || qt_dit.update()) {
|
|
||||||
bool pressed = !dit.read() || qt_dit.read();
|
|
||||||
adapter.HandlePaddle(PADDLE_DIT, pressed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dah.update() || qt_dah.update()) {
|
if (dah.update()) {
|
||||||
bool pressed = !dah.read() || qt_dah.read();
|
keyerCurrent->Key(PADDLE_DAH, !dah.read());
|
||||||
adapter.HandlePaddle(PADDLE_DAH, pressed);
|
}
|
||||||
|
|
||||||
|
bool sounding = keyerCurrent->Tick(now);
|
||||||
|
if (sounding) {
|
||||||
|
if (!txBegin) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue