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!"
|
99
keyers.cpp
99
keyers.cpp
|
@ -47,31 +47,24 @@ public:
|
|||
|
||||
class StraightKeyer: public Keyer {
|
||||
public:
|
||||
Transmitter *output;
|
||||
unsigned int ditDuration;
|
||||
bool transmitting = false;
|
||||
unsigned int ditDuration = 100;
|
||||
bool txRelays[2];
|
||||
|
||||
StraightKeyer() {
|
||||
this->Reset();
|
||||
}
|
||||
|
||||
void SetOutput(Transmitter *output) {
|
||||
this->output = output;
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
if (this->output) {
|
||||
this->output->EndTx();
|
||||
}
|
||||
this->ditDuration = 100;
|
||||
char *Name() {
|
||||
return "straight";
|
||||
}
|
||||
|
||||
void SetDitDuration(unsigned int duration) {
|
||||
this->ditDuration = duration;
|
||||
}
|
||||
|
||||
void Release() {
|
||||
this->Reset();
|
||||
void Reset() {
|
||||
this->transmitting = false;
|
||||
}
|
||||
|
||||
bool TxClosed() {
|
||||
|
@ -90,22 +83,16 @@ public:
|
|||
void Tx(int relay, bool closed) {
|
||||
bool wasClosed = this->TxClosed();
|
||||
this->txRelays[relay] = closed;
|
||||
bool nowClosed = this->TxClosed();
|
||||
|
||||
if (wasClosed != nowClosed) {
|
||||
if (nowClosed) {
|
||||
this->output->BeginTx();
|
||||
} else {
|
||||
this->output->EndTx();
|
||||
}
|
||||
}
|
||||
this->transmitting = this->TxClosed();
|
||||
}
|
||||
|
||||
void Key(Paddle key, bool pressed) {
|
||||
this->Tx(key, pressed);
|
||||
}
|
||||
|
||||
void Tick(unsigned int millis) {};
|
||||
bool Tick(unsigned long now) {
|
||||
return this->transmitting;
|
||||
}
|
||||
};
|
||||
|
||||
class BugKeyer: public StraightKeyer {
|
||||
|
@ -115,6 +102,10 @@ public:
|
|||
|
||||
using StraightKeyer::StraightKeyer;
|
||||
|
||||
char *Name() {
|
||||
return "bug";
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
StraightKeyer::Reset();
|
||||
this->nextPulse = 0;
|
||||
|
@ -131,10 +122,11 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void Tick(unsigned int millis) {
|
||||
if (this->nextPulse && (millis >= this->nextPulse)) {
|
||||
this->pulse(millis);
|
||||
bool Tick(unsigned long now) {
|
||||
if (this->nextPulse && (now >= this->nextPulse)) {
|
||||
this->pulse(now);
|
||||
}
|
||||
return this->transmitting;
|
||||
}
|
||||
|
||||
void beginPulsing() {
|
||||
|
@ -143,7 +135,7 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
virtual void pulse(unsigned int millis) {
|
||||
virtual void pulse(unsigned int now) {
|
||||
if (this->TxClosed(0)) {
|
||||
this->Tx(0, false);
|
||||
} else if (this->keyPressed[0]) {
|
||||
|
@ -152,7 +144,7 @@ public:
|
|||
this->nextPulse = 0;
|
||||
return;
|
||||
}
|
||||
this->nextPulse = millis + this->ditDuration;
|
||||
this->nextPulse = now + this->ditDuration;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -162,6 +154,10 @@ public:
|
|||
|
||||
using BugKeyer::BugKeyer;
|
||||
|
||||
char *Name() {
|
||||
return "el bug";
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
BugKeyer::Reset();
|
||||
this->nextRepeat = -1;
|
||||
|
@ -204,7 +200,7 @@ public:
|
|||
return this->nextRepeat;
|
||||
}
|
||||
|
||||
virtual void pulse(unsigned int millis) {
|
||||
virtual void pulse(unsigned int now) {
|
||||
int nextPulse = 0;
|
||||
if (this->TxClosed(0)) {
|
||||
// Pause if we're currently transmitting
|
||||
|
@ -219,7 +215,7 @@ public:
|
|||
}
|
||||
|
||||
if (nextPulse) {
|
||||
this->nextPulse = millis + nextPulse;
|
||||
this->nextPulse = now + nextPulse;
|
||||
} else {
|
||||
this->nextPulse = 0;
|
||||
}
|
||||
|
@ -232,6 +228,10 @@ public:
|
|||
|
||||
using ElBugKeyer::ElBugKeyer;
|
||||
|
||||
char *Name() {
|
||||
return "ultimatic";
|
||||
}
|
||||
|
||||
void Key(Paddle key, bool pressed) {
|
||||
if (pressed) {
|
||||
this->queue.add(key);
|
||||
|
@ -254,6 +254,10 @@ public:
|
|||
|
||||
using ElBugKeyer::ElBugKeyer;
|
||||
|
||||
char *Name() {
|
||||
return "single dot";
|
||||
}
|
||||
|
||||
void Key(Paddle key, bool pressed) {
|
||||
if (pressed && (key == PADDLE_DIT)) {
|
||||
this->queue.add(key);
|
||||
|
@ -274,9 +278,12 @@ public:
|
|||
|
||||
class IambicKeyer: public ElBugKeyer {
|
||||
public:
|
||||
|
||||
using ElBugKeyer::ElBugKeyer;
|
||||
|
||||
char *Name() {
|
||||
return "iambic plain";
|
||||
}
|
||||
|
||||
virtual int nextTx() {
|
||||
int next = ElBugKeyer::nextTx();
|
||||
if (this->keyPressed[PADDLE_DIT] && this->keyPressed[PADDLE_DAH]) {
|
||||
|
@ -292,6 +299,10 @@ public:
|
|||
|
||||
using IambicKeyer::IambicKeyer;
|
||||
|
||||
char *Name() {
|
||||
return "iambic a";
|
||||
}
|
||||
|
||||
void Key(Paddle key, bool pressed) {
|
||||
if (pressed && (key == PADDLE_DIT)) {
|
||||
this->queue.add(key);
|
||||
|
@ -312,15 +323,21 @@ public:
|
|||
class IambicBKeyer: public IambicKeyer {
|
||||
public:
|
||||
QSet queue;
|
||||
int sending;
|
||||
|
||||
using IambicKeyer::IambicKeyer;
|
||||
|
||||
char *Name() {
|
||||
return "iambic b";
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
this->sending = -1;
|
||||
IambicKeyer::Reset();
|
||||
}
|
||||
|
||||
void Key(Paddle key, bool pressed) {
|
||||
if (pressed) {
|
||||
if (pressed && (this->sending != key)) {
|
||||
this->queue.add(key);
|
||||
}
|
||||
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;
|
||||
|
||||
char *Name() {
|
||||
return "keyahead";
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
ElBugKeyer::Reset();
|
||||
this->qlen = 0;
|
||||
|
@ -381,8 +403,7 @@ IambicAKeyer iambicAKeyer = IambicAKeyer();
|
|||
IambicBKeyer iambicBKeyer = IambicBKeyer();
|
||||
KeyaheadKeyer keyaheadKeyer = KeyaheadKeyer();
|
||||
|
||||
Keyer *keyers[] = {
|
||||
NULL,
|
||||
Keyer *AllKeyers[] = {
|
||||
&straightKeyer,
|
||||
&bugKeyer,
|
||||
&elBugKeyer,
|
||||
|
@ -394,12 +415,10 @@ Keyer *keyers[] = {
|
|||
&keyaheadKeyer,
|
||||
};
|
||||
|
||||
Keyer *GetKeyerByNumber(int n, Transmitter *output) {
|
||||
if (n >= len(keyers)) {
|
||||
Keyer *GetKeyerByNumber(int n) {
|
||||
if (n >= len(AllKeyers)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Keyer *k = keyers[n];
|
||||
k->SetOutput(output);
|
||||
return k;
|
||||
return AllKeyers[n];
|
||||
}
|
26
keyers.h
26
keyers.h
|
@ -5,26 +5,18 @@
|
|||
typedef enum {
|
||||
PADDLE_DIT = 0,
|
||||
PADDLE_DAH = 1,
|
||||
PADDLE_STRAIGHT,
|
||||
} Paddle;
|
||||
|
||||
class Transmitter {
|
||||
public:
|
||||
virtual void BeginTx();
|
||||
virtual void EndTx();
|
||||
};
|
||||
|
||||
class Keyer {
|
||||
public:
|
||||
virtual void SetOutput(Transmitter *output);
|
||||
virtual void Reset();
|
||||
virtual void SetDitDuration(unsigned int d);
|
||||
virtual void Release();
|
||||
virtual bool TxClosed();
|
||||
virtual bool TxClosed(int relay);
|
||||
virtual void Tx(int relay, bool closed);
|
||||
virtual void Key(Paddle key, bool pressed);
|
||||
virtual void Tick(unsigned int millis);
|
||||
virtual char *Name() = 0;
|
||||
virtual void Reset() = 0;
|
||||
virtual void SetDitDuration(unsigned int d) = 0;
|
||||
virtual void Key(Paddle key, bool pressed) = 0;
|
||||
|
||||
// Tick updates internal state,
|
||||
// and returns whether the keyer is transmitting at time now.
|
||||
virtual bool Tick(unsigned long now) = 0;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
201
vail-adapter.ino
201
vail-adapter.ino
|
@ -2,119 +2,164 @@
|
|||
// Distributed under the MIT license
|
||||
// Please see https://github.com/nealey/vail-adapter/
|
||||
|
||||
// MIDIUSB - Version: Latest
|
||||
#include <MIDIUSB.h>
|
||||
#include <Keyboard.h>
|
||||
#include <Adafruit_FreeTouch.h>
|
||||
#include <Arduino.h>
|
||||
#include <Adafruit_NeoPixel.h>
|
||||
#include <Adafruit_GFX.h>
|
||||
#include <Adafruit_SSD1306.h>
|
||||
#include <Fonts/FreeSans9pt7b.h>
|
||||
#include "bounce2.h"
|
||||
#include "touchbounce.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
|
||||
#include "keyers.h"
|
||||
#include "polybuzzer.h"
|
||||
|
||||
#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 SECOND (1 * MILLISECOND)
|
||||
|
||||
bool trs = false; // true if a TRS plug is in a TRRS jack
|
||||
uint16_t iambicDelay = 80 * MILLISECOND;
|
||||
Bounce dit = Bounce();
|
||||
Bounce dah = Bounce();
|
||||
Bounce key = Bounce();
|
||||
TouchBounce qt_dit = TouchBounce();
|
||||
TouchBounce qt_dah = TouchBounce();
|
||||
TouchBounce qt_key = TouchBounce();
|
||||
VailAdapter adapter = VailAdapter(PIEZO);
|
||||
Bounce s1 = Bounce();
|
||||
Bounce s2 = Bounce();
|
||||
PolyBuzzer buzzer = PolyBuzzer(SPEAKER);
|
||||
|
||||
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() {
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
dit.attach(DIT_PIN, INPUT_PULLUP);
|
||||
dah.attach(DAH_PIN, INPUT_PULLUP);
|
||||
key.attach(KEY_PIN, INPUT_PULLUP);
|
||||
qt_dit.attach(QT_DIT_PIN);
|
||||
qt_dah.attach(QT_DAH_PIN);
|
||||
qt_key.attach(QT_KEY_PIN);
|
||||
s1.attach(S1_PIN, INPUT_PULLUP);
|
||||
s2.attach(S2_PIN, INPUT_PULLUP);
|
||||
pinMode(SOUNDER, OUTPUT);
|
||||
|
||||
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,
|
||||
// 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
|
||||
// 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++) {
|
||||
hello(millis());
|
||||
delay(20);
|
||||
dah.update();
|
||||
}
|
||||
if (dah.read() == LOW) {
|
||||
trs = true;
|
||||
key = dit;
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
loadKeyer(0); // Straight
|
||||
} else {
|
||||
noTone(PIEZO);
|
||||
loadKeyer(7); // Iambic B
|
||||
}
|
||||
beepin = on;
|
||||
}
|
||||
}
|
||||
|
||||
digitalWrite(LED_BUILTIN, on?LED_ON:LED_OFF);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
unsigned now = millis();
|
||||
midiEventPacket_t event = MidiUSB.read();
|
||||
static unsigned long txBegin = 0;
|
||||
unsigned long now = millis();
|
||||
|
||||
setLED();
|
||||
adapter.Tick(now);
|
||||
hello(now);
|
||||
|
||||
if (event.header) {
|
||||
adapter.HandleMIDI(event);
|
||||
s1.update();
|
||||
s2.update();
|
||||
if (s1.fell() || s2.fell()) {
|
||||
keyerCurrentN = loadKeyer(keyerCurrentN + 1);
|
||||
// XXX: menu
|
||||
}
|
||||
|
||||
// Monitor straight key pin
|
||||
if (key.update() || qt_key.update()) {
|
||||
bool pressed = !key.read() || qt_key.read();
|
||||
adapter.HandlePaddle(PADDLE_STRAIGHT, pressed);
|
||||
if (dit.update()) {
|
||||
keyerCurrent->Key(PADDLE_DIT, !dit.read());
|
||||
}
|
||||
|
||||
// If we made dit = dah, we have a straight key on the dit pin,
|
||||
// so we skip other keys polling.
|
||||
if (trs) {
|
||||
return;
|
||||
if (dah.update()) {
|
||||
keyerCurrent->Key(PADDLE_DAH, !dah.read());
|
||||
}
|
||||
|
||||
if (dit.update() || qt_dit.update()) {
|
||||
bool pressed = !dit.read() || qt_dit.read();
|
||||
adapter.HandlePaddle(PADDLE_DIT, 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();
|
||||
}
|
||||
|
||||
if (dah.update() || qt_dah.update()) {
|
||||
bool pressed = !dah.read() || qt_dah.read();
|
||||
adapter.HandlePaddle(PADDLE_DAH, pressed);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue