Skip to content

Commit

Permalink
Attempt to get local keyer modes going
Browse files Browse the repository at this point in the history
  • Loading branch information
nealey committed May 23, 2022
1 parent 3521861 commit 012ee5a
Show file tree
Hide file tree
Showing 13 changed files with 789 additions and 86 deletions.
10 changes: 10 additions & 0 deletions .clangd
Original file line number Diff line number Diff line change
@@ -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 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"clangd.arguments": [
"--enable-config"
]
}
21 changes: 16 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
FQBN = Seeeduino:samd:seeed_XIAO_m0
FQBN_qtpy = adafruit:samd:adafruit_qtpy_m0
FQBN_xiao = Seeeduino:samd:seeed_XIAO_m0
UF2_MOUNT = /mnt/chromeos/removable/Arduino
ARDUINO_DIR = /app/Arduino
BUILDER = flatpak run --command ${ARDUINO_DIR}/arduino-builder cc.arduino.arduinoide

default: build/vail-adapter.xiao.uf2
default: build/vail-adapter.qtpy.uf2 build/vail-adapter.xiao.uf2
install: build/vail-adapter.xiao.uf2
./install.sh $< $(UF2_MOUNT)

clean:
rm -rf build/*

# uf2conv.py is covered by an MIT license.
build/uf2conv.py: build/uf2families.json
mkdir -p build
Expand All @@ -16,13 +20,19 @@ build/uf2families.json:
mkdir -p build
curl -L https://raw.githubusercontent.com/microsoft/uf2/master/utils/$(@F) > $@

%.xiao.uf2: %.ino.bin build/uf2conv.py
%.xiao.uf2: %.xiao.bin build/uf2conv.py
build/uf2conv.py -b 0x2000 -c -o $@ $<

%.qtpy.uf2: %.qtpy.bin build/uf2conv.py
build/uf2conv.py -b 0x2000 -c -o $@ $<

build/%.bin: % *.cpp *.h
build/%.qtpy.bin: FQBN = adafruit:samd:adafruit_qtpy_m0
build/%.xiao.bin: FQBN = Seeeduino:samd:seeed_XIAO_m0
build/vail-adapter.%.bin: vail-adapter.ino *.cpp *.h
mkdir -p build/$*
arduino-builder \
-build-cache ~/.cache/arduino \
-build-path build \
-build-path build/$* \
-core-api-version 10813 \
-fqbn $(FQBN) \
-hardware ~/.arduino15/packages \
Expand All @@ -34,6 +44,7 @@ build/%.bin: % *.cpp *.h
-libraries ~/Arduino/libraries \
-compile \
$<
mv build/$*/vail-adapter.ino.bin $@

upload: vail-adapter.ino
arduino --upload --board $(FQBN) $<
113 changes: 113 additions & 0 deletions adapter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#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);
this->txToneFrequency = 440;
}

// Send a MIDI Key Event
void VailAdapter::midiKey(uint8_t key, bool down) {
midiEventPacket_t event = {down?9:8, 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->Tone(0, this->txToneFrequency);
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;
break;
}
break;
case 0xC0: // Program Change
this->keyer = GetKeyerByNumber(event.byte2, this);
break;
case 0x80: // Note off
this->buzzer->NoTone(1);
break;
case 0x90: // Note on
this->buzzer->Note(1, event.byte2);
break;
}
}
25 changes: 25 additions & 0 deletions adapter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#pragma once

#include <MIDIUSB.h>
#include "keyers.h"
#include "polybuzzer.h"

class VailAdapter: public Transmitter {
private:
unsigned int txToneFrequency;
unsigned int ditDuration = 100;
bool keyboardMode = false;
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);
void HandlePaddle(Paddle key, bool pressed);
void HandleMIDI(midiEventPacket_t event);
void BeginTx();
void EndTx();
};
55 changes: 55 additions & 0 deletions doc/MIDI.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Vail MIDI Protocol

When it boots,
the Vail adapter sends left and right Control keyboard key up and down events.
It also shows up as a MIDI device.

The Vail web site sends MIDI control commands to enable MIDI keyer mode,
tells the keyer what sideband pitch to generate,
and can set the keyer mode.


## Controller 0 - MIDI Mode

`b0 00 ff` will enable MIDI mode and disable Keyboard mode

`b0 00 00` will enable Keyboard mode and disable MIDI mode


## Controller 1 - dit length

`b0 00 xx` will set the dit duration to `xx` times 2 milliseconds


## Controller 2 - sidetone note

`b0 00 xx` will play note `xx` as the sidetone note


## Program Change

`c0 xx` will change the keyer mode to `xx`.


### Keyer Modes

* 0: passthrough (sends C# and D for dit and dah)
* 1: cootie / straight key
* 2: bug
* 3: electric bug
* 4: single dot
* 5: ultimatic
* 6: plain iambic
* 7: iambic a
* 8: iambic b
* 9: keyahead

Any other mode will set to passthrough.


## Notes (key down / key up)

`90 00 xx` will begin playing note `xx`
`80 00 xx` will end playing note `xx`

These work just like a regular MIDI synthesizer.
24 changes: 0 additions & 24 deletions doc/tech-notes.md

This file was deleted.

Loading

0 comments on commit 012ee5a

Please sign in to comment.