232 lines
8.0 KiB
C++
232 lines
8.0 KiB
C++
// Loopy MIDI Controller - Phase 1
|
|
// ESP32-S3 USB MIDI Foot Controller
|
|
|
|
#include <Arduino.h>
|
|
#include <driver/gpio.h>
|
|
#include <esp_rom_sys.h>
|
|
#include "midi_transport.h"
|
|
#include "Adafruit_TinyUSB.h"
|
|
#include "pixel_stomp_mux.h"
|
|
#include "led_stub.h"
|
|
#include "switch_stub.h"
|
|
#include "app_task.h"
|
|
|
|
PixelStompMux mux(12, 10, 11, 9);
|
|
|
|
DefaultLedStub led_driver;
|
|
DefaultSwitchStub switch_driver;
|
|
UsbMidiTransport midi_transport;
|
|
|
|
AppTask controller(&led_driver, &switch_driver, &midi_transport);
|
|
|
|
TaskHandle_t midi_task_handle = NULL;
|
|
|
|
void midi_task(void* parameter) {
|
|
Serial.println("[TASK] MIDI task started on core 0");
|
|
|
|
while (true) {
|
|
midi_transport.update();
|
|
vTaskDelay(1);
|
|
}
|
|
}
|
|
|
|
void handle_serial_command(const String& cmd) {
|
|
if (cmd == "dump" || cmd == "d") {
|
|
mux.dump();
|
|
} else if (cmd == "probe" || cmd == "p") {
|
|
mux.probe();
|
|
} else if (cmd == "ledon") {
|
|
for (int i = 0; i < PixelStompMux::NUM_LEDS; i++) {
|
|
mux.set_led_color(i, 255, 255, 255);
|
|
}
|
|
mux.show();
|
|
Serial.println("[CMD] All LEDs WHITE");
|
|
} else if (cmd == "ledoff") {
|
|
mux.clear_all();
|
|
} else if (cmd == "ledtest") {
|
|
uint32_t colors[] = {0xFF0000, 0x00FF00, 0x0000FF, 0xFFFF00, 0xFF00FF, 0x00FFFF};
|
|
for (int c = 0; c < 6; c++) {
|
|
uint8_t r = (colors[c] >> 16) & 0xFF;
|
|
uint8_t g = (colors[c] >> 8) & 0xFF;
|
|
uint8_t b = colors[c] & 0xFF;
|
|
for (int i = 0; i < PixelStompMux::NUM_LEDS; i++) {
|
|
mux.set_led_color(i, r, g, b);
|
|
}
|
|
mux.show();
|
|
delay(300);
|
|
}
|
|
mux.clear_all();
|
|
Serial.println("[CMD] LED test complete");
|
|
} else if (cmd == "read") {
|
|
uint16_t raw = mux.read_buttons();
|
|
Serial.printf("[CMD] Raw: 0x%04X (", raw);
|
|
for (int i = 15; i >= 0; i--) {
|
|
Serial.print((raw >> i) & 1);
|
|
}
|
|
Serial.println(")");
|
|
} else if (cmd == "red") {
|
|
for (int i = 0; i < PixelStompMux::NUM_LEDS; i++) mux.set_led_color(i, 255, 0, 0);
|
|
mux.show();
|
|
} else if (cmd == "green") {
|
|
for (int i = 0; i < PixelStompMux::NUM_LEDS; i++) mux.set_led_color(i, 0, 255, 0);
|
|
mux.show();
|
|
} else if (cmd == "blue") {
|
|
for (int i = 0; i < PixelStompMux::NUM_LEDS; i++) mux.set_led_color(i, 0, 0, 255);
|
|
mux.show();
|
|
} else if (cmd == "pixel0") {
|
|
mux.set_led_brightness(255);
|
|
mux.set_led_color(0, 255, 255, 255);
|
|
mux.show();
|
|
Serial.println("[CMD] Pixel 0 WHITE (max brightness)");
|
|
} else if (cmd == "pixel1") {
|
|
mux.set_led_brightness(255);
|
|
mux.set_led_color(1, 255, 255, 255);
|
|
mux.show();
|
|
Serial.println("[CMD] Pixel 1 WHITE (max brightness)");
|
|
} else if (cmd == "usb") {
|
|
Serial.printf("[CMD] USB mounted: %s\n", TinyUSBDevice.mounted() ? "YES" : "NO");
|
|
Serial.printf("[CMD] USB ready: %s\n", TinyUSBDevice.ready() ? "YES" : "NO");
|
|
} else if (cmd == "gpiotest") {
|
|
Serial.println("[CMD] === Raw GPIO Test ===");
|
|
uint8_t pins[] = {9, 10, 11, 12};
|
|
const char* names[] = {"DAT(9)", "LD(10)", "CLK(11)", "DI(12)"};
|
|
for (int p = 0; p < 4; p++) {
|
|
uint8_t pin = pins[p];
|
|
pinMode(pin, INPUT_PULLUP);
|
|
delay(10);
|
|
int hi = digitalRead(pin);
|
|
pinMode(pin, INPUT_PULLDOWN);
|
|
delay(10);
|
|
int lo = digitalRead(pin);
|
|
pinMode(pin, INPUT);
|
|
delay(10);
|
|
int fl = digitalRead(pin);
|
|
Serial.printf(" %s: PULLUP=%d PULLDOWN=%d FLOAT=%d\n", names[p], hi, lo, fl);
|
|
}
|
|
Serial.println(" Testing WS2812(12) output toggle...");
|
|
pinMode(12, OUTPUT);
|
|
for (int i = 0; i < 5; i++) {
|
|
digitalWrite(12, HIGH);
|
|
delayMicroseconds(100);
|
|
int readback = digitalRead(9);
|
|
Serial.printf(" HIGH -> readback=%d\n", readback);
|
|
digitalWrite(9, LOW);
|
|
delayMicroseconds(100);
|
|
readback = digitalRead(9);
|
|
Serial.printf(" LOW -> readback=%d\n", readback);
|
|
}
|
|
Serial.println("[CMD] === GPIO Test Complete ===");
|
|
} else if (cmd == "help") {
|
|
Serial.println("[CMD] Commands:");
|
|
Serial.println(" dump - show button states");
|
|
Serial.println(" probe - hardware diagnostic");
|
|
Serial.println(" ledon - all LEDs white");
|
|
Serial.println(" ledoff - all LEDs off");
|
|
Serial.println(" ledtest - colour cycle");
|
|
Serial.println(" read - raw button read");
|
|
Serial.println(" red/green/blue - solid colour");
|
|
Serial.println(" pixel0/pixel1 - single pixel test");
|
|
Serial.println(" usb - USB connection status and descriptor info");
|
|
Serial.println(" gpiotest - raw GPIO pin diagnostic");
|
|
Serial.println(" rawled - bit-bang WS2812 (no library)");
|
|
Serial.println(" anim - re-run startup animation from loop");
|
|
} else if (cmd == "anim") {
|
|
Serial.println("[CMD] Running animation...");
|
|
uint32_t colors[] = {0xFF0000, 0x00FF00, 0x0000FF, 0xFFFF00, 0xFF00FF, 0x00FFFF, 0xFFFFFF};
|
|
for (int c = 0; c < 7; c++) {
|
|
uint8_t r = (colors[c] >> 16) & 0xFF;
|
|
uint8_t g = (colors[c] >> 8) & 0xFF;
|
|
uint8_t b = colors[c] & 0xFF;
|
|
Serial.printf(" Colour %d: #%06X\n", c, colors[c]);
|
|
for (int i = 0; i < PixelStompMux::NUM_LEDS; i++) mux.set_led_color(i, r, g, b);
|
|
mux.show();
|
|
delay(500);
|
|
}
|
|
mux.clear_all();
|
|
Serial.println("[CMD] Animation done");
|
|
} else if (cmd == "rawled") {
|
|
uint8_t pin = 12;
|
|
Serial.println("[CMD] Bit-bang WS2812: pixel 0 = RED on GPIO 9");
|
|
uint8_t colour[] = {0x00, 0xFF, 0x00};
|
|
noInterrupts();
|
|
for (int byte_idx = 0; byte_idx < 3; byte_idx++) {
|
|
uint8_t val = colour[byte_idx];
|
|
for (int bit = 7; bit >= 0; bit--) {
|
|
gpio_set_level((gpio_num_t)pin, 1);
|
|
if (val & (1 << bit)) {
|
|
esp_rom_delay_us(1);
|
|
}
|
|
gpio_set_level((gpio_num_t)pin, 0);
|
|
if (!(val & (1 << bit))) {
|
|
esp_rom_delay_us(1);
|
|
}
|
|
}
|
|
}
|
|
gpio_set_level((gpio_num_t)pin, 0);
|
|
esp_rom_delay_us(50);
|
|
interrupts();
|
|
Serial.println("[CMD] Sent. Wait 2 sec...");
|
|
delay(2000);
|
|
Serial.println("[CMD] Done");
|
|
} else {
|
|
Serial.println("[CMD] Unknown command. Type 'help'");
|
|
}
|
|
}
|
|
|
|
void setup() {
|
|
Serial.begin(115200);
|
|
delay(2000);
|
|
|
|
Serial.println("=================================");
|
|
Serial.println(" Loopy MIDI Controller v0.1");
|
|
Serial.println(" Phase 1: USB MIDI");
|
|
Serial.println(" Board: ESP32-S3-WROOM-1");
|
|
Serial.println("=================================");
|
|
|
|
Serial.println("[INIT] Initializing PixelStomp MUX...");
|
|
mux.begin();
|
|
|
|
Serial.println("[INIT] Starting LED startup animation...");
|
|
led_driver.set_mux(&mux);
|
|
led_driver.begin();
|
|
|
|
Serial.println("[INIT] Initializing switches via MUX...");
|
|
switch_driver.set_mux(&mux);
|
|
switch_driver.begin();
|
|
|
|
Serial.println("[INIT] Initializing USB MIDI...");
|
|
midi_transport.begin();
|
|
|
|
Serial.println("[INIT] Registering MIDI callbacks...");
|
|
controller.begin();
|
|
|
|
xTaskCreatePinnedToCore(
|
|
midi_task,
|
|
"midi_task",
|
|
4096,
|
|
NULL,
|
|
3,
|
|
&midi_task_handle,
|
|
0
|
|
);
|
|
|
|
Serial.println("=================================");
|
|
Serial.println(" All systems ready");
|
|
Serial.println(" Type 'help' for diagnostics");
|
|
Serial.println("=================================");
|
|
Serial.flush();
|
|
}
|
|
|
|
void loop() {
|
|
controller.update();
|
|
|
|
if (Serial.available()) {
|
|
String cmd = Serial.readStringUntil('\n');
|
|
cmd.trim();
|
|
cmd.toLowerCase();
|
|
handle_serial_command(cmd);
|
|
}
|
|
|
|
delay(10);
|
|
}
|