WrathPak
·
2025-08-18
adapter.cpp
1#include <Arduino.h>
2#include <Keyboard.h>
3#include <MIDIUSB.h>
4#include <cstddef>
5#include "keyers.h"
6#include "adapter.h"
7#include "polybuzzer.h"
8
9// For SAMD21 software reset if needed by other parts of code
10#if defined(ARDUINO_ARCH_SAMD)
11// NVIC_SystemReset() is typically available through Arduino.h / CMSIS includes
12#endif
13
14extern void saveSettingsToEEPROM(uint8_t keyerType, uint16_t ditDuration, uint8_t txNote);
15
16VailAdapter::VailAdapter(unsigned int PiezoPin) {
17this->buzzer = new PolyBuzzer(PiezoPin);
18this->buzzerEnabled = true;
19this->radioModeActive = false;
20this->keyIsPressed = false;
21this->keyPressStartTime = 0;
22this->lastDitTime = 0;
23this->ditPressCount = 0;
24this->lastCapDahTime = 0;
25this->capDahPressCount = 0;
26this->radioDitState = false;
27this->radioDahState = false;
28this->keyboardMode = true;
29this->keyer = NULL;
30this->txNote = DEFAULT_TONE_NOTE;
31this->ditDuration = DEFAULT_ADAPTER_DIT_DURATION_MS;
32}
33
34bool VailAdapter::KeyboardMode() {
35return this->keyboardMode;
36}
37
38uint8_t VailAdapter::getCurrentKeyerType() const {
39return getKeyerNumber(this->keyer);
40}
41
42uint16_t VailAdapter::getDitDuration() const {
43return this->ditDuration;
44}
45
46uint8_t VailAdapter::getTxNote() const {
47return this->txNote;
48}
49
50bool VailAdapter::isBuzzerEnabled() const {
51return this->buzzerEnabled;
52}
53
54bool VailAdapter::isRadioModeActive() const {
55return this->radioModeActive;
56}
57
58void VailAdapter::ResetDitCounter() {
59this->ditPressCount = 0;
60}
61
62void VailAdapter::ResetDahCounter() {
63this->capDahPressCount = 0;
64}
65
66// Corrected MIDI key event function
67void VailAdapter::midiKey(uint8_t key, bool down) {
68uint8_t header;
69uint8_t status_byte;
70uint8_t velocity;
71
72if (down) { // Note On
73 header = 0x09; // CIN = 9 (Note On) for USB MIDI event packet
74 status_byte = 0x90; // MIDI Status = 0x90 (Note On, Channel 1)
75 velocity = 0x7F; // Standard velocity for Note On
76} else { // Note Off
77 header = 0x08; // CIN = 8 (Note Off) for USB MIDI event packet
78 status_byte = 0x80; // MIDI Status = 0x80 (Note Off, Channel 1)
79 velocity = 0x00; // Velocity for Note Off (0x00 is common)
80}
81// Construct the MIDI event packet for MIDIUSB library
82midiEventPacket_t event = {header, status_byte, key, velocity};
83MidiUSB.sendMIDI(event);
84MidiUSB.flush();
85}
86
87void VailAdapter::keyboardKey(uint8_t key, bool down) {
88if (down) {
89Keyboard.press(key);
90} else {
91Keyboard.release(key);
92}
93}
94
95#ifdef HAS_RADIO_OUTPUT
96void VailAdapter::setRadioDit(bool active) {
97digitalWrite(RADIO_DIT_PIN, active ? RADIO_ACTIVE_LEVEL : RADIO_INACTIVE_LEVEL);
98}
99
100void VailAdapter::setRadioDah(bool active) {
101digitalWrite(RADIO_DAH_PIN, active ? RADIO_ACTIVE_LEVEL : RADIO_INACTIVE_LEVEL);
102}
103#else
104void VailAdapter::setRadioDit(bool active) {(void)active;}
105void VailAdapter::setRadioDah(bool active) {(void)active;}
106#endif
107
108void VailAdapter::BeginTx() {
109if (!keyIsPressed) {
110keyIsPressed = true;
111if (!this->radioModeActive) {
112if (this->keyPressStartTime == 0) {
113this->keyPressStartTime = millis();
114}
115}
116}
117
118if (this->buzzerEnabled && !this->radioModeActive) {
119 this->buzzer->Note(0, this->txNote);
120}
121
122if (!this->radioModeActive) {
123 if (this->keyboardMode) {
124 this->keyboardKey(KEY_LEFT_CTRL, true);
125 } else {
126 this->midiKey(0, true);
127 }
128}
129}
130
131void VailAdapter::EndTx() {
132if (keyIsPressed) {
133keyIsPressed = false;
134if (!this->radioModeActive) {
135this->keyPressStartTime = 0;
136}
137}
138
139this->buzzer->NoTone(0);
140
141if (!this->radioModeActive) {
142 if (this->keyboardMode) {
143 this->keyboardKey(KEY_LEFT_CTRL, false);
144 } else {
145 this->midiKey(0, false);
146 }
147}
148}
149
150void VailAdapter::DisableBuzzer() {
151this->buzzer->NoTone(0);
152this->buzzer->Note(1, 70); delay(100);
153this->buzzer->Note(1, 65); delay(100);
154this->buzzer->Note(1, 60); delay(100);
155this->buzzer->NoTone(1);
156this->buzzerEnabled = false;
157Serial.println("Buzzer Disabled");
158}
159
160void VailAdapter::ToggleRadioMode() {
161#ifdef HAS_RADIO_OUTPUT
162this->radioModeActive = !this->radioModeActive;
163
164if (keyer) keyer->Release();
165if (keyIsPressed) EndTx();
166
167setRadioDit(false);
168setRadioDah(false);
169radioDitState = false;
170radioDahState = false;
171keyIsPressed = false;
172
173if (this->radioModeActive) {
174 Serial.println("Radio Mode Activated (Sidetone Disabled)");
175 this->buzzer->NoTone(0);
176 this->buzzer->Note(1, 60); delay(100);
177 this->buzzer->Note(1, 65); delay(100);
178 this->buzzer->Note(1, 70); delay(100);
179 this->buzzer->NoTone(1);
180} else {
181 Serial.println("Radio Mode Deactivated. Resetting controller...");
182 this->buzzer->Note(1, 70); delay(100);
183 this->buzzer->Note(1, 65); delay(100);
184 this->buzzer->Note(1, 60); delay(100);
185 this->buzzer->NoTone(1);
186 delay(100);
187
188 NVIC_SystemReset();
189}
190#else
191Serial.println("Radio output not configured. Radio mode unavailable.");
192this->buzzer->Tone(1, 100); delay(200); this->buzzer->NoTone(1);
193#endif
194}
195
196void VailAdapter::ProcessPaddleInput(Paddle paddle, bool pressed, bool isCapacitive) {
197unsigned long currentTime = millis();
198
199if (paddle == PADDLE_DIT && pressed && this->buzzerEnabled) {
200 if (currentTime - this->lastDitTime < SPAM_DISABLE_WINDOW) {
201 this->ditPressCount++;
202 if (this->ditPressCount >= DIT_SPAM_COUNT_BUZZER_DISABLE) {
203 this->DisableBuzzer();
204 this->ditPressCount = 0;
205 }
206 } else {
207 this->ditPressCount = 1;
208 }
209 this->lastDitTime = currentTime;
210}
211#ifdef HAS_RADIO_OUTPUT
212if (paddle == PADDLE_DAH && isCapacitive && pressed) {
213if (currentTime - this->lastCapDahTime < SPAM_DISABLE_WINDOW) {
214this->capDahPressCount++;
215if (this->capDahPressCount >= DAH_SPAM_COUNT_RADIO_MODE) {
216this->ToggleRadioMode();
217this->capDahPressCount = 0;
218}
219} else {
220this->capDahPressCount = 1;
221}
222this->lastCapDahTime = currentTime;
223} else if (paddle == PADDLE_DAH && !isCapacitive && pressed) {
224this->capDahPressCount = 0;
225} else if (paddle == PADDLE_DIT && pressed) {
226this->capDahPressCount = 0;
227}
228#endif
229
230if (this->radioModeActive) {
231#ifdef HAS_RADIO_OUTPUT
232bool radioKeyIsActiveBefore = radioDitState || radioDahState;
233
234 if (paddle == PADDLE_DIT) {
235 radioDitState = pressed;
236 setRadioDit(radioDitState);
237 } else if (paddle == PADDLE_DAH) {
238 radioDahState = pressed;
239 setRadioDah(radioDahState);
240 } else if (paddle == PADDLE_STRAIGHT) {
241 radioDitState = pressed;
242 setRadioDit(pressed);
243 }
244
245 keyIsPressed = radioDitState || radioDahState;
246
247 if (!keyIsPressed && radioKeyIsActiveBefore) {
248 this->buzzer->NoTone(0);
249 }
250#endif
251} else {
252if (paddle == PADDLE_STRAIGHT) {
253if (pressed) BeginTx(); else EndTx();
254} else {
255if (this->keyer) {
256this->keyer->Key(paddle, pressed);
257} else {
258bool currentPaddleActivity = false;
259if (paddle == PADDLE_DIT) {
260if (this->keyboardMode) this->keyboardKey(DIT_KEYBOARD_KEY, pressed);
261else this->midiKey(1, pressed);
262if (pressed) currentPaddleActivity = true;
263} else if (paddle == PADDLE_DAH) {
264if (this->keyboardMode) this->keyboardKey(DAH_KEYBOARD_KEY, pressed);
265else this->midiKey(2, pressed);
266if (pressed) currentPaddleActivity = true;
267}
268
269 if (currentPaddleActivity) {
270 if(!keyIsPressed) BeginTx();
271 } else {
272 if(keyIsPressed) EndTx();
273 }
274 }
275 }
276 }
277}
278
279void VailAdapter::HandleMIDI(midiEventPacket_t event) {
280uint16_t msg = (event.byte1 << 8) | (event.byte2 << 0);
281switch (event.byte1) {
282case 0xB0:
283switch (event.byte2) {
284case 0:
285this->keyboardMode = (event.byte3 > 0x3f);
286Serial.print("Keyboard mode: "); Serial.println(this->keyboardMode ? "ON" : "OFF");
287MidiUSB.sendMIDI(event);
288break;
289case 1:
290this->ditDuration = event.byte3 * 2 * MILLISECOND;
291if (this->keyer) {
292this->keyer->SetDitDuration(this->ditDuration);
293}
294Serial.print("Dit duration set to: "); Serial.println(this->ditDuration);
295saveSettingsToEEPROM(getCurrentKeyerType(), this->ditDuration, this->txNote);
296break;
297case 2:
298this->txNote = event.byte3;
299Serial.print("TX Note set to: "); Serial.println(this->txNote);
300
301saveSettingsToEEPROM(getCurrentKeyerType(), this->ditDuration, this->txNote);
302break;
303}
304break;
305case 0xC0:
306if (this->keyer) {
307this->keyer->Release();
308this->keyer = NULL;
309}
310this->keyer = GetKeyerByNumber(event.byte2, this);
311if (this->keyer) {
312this->keyer->SetDitDuration(this->ditDuration);
313Serial.print("Keyer mode set to: "); Serial.println(event.byte2);
314} else {
315Serial.print("Keyer mode set to passthrough (or invalid): "); Serial.println(event.byte2);
316}
317saveSettingsToEEPROM(event.byte2, this->ditDuration, this->txNote);
318break;
319case 0x80:
320if (this->buzzerEnabled && !this->radioModeActive) this->buzzer->NoTone(1);
321break;
322case 0x90:
323if (this->buzzerEnabled && !this->radioModeActive) this->buzzer->Note(1, event.byte2);
324break;
325}
326}
327
328void VailAdapter::Tick(unsigned int currentMillis) {
329if (!radioModeActive && keyIsPressed && this->buzzerEnabled && this->keyPressStartTime > 0) {
330if (currentMillis - this->keyPressStartTime >= KEY_HOLD_DISABLE_THRESHOLD) {
331this->DisableBuzzer();
332}
333}
334
335if (this->keyer && !this->radioModeActive) {
336 this->keyer->Tick(currentMillis);
337}
338}
339