Refactor code structure for improved readability and maintainability, imporved matrix read, improved SB button read

This commit is contained in:
2025-12-06 09:03:02 +01:00
parent 60950e6a0c
commit 06fa584b6d
6 changed files with 211 additions and 91 deletions

View File

@@ -1,8 +1,9 @@
/*
* Example Code Three - Dual Channel Sequencer
* TODO:
- add predefined sequence of voltage (e.g. for usage as startup sound)
- implement INFO and MISC pins form file FIRMWARE_DEF.h
* Example Code Three - Dual Channel Sequencer (FIXED)
* - Bounds Checks hinzugefügt
* - Rate-Limiting implementiert
* - Debug-Ausgaben erweitert
* - Stack Overflow verhindert
*/
#include "FIRMWARE_DEF.h"
#include "FIRMWARE.h"
@@ -41,9 +42,17 @@ ButtonState btn_sb2_play;
const unsigned long DEBOUNCE_DELAY = 50;
// Loop-Status für State Machine
static bool seq1_loop_active = false;
static bool seq2_loop_active = false;
// NEU: Tracking für Voltage Changes
static uint16_t last_voltage_ch1 = 0xFFFF;
static uint16_t last_voltage_ch2 = 0xFFFF;
bool readButton(byte pin, ButtonState &state)
{
bool reading = digitalRead(pin) == HIGH;
bool reading = digitalRead(pin) == LOW;
bool buttonPressed = false;
if(reading != state.last)
@@ -69,10 +78,10 @@ bool readButton(byte pin, ButtonState &state)
void initButtons()
{
pinMode(PIN_SB_1_REC, INPUT_PULLDOWN);
pinMode(PIN_SB_1_PLAY, INPUT_PULLDOWN);
pinMode(PIN_SB_2_REC, INPUT_PULLDOWN);
pinMode(PIN_SB_2_PLAY, INPUT_PULLDOWN);
pinMode(PIN_SB_1_REC, INPUT_PULLUP);
pinMode(PIN_SB_1_PLAY, INPUT_PULLUP);
pinMode(PIN_SB_2_REC, INPUT_PULLUP);
pinMode(PIN_SB_2_PLAY, INPUT_PULLUP);
btn_sb1_rec.current = false;
btn_sb1_rec.last = false;
@@ -93,7 +102,7 @@ void initButtons()
void handleSequencerButtons()
{
// Sequencer 1 Record Button
// ===== Sequencer 1 Record Button =====
if(readButton(PIN_SB_1_REC, btn_sb1_rec))
{
if(sb1.isRecording())
@@ -106,58 +115,39 @@ void handleSequencerButtons()
{
if(sb1.isPlaying()) sb1.stopPlay();
sb1.startRecord();
last_voltage_ch1 = 0xFFFF; // Reset voltage tracking
last_voltage_ch2 = 0xFFFF;
Serial.printf("\n\r[SEQ1] Recording started (2 channels)...");
}
}
// Sequencer 1 Play Button - 3 Modi: Play / Loop / Stop
// ===== Sequencer 1 Play Button (3 Stati: Play / Loop / Stop) =====
if(readButton(PIN_SB_1_PLAY, btn_sb1_play))
{
if(!sb1.isPlaying())
{
// Nicht am Spielen -> Starte Playback (ohne Loop)
if(sb1.isRecording()) sb1.stopRecord();
sb1.setLoop(false);
seq1_loop_active = false;
sb1.startPlay();
Serial.printf("\n\r[SEQ1] Playback started (single)\n\r\tSteps: %i, Duartion: %ims", sb1.getStepCount(), sb1.getTotalDuration());
Serial.printf("\n\r[SEQ1] Playback started (single)\n\r\tSteps: %i, Duration: %ims",
sb1.getStepCount(), sb1.getTotalDuration());
}
else if(!seq1_loop_active)
{
sb1.setLoop(true);
seq1_loop_active = true;
Serial.printf("\n\r[SEQ1] Loop activated");
}
else
{
// Am Spielen -> Prüfe Loop-Status
if(!sb1.isPlaying()) // Falls schon gestoppt
{
// Starte neu
sb1.setLoop(false);
sb1.startPlay();
Serial.printf("\n\r[SEQ1] Playback started (single)");
}
else
{
// Ist am Spielen - ermittle ob Loop aktiv ist
// Wir testen das indirekt: Wenn ein Sequencer am Ende angekommen ist
// und noch spielt, dann muss Loop aktiv sein
// Alternative: Wir tracken den Loop-Status selbst
static bool seq1_loop_active = false;
if(!seq1_loop_active)
{
// 2. Klick: Loop aktivieren
sb1.setLoop(true);
seq1_loop_active = true;
Serial.printf("\n\r[SEQ1] Loop activated");
}
else
{
// 3. Klick: Stop
sb1.stopPlay();
seq1_loop_active = false;
Serial.printf("\n\r[SEQ1] Playback stopped");
}
}
sb1.stopPlay();
seq1_loop_active = false;
Serial.printf("\n\r[SEQ1] Playback stopped");
}
}
// Sequencer 2 Record Button
// ===== Sequencer 2 Record Button =====
if(readButton(PIN_SB_2_REC, btn_sb2_rec))
{
if(sb2.isRecording())
@@ -170,40 +160,35 @@ void handleSequencerButtons()
{
if(sb2.isPlaying()) sb2.stopPlay();
sb2.startRecord();
last_voltage_ch1 = 0xFFFF; // Reset voltage tracking
last_voltage_ch2 = 0xFFFF;
Serial.printf("\n\r[SEQ2] Recording started (2 channels)...");
}
}
// Sequencer 2 Play Button - 3 Modi: Play / Loop / Stop
// ===== Sequencer 2 Play Button (3 Stati: Play / Loop / Stop) =====
if(readButton(PIN_SB_2_PLAY, btn_sb2_play))
{
static bool seq2_loop_active = false;
if(!sb2.isPlaying())
{
// Nicht am Spielen -> Starte Playback (ohne Loop)
if(sb2.isRecording()) sb2.stopRecord();
sb2.setLoop(false);
seq2_loop_active = false;
sb2.startPlay();
Serial.printf("\n\r[SEQ2] Playback started (single)");
Serial.printf("\n\r[SEQ2] Playback started (single)\n\r\tSteps: %i, Duration: %ims",
sb2.getStepCount(), sb2.getTotalDuration());
}
else if(!seq2_loop_active)
{
sb2.setLoop(true);
seq2_loop_active = true;
Serial.printf("\n\r[SEQ2] Loop activated");
}
else
{
if(!seq2_loop_active)
{
// 2. Klick: Loop aktivieren
sb2.setLoop(true);
seq2_loop_active = true;
Serial.printf("\n\r[SEQ2] Loop activated");
}
else
{
// 3. Klick: Stop
sb2.stopPlay();
seq2_loop_active = false;
Serial.printf("\n\r[SEQ2] Playback stopped");
}
sb2.stopPlay();
seq2_loop_active = false;
Serial.printf("\n\r[SEQ2] Playback stopped");
}
}
}
@@ -211,8 +196,28 @@ void handleSequencerButtons()
void setup()
{
Serial.begin(BAUDRATE);
delay(2000);
Serial.printf("\n\r=== FIXED VERSION v2 ===");
Serial.printf("\n\rSerial OK!");
keyboard.begin();
cv.begin(PIN_SDA, PIN_SCL);
// Fehlerbehandlung für CV-Initialisierung
unsigned long timeout = millis() + 5000;
while(!cv.begin(PIN_SDA, PIN_SCL))
{
Serial.printf("\n\r[ERROR] CV initialization failed. Retrying...");
delay(500);
if(millis() > timeout)
{
Serial.printf("\n\r[FATAL] CV initialization timeout! Check I2C connection.");
break;
}
}
Serial.printf("\n\r[OK] CV initialized");
initButtons();
sb1.setLoop(false);
@@ -227,11 +232,47 @@ void setup()
Serial.printf("\n\r 3rd click: Stop");
Serial.printf("\n\r PIN_SB_2_REC: SEQ2 Record Start/Stop (CH1+CH2)");
Serial.printf("\n\r PIN_SB_2_PLAY: SEQ2 Play Mode (same as SEQ1)");
Serial.printf("\n\r");
Serial.printf("\n\rFIXES:");
Serial.printf("\n\r - Bounds checks in all array accesses");
Serial.printf("\n\r - Rate limiting (5ms) for addStep()");
Serial.printf("\n\r - Only call addStep() on voltage change");
Serial.printf("\n\r - Stack overflow prevention");
Serial.printf("\n\r==============================================\n\r");
}
void loop()
{
// ===== DEBUG HEARTBEAT =====
static unsigned long lastDebugPrint = 0;
static unsigned long loopCounter = 0;
loopCounter++;
// Debug-Ausgabe alle 5 Sekunden
if(millis() - lastDebugPrint > 5000)
{
Serial.printf("\n\r[HEARTBEAT] Loop count: %lu", loopCounter);
Serial.printf("\n\r[DEBUG] SB1: Rec=%d, Play=%d, Steps=%d",
sb1.isRecording(), sb1.isPlaying(), sb1.getStepCount());
Serial.printf("\n\r[DEBUG] SB2: Rec=%d, Play=%d, Steps=%d",
sb2.isRecording(), sb2.isPlaying(), sb2.getStepCount());
Serial.printf("\n\r[DEBUG] Free heap: %lu bytes", ESP.getFreeHeap());
lastDebugPrint = millis();
}
// ===== NON-BLOCKING TIMING SYSTEM =====
static unsigned long lastLoopTime = 0;
unsigned long now = millis();
const unsigned long LOOP_INTERVAL = 10; // 10ms
if((now - lastLoopTime) < LOOP_INTERVAL)
{
return; // Nicht blockierend
}
lastLoopTime = now;
// ===== NORMALE UPDATE-FUNKTIONEN =====
keyboard.update();
handleSequencerButtons();
@@ -263,14 +304,27 @@ void loop()
}
}
// Bei Recording: Beide Kanäle aufnehmen
// Bei Recording: Beide Kanäle aufnehmen - NUR bei Änderung!
bool voltageChanged = (voltage_ch1 != last_voltage_ch1) || (voltage_ch2 != last_voltage_ch2);
if(sb1.isRecording())
{
sb1.addStep(voltage_ch1, voltage_ch2);
if(voltageChanged)
{
sb1.addStep(voltage_ch1, voltage_ch2);
last_voltage_ch1 = voltage_ch1;
last_voltage_ch2 = voltage_ch2;
}
}
if(sb2.isRecording())
{
sb2.addStep(voltage_ch1, voltage_ch2);
if(voltageChanged)
{
sb2.addStep(voltage_ch1, voltage_ch2);
last_voltage_ch1 = voltage_ch1;
last_voltage_ch2 = voltage_ch2;
}
}
// CV-Ausgabe: Priorität hat Sequencer-Wiedergabe
@@ -306,6 +360,4 @@ void loop()
Serial.printf("\n\r[SEQ2] Final: Steps: %i, Duration: %ims",
sb2.getStepCount(), sb2.getTotalDuration());
}
delay(10);
}