diff --git a/purrChestrion-arduino/gfx.h b/purrChestrion-arduino/gfx.h index 0b8cbbd..6e937bd 100644 --- a/purrChestrion-arduino/gfx.h +++ b/purrChestrion-arduino/gfx.h @@ -63,4 +63,13 @@ const unsigned char savetorom [] PROGMEM = { 0x00, 0x05, 0x55, 0x54, 0x00, 0x0f, 0xff, 0xfe, 0x01, 0x0c, 0x00, 0x02, 0x01, 0x8c, 0x00, 0x02, 0x01, 0xcd, 0x89, 0xb2, 0xbf, 0xed, 0x55, 0x54, 0x5f, 0xed, 0x95, 0x14, 0x01, 0xcd, 0x49, 0x12, 0x01, 0x8c, 0x00, 0x02, 0x01, 0x0c, 0x00, 0x02, 0x00, 0x0f, 0xff, 0xfe, 0x00, 0x05, 0x55, 0x54 +}; + +// 'panic', 32x12px +#define ICONPANIC_WIDTH 32 +#define ICONPANIC_HEIGHT 12 +const unsigned char iconpanic [] PROGMEM = { + 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x01, 0x8e, 0x38, 0xc9, 0x4d, 0x91, 0x25, 0x29, 0x51, + 0xa4, 0xa5, 0x2d, 0x51, 0xa4, 0xb9, 0xed, 0x51, 0xa0, 0xa1, 0x2b, 0x51, 0xa4, 0xa1, 0x2b, 0x51, + 0x91, 0x21, 0x29, 0x51, 0x8e, 0x21, 0x29, 0x4d, 0x80, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff }; \ No newline at end of file diff --git a/purrChestrion-arduino/gfx_src/panic.aseprite b/purrChestrion-arduino/gfx_src/panic.aseprite new file mode 100644 index 0000000..bab8174 Binary files /dev/null and b/purrChestrion-arduino/gfx_src/panic.aseprite differ diff --git a/purrChestrion-arduino/gfx_src/panic.bmp b/purrChestrion-arduino/gfx_src/panic.bmp new file mode 100644 index 0000000..39b9bb7 Binary files /dev/null and b/purrChestrion-arduino/gfx_src/panic.bmp differ diff --git a/purrChestrion-arduino/purrChestrion-arduino.ino b/purrChestrion-arduino/purrChestrion-arduino.ino index 006ffdd..233b650 100644 --- a/purrChestrion-arduino/purrChestrion-arduino.ino +++ b/purrChestrion-arduino/purrChestrion-arduino.ino @@ -1,6 +1,7 @@ -#define FW_VER 1.01 +#define FW_VER 1.02 #define USE_OLED true // OLED displays #define USE_ENCODER true +#define USE_PANIC true #define EEPROM_MIDI_CHANNEL_ADDR 0 // Address to save MIDI channel in EEPROM // include libraries @@ -25,13 +26,18 @@ int lastClk = HIGH; #endif +#if USE_PANIC + #define PANIC_BUTTON 4 + bool midiPanic = false; +#endif + unsigned long lastChannelChangeTime = 0; bool channelChanged = false; const unsigned long saveDelay = 2000; // 2 seconds bool showSavedMessage = false; unsigned long savedMessageStart = 0; -const unsigned long savedMessageDuration = 3000; // 1 second +const unsigned long savedMessageDuration = 3000; // 3 seconds // Instantiate libraries Adafruit_MCP23X17 mcp1; // First MCP23017 @@ -112,6 +118,12 @@ void updateOLED() { oled.drawBitmap(88, 0, savetorom, SAVETOROM_WIDTH, SAVETOROM_HEIGHT, 1); } + #if USE_PANIC + if (midiPanic) { + oled.drawBitmap(1, 0, iconpanic, ICONPANIC_WIDTH, ICONPANIC_HEIGHT, 1); + } + #endif + oled.display(); #endif } @@ -148,6 +160,84 @@ void checkEncoder() { } +void handleNoteOn(byte channel, byte pitch, byte velocity) { + #if USE_PANIC + if (midiPanic) return; // suppress all notes during panic + #endif + + if (currentMidiChannel == MIDI_CHANNEL_OMNI || channel == currentMidiChannel) { + + if (pitch >= 57 && pitch <= 88) { + MCPOutput out = noteToMCP[pitch - 57]; + if (out.chip == 0) { + mcp1.digitalWrite(out.pin, HIGH); + } else if (out.chip == 1) { + mcp2.digitalWrite(out.pin, HIGH); + } + } + } + + // Flash LED on Note On + digitalWrite(LED_BUILTIN, HIGH); + ledOnTime = millis(); + ledActive = true; +} + +void handleNoteOff(byte channel, byte pitch, byte velocity) { + #if USE_PANIC + if (midiPanic) return; // suppress all notes during panic + #endif + + if (currentMidiChannel == MIDI_CHANNEL_OMNI || channel == currentMidiChannel) { + if (pitch >= 57 && pitch <= 88) { + MCPOutput out = noteToMCP[pitch - 57]; + if (out.chip == 0) { + mcp1.digitalWrite(out.pin, LOW); + } else if (out.chip == 1) { + mcp2.digitalWrite(out.pin, LOW); + } + } + } +} + +void checkMidiPanic() { + #if USE_PANIC + static bool lastButtonState = HIGH; + static unsigned long lastToggleTime = 0; + const unsigned long debounceDelay = 50; + + bool currentButtonState = digitalRead(PANIC_BUTTON); + + if (lastButtonState == HIGH && currentButtonState == LOW) { + unsigned long now = millis(); + if (now - lastToggleTime > debounceDelay) { + midiPanic = !midiPanic; + lastToggleTime = now; + + if (midiPanic) { + // Turn off all notes immediately + for (int i = 0; i < 32; i++) { + if (noteToMCP[i].chip == 0) { + mcp1.digitalWrite(noteToMCP[i].pin, LOW); + } else { + mcp2.digitalWrite(noteToMCP[i].pin, LOW); + } + } + } + + #if USE_OLED + updateOLED(); // Only update display when state changes + #endif + } + } + + lastButtonState = currentButtonState; + #endif +} + + + + void setup() { // Validate saved midi channel in EEPROM if (savedChannel <= 16) { @@ -175,6 +265,10 @@ void setup() { pinMode(ENCODER_CLK, INPUT_PULLUP); pinMode(ENCODER_DT, INPUT_PULLUP); #endif + + #if USE_PANIC + pinMode(PANIC_BUTTON, INPUT_PULLUP); + #endif // Midi Activity LED on Pin 13 (aka LED_BUILTIN) pinMode(LED_BUILTIN, OUTPUT); @@ -196,12 +290,18 @@ void setup() { updateOLED(); } + + void loop() { MIDI.read(); #if USE_ENCODER checkEncoder(); #endif + + #if USE_PANIC + checkMidiPanic(); + #endif // Save channel if change timeout has passed if (channelChanged && (millis() - lastChannelChangeTime >= saveDelay)) { @@ -228,37 +328,4 @@ void loop() { digitalWrite(LED_BUILTIN, LOW); ledActive = false; } -} - - -void handleNoteOn(byte channel, byte pitch, byte velocity) { - if (currentMidiChannel == MIDI_CHANNEL_OMNI || channel == currentMidiChannel) { - - if (pitch >= 57 && pitch <= 88) { - MCPOutput out = noteToMCP[pitch - 57]; - if (out.chip == 0) { - mcp1.digitalWrite(out.pin, HIGH); - } else if (out.chip == 1) { - mcp2.digitalWrite(out.pin, HIGH); - } - } - } - - // Flash LED on Note On - digitalWrite(LED_BUILTIN, HIGH); - ledOnTime = millis(); - ledActive = true; -} - -void handleNoteOff(byte channel, byte pitch, byte velocity) { - if (currentMidiChannel == MIDI_CHANNEL_OMNI || channel == currentMidiChannel) { - if (pitch >= 57 && pitch <= 88) { - MCPOutput out = noteToMCP[pitch - 57]; - if (out.chip == 0) { - mcp1.digitalWrite(out.pin, LOW); - } else if (out.chip == 1) { - mcp2.digitalWrite(out.pin, LOW); - } - } - } } \ No newline at end of file