2020-05-16 10:58:40 -06:00
|
|
|
// Copyright 2020 Neale Pickett
|
|
|
|
// Distributed under the MIT license
|
|
|
|
// Please see https://github.com/nealey/vail-adapter/
|
|
|
|
|
2024-10-20 17:34:41 -06:00
|
|
|
#include <Arduino.h>
|
|
|
|
#include <Adafruit_NeoPixel.h>
|
|
|
|
#include <Adafruit_GFX.h>
|
|
|
|
#include <Adafruit_SSD1306.h>
|
|
|
|
#include <Fonts/FreeSans9pt7b.h>
|
2020-05-16 10:43:38 -06:00
|
|
|
#include "bounce2.h"
|
2024-10-20 17:34:41 -06:00
|
|
|
#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
|
2022-04-21 22:07:39 -06:00
|
|
|
#define MILLISECOND 1
|
|
|
|
#define SECOND (1 * MILLISECOND)
|
2020-05-16 10:43:38 -06:00
|
|
|
|
2022-04-21 22:07:39 -06:00
|
|
|
uint16_t iambicDelay = 80 * MILLISECOND;
|
2020-05-16 10:43:38 -06:00
|
|
|
Bounce dit = Bounce();
|
|
|
|
Bounce dah = Bounce();
|
2024-10-20 17:34:41 -06:00
|
|
|
Bounce s1 = Bounce();
|
|
|
|
Bounce s2 = Bounce();
|
|
|
|
PolyBuzzer buzzer = PolyBuzzer(SPEAKER);
|
2020-05-16 10:43:38 -06:00
|
|
|
|
2024-10-20 17:34:41 -06:00
|
|
|
Adafruit_NeoPixel pixels(1, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800);
|
|
|
|
Adafruit_SSD1306 display(128, 32, &Wire, -1);
|
2022-05-21 20:14:03 -06:00
|
|
|
|
2024-10-20 17:34:41 -06:00
|
|
|
// Tones default to a minor third
|
|
|
|
unsigned int txNote = 72; // C5
|
|
|
|
unsigned int rxNote = 69; // A4
|
2020-05-16 10:43:38 -06:00
|
|
|
|
2024-10-20 17:34:41 -06:00
|
|
|
int keyerCurrentN = 0;
|
|
|
|
Keyer *keyerCurrent = NULL;
|
|
|
|
|
|
|
|
void led(uint32_t color = 0) {
|
|
|
|
pixels.fill(color);
|
|
|
|
pixels.show();
|
2022-04-21 22:07:39 -06:00
|
|
|
}
|
|
|
|
|
2024-10-20 17:34:41 -06:00
|
|
|
|
2022-04-21 22:07:39 -06:00
|
|
|
// A reentrant doodad to blink out the letter V at startup
|
|
|
|
#define HELLO_BITS 0b0000101010111000
|
2024-10-20 17:34:41 -06:00
|
|
|
void hello(unsigned long now) {
|
|
|
|
static bool beeping = false;
|
|
|
|
int beat = now / iambicDelay;
|
2022-04-19 22:16:12 -06:00
|
|
|
|
2022-04-21 22:07:39 -06:00
|
|
|
if (beat < 16) {
|
2024-10-20 17:34:41 -06:00
|
|
|
bool on = HELLO_BITS & (1 << (15-beat));
|
|
|
|
if (on != beeping) {
|
2022-04-21 22:07:39 -06:00
|
|
|
if (on) {
|
2024-10-20 17:34:41 -06:00
|
|
|
buzzer.Note(0, rxNote);
|
|
|
|
led(0x220000);
|
2022-04-21 22:07:39 -06:00
|
|
|
} else {
|
2024-10-20 17:34:41 -06:00
|
|
|
buzzer.NoTone(0);
|
|
|
|
led(0);
|
2022-04-21 22:07:39 -06:00
|
|
|
}
|
2024-10-20 17:34:41 -06:00
|
|
|
beeping = on;
|
2022-04-21 22:07:39 -06:00
|
|
|
}
|
2022-04-19 22:16:12 -06:00
|
|
|
}
|
2020-05-16 10:43:38 -06:00
|
|
|
}
|
|
|
|
|
2024-10-20 17:34:41 -06:00
|
|
|
int loadKeyer(int n) {
|
|
|
|
keyerCurrent = GetKeyerByNumber(n);
|
|
|
|
if (NULL == keyerCurrent) {
|
|
|
|
n = 0;
|
|
|
|
keyerCurrent = GetKeyerByNumber(n);
|
|
|
|
}
|
2022-05-28 15:18:28 -06:00
|
|
|
|
2024-10-20 17:34:41 -06:00
|
|
|
display.clearDisplay();
|
|
|
|
display.setCursor(1, 12);
|
|
|
|
display.println(keyerCurrent->Name());
|
|
|
|
display.display();
|
2022-04-21 22:07:39 -06:00
|
|
|
|
2024-10-20 17:34:41 -06:00
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-05-16 10:43:38 -06:00
|
|
|
|
2024-10-20 17:34:41 -06:00
|
|
|
void setup() {
|
|
|
|
dit.attach(DIT_PIN, INPUT_PULLUP);
|
|
|
|
dah.attach(DAH_PIN, INPUT_PULLUP);
|
|
|
|
s1.attach(S1_PIN, INPUT_PULLUP);
|
|
|
|
s2.attach(S2_PIN, INPUT_PULLUP);
|
|
|
|
pinMode(SOUNDER, OUTPUT);
|
|
|
|
|
|
|
|
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,
|
|
|
|
// 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) {
|
|
|
|
loadKeyer(0); // Straight
|
|
|
|
} else {
|
|
|
|
loadKeyer(7); // Iambic B
|
2020-05-16 10:43:38 -06:00
|
|
|
}
|
2024-10-20 17:34:41 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
void loop() {
|
|
|
|
static unsigned long txBegin = 0;
|
|
|
|
unsigned long now = millis();
|
2022-04-21 22:07:39 -06:00
|
|
|
|
2024-10-20 17:34:41 -06:00
|
|
|
hello(now);
|
|
|
|
|
|
|
|
s1.update();
|
|
|
|
s2.update();
|
|
|
|
if (s1.fell() || s2.fell()) {
|
|
|
|
keyerCurrentN = loadKeyer(keyerCurrentN + 1);
|
|
|
|
// XXX: menu
|
2022-04-21 22:07:39 -06:00
|
|
|
}
|
|
|
|
|
2024-10-20 17:34:41 -06:00
|
|
|
if (dit.update()) {
|
|
|
|
keyerCurrent->Key(PADDLE_DIT, !dit.read());
|
2020-05-16 10:43:38 -06:00
|
|
|
}
|
|
|
|
|
2024-10-20 17:34:41 -06:00
|
|
|
if (dah.update()) {
|
|
|
|
keyerCurrent->Key(PADDLE_DAH, !dah.read());
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
2020-05-16 10:43:38 -06:00
|
|
|
}
|
|
|
|
}
|