From 59b48c1ab33d75352fc9da64d7e628fde20ab226 Mon Sep 17 00:00:00 2001 From: Dejvino Date: Fri, 27 Feb 2026 06:33:46 +0100 Subject: [PATCH] Rotary encoder --- AudioThread.cpp | 10 ++------ SharedState.cpp | 11 ++++++++- SharedState.h | 10 ++++++++ UIThread.cpp | 63 ++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 84 insertions(+), 10 deletions(-) diff --git a/AudioThread.cpp b/AudioThread.cpp index 68a57c0..71d88bb 100644 --- a/AudioThread.cpp +++ b/AudioThread.cpp @@ -17,12 +17,6 @@ const int16_t AMPLITUDE = 16383; // Use a lower amplitude to avoid clipping (max I2S i2s(OUTPUT); // --- Synthesizer State --- -// Frequencies for a C-Major scale to pick from -const float NOTE_FREQUENCIES[] = { - 261.63, 293.66, 329.63, 349.23, 392.00, 440.00, 493.88, 523.25 -}; -const int NUM_NOTES = sizeof(NOTE_FREQUENCIES) / sizeof(NOTE_FREQUENCIES[0]); - float currentFrequency = 440.0f; double phase = 0.0; unsigned long lastNoteChangeTime = 0; @@ -50,8 +44,8 @@ void loopAudio() { // Every 500ms, pick a new random note to play if (now - lastNoteChangeTime > 500) { lastNoteChangeTime = now; - int noteIndex = random(0, NUM_NOTES); - currentFrequency = NOTE_FREQUENCIES[noteIndex]; + int noteIndex = random(0, SCALES[currentScaleIndex].numNotes); + currentFrequency = SCALES[currentScaleIndex].frequencies[noteIndex]; Serial.println("Playing note: " + String(currentFrequency) + " Hz"); } diff --git a/SharedState.cpp b/SharedState.cpp index ece37bc..c470822 100644 --- a/SharedState.cpp +++ b/SharedState.cpp @@ -2,4 +2,13 @@ volatile unsigned long lastLoop0Time = 0; volatile unsigned long lastLoop1Time = 0; -volatile bool watchdogActive = false; \ No newline at end of file +volatile bool watchdogActive = false; + +const Scale SCALES[] = { + { "C Major", { 261.63, 293.66, 329.63, 349.23, 392.00, 440.00, 493.88, 523.25 }, 8 }, + { "C Minor", { 261.63, 293.66, 311.13, 349.23, 392.00, 415.30, 466.16, 523.25 }, 8 }, + { "Pentatonic", { 261.63, 293.66, 329.63, 392.00, 440.00, 523.25, 587.33, 659.25 }, 8 }, + { "Blues", { 261.63, 311.13, 349.23, 369.99, 392.00, 466.16, 523.25, 587.33 }, 8 } +}; +const int NUM_SCALES = sizeof(SCALES) / sizeof(SCALES[0]); +volatile int currentScaleIndex = 0; \ No newline at end of file diff --git a/SharedState.h b/SharedState.h index e0e83b0..833e365 100644 --- a/SharedState.h +++ b/SharedState.h @@ -7,4 +7,14 @@ extern volatile unsigned long lastLoop0Time; extern volatile unsigned long lastLoop1Time; extern volatile bool watchdogActive; +struct Scale { + const char* name; + const float frequencies[8]; + int numNotes; +}; + +extern const Scale SCALES[]; +extern const int NUM_SCALES; +extern volatile int currentScaleIndex; + #endif // SHAREDSTATE_H \ No newline at end of file diff --git a/UIThread.cpp b/UIThread.cpp index 729f42f..c96eee4 100644 --- a/UIThread.cpp +++ b/UIThread.cpp @@ -14,13 +14,45 @@ #define PIN_SDA 4 #define PIN_SCL 5 +// Encoder Pins +#define PIN_ENC_CLK 12 +#define PIN_ENC_DT 13 + Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); +volatile int8_t encoderDelta = 0; +static uint8_t prevNextCode = 0; +static uint16_t store = 0; + +// --- ENCODER INTERRUPT --- +// Robust Rotary Encoder reading +void readEncoder() { + static int8_t rot_enc_table[] = {0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0}; + + prevNextCode <<= 2; + if (digitalRead(PIN_ENC_DT)) prevNextCode |= 0x02; + if (digitalRead(PIN_ENC_CLK)) prevNextCode |= 0x01; + prevNextCode &= 0x0f; + + // If valid state + if (rot_enc_table[prevNextCode]) { + store <<= 4; + store |= prevNextCode; + if ((store & 0xff) == 0x2b) encoderDelta--; + if ((store & 0xff) == 0x17) encoderDelta++; + } +} + void setupUI() { Wire.setSDA(PIN_SDA); Wire.setSCL(PIN_SCL); Wire.begin(); + pinMode(PIN_ENC_CLK, INPUT_PULLUP); + pinMode(PIN_ENC_DT, INPUT_PULLUP); + attachInterrupt(digitalPinToInterrupt(PIN_ENC_CLK), readEncoder, CHANGE); + attachInterrupt(digitalPinToInterrupt(PIN_ENC_DT), readEncoder, CHANGE); + if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) { Serial.println(F("SSD1306 allocation failed")); for(;;); @@ -34,7 +66,36 @@ void setupUI() { display.display(); } +void handleInput() { + // Handle Encoder Rotation + int rotation = 0; + noInterrupts(); + rotation = encoderDelta; + encoderDelta = 0; + interrupts(); + + if (rotation != 0) { + currentScaleIndex += rotation; + while (currentScaleIndex < 0) currentScaleIndex += NUM_SCALES; + while (currentScaleIndex >= NUM_SCALES) currentScaleIndex -= NUM_SCALES; + } +} + void loopUI() { + // sleep to avoid thread starvation + delay(10); + + handleInput(); // The loop on core 0 is responsible for updating the UI. - delay(100); + static unsigned long lastUpdate = 0; + if (millis() - lastUpdate < 100) return; + lastUpdate = millis(); + + display.clearDisplay(); + display.setCursor(0, 0); + display.setTextSize(1); + display.println(F("Current Scale:")); + display.setTextSize(2); + display.println(SCALES[currentScaleIndex].name); + display.display(); } \ No newline at end of file