From c20f1dec2c5a8645d9a6c8e10e5ed21508b48d6e Mon Sep 17 00:00:00 2001 From: Ashley Strahle Date: Thu, 25 Jun 2026 22:39:37 +0000 Subject: [PATCH] Add visual MIDI type indicators - SysEx: ALL LEDs flash white 200ms - Note: LED 0 flashes red 100ms - CC: LED 1 flashes green 100ms - Helps diagnose what MIDI arrives without serial --- include/led_stub.h | 4 ++++ src/app_task.cpp | 12 ++++++++---- src/led_stub.cpp | 47 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 4 deletions(-) diff --git a/include/led_stub.h b/include/led_stub.h index 3c543fc..f76e407 100644 --- a/include/led_stub.h +++ b/include/led_stub.h @@ -12,6 +12,8 @@ public: virtual void set_led_brightness(uint8_t brightness) = 0; virtual void flash_activity() {} virtual void flash_sysex() {} + virtual void flash_all(uint8_t r, uint8_t g, uint8_t b, uint16_t duration) {} + virtual void flash_one(uint8_t index, uint8_t r, uint8_t g, uint8_t b, uint16_t duration) {} virtual void update() {} virtual uint8_t note_to_index(uint8_t note) { @@ -49,6 +51,8 @@ public: void set_led_brightness(uint8_t brightness) override; void flash_activity() override; void flash_sysex() override; + void flash_all(uint8_t r, uint8_t g, uint8_t b, uint16_t duration) override; + void flash_one(uint8_t index, uint8_t r, uint8_t g, uint8_t b, uint16_t duration) override; void update() override; void set_mux(PixelStompMux* mux); diff --git a/src/app_task.cpp b/src/app_task.cpp index b210eda..6dcc400 100644 --- a/src/app_task.cpp +++ b/src/app_task.cpp @@ -46,16 +46,20 @@ void AppTask::process_midi_event(const MidiEvent& event) { Serial.printf("[APP] MIDI IN: Type=%d Ch=%d Data1=%d Data2=%d\n", event.type, event.channel, event.data1, event.data2); - // Flash LED 0 white briefly on ANY MIDI input - visual activity indicator - // (visible without serial when connected to iPad) - led_driver->flash_activity(); + // Visual MIDI type indicator (works without serial on iPad) + if (event.type == MidiEvent::SYSEX) { + led_driver->flash_all(255, 255, 255, 200); // SysEx = ALL white + } else if (event.type == MidiEvent::NOTE_ON || event.type == MidiEvent::NOTE_OFF) { + led_driver->flash_one(0, 255, 0, 0, 100); // Note = LED 0 red + } else if (event.type == MidiEvent::CONTROL_CHANGE) { + led_driver->flash_one(1, 0, 255, 0, 100); // CC = LED 1 green + } if (event.type == MidiEvent::SYSEX) { // Cin is encoded in channel for SYSEX packets uint8_t cin = event.channel; uint8_t packet[3] = {event.data1, event.data2, 0}; process_sysex_packet(packet, cin); - led_driver->flash_sysex(); return; } diff --git a/src/led_stub.cpp b/src/led_stub.cpp index 357a9ab..b5e8a30 100644 --- a/src/led_stub.cpp +++ b/src/led_stub.cpp @@ -345,3 +345,50 @@ void DefaultLedStub::update() { if (heartbeat_phase > 200) heartbeat_phase = 0; } } + +void DefaultLedStub::flash_all(uint8_t r, uint8_t g, uint8_t b, uint16_t duration) { + if (!initialized || !mux_ptr) return; + + // Save all LED states + for (int i = 0; i < NUM_LEDS; i++) { + if (led_states[i].active) { + uint32_t color = launchpad_palette[led_states[i].velocity]; + sysex_saved_r[i] = (color >> 16) & 0xFF; + sysex_saved_g[i] = (color >> 8) & 0xFF; + sysex_saved_b[i] = color & 0xFF; + } else { + sysex_saved_r[i] = 0; + sysex_saved_g[i] = 0; + sysex_saved_b[i] = 0; + } + } + + for (int i = 0; i < NUM_LEDS; i++) { + mux_ptr->set_led_color(i, r, g, b); + } + mux_ptr->show(); + activity_off_time = millis() + duration; + sysex_flash_active = true; +} + +void DefaultLedStub::flash_one(uint8_t index, uint8_t r, uint8_t g, uint8_t b, uint16_t duration) { + if (!initialized || !mux_ptr) return; + if (index >= NUM_LEDS) return; + + // Save only this LED's state + if (led_states[index].active) { + uint32_t color = launchpad_palette[led_states[index].velocity]; + saved_r = (color >> 16) & 0xFF; + saved_g = (color >> 8) & 0xFF; + saved_b = color & 0xFF; + } else { + saved_r = 0; + saved_g = 0; + saved_b = 0; + } + + mux_ptr->set_led_color(index, r, g, b); + mux_ptr->show(); + activity_off_time = millis() + duration; + sysex_flash_active = false; +}