Add expression pedal support on GPIO5 (CC 4)

This commit is contained in:
ash
2026-07-02 04:00:06 +00:00
parent ae2891fca8
commit 08d6fc8701
3 changed files with 88 additions and 1 deletions
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#include <cstdint>
class UsbMidiTransport;
class ExpressionPedal {
public:
ExpressionPedal(uint8_t adc_pin = 5);
void begin();
void update(UsbMidiTransport& midi);
uint8_t get_value() const { return current_value; }
private:
uint8_t adc_pin;
uint8_t current_value;
uint8_t last_sent_value;
uint8_t midi_channel;
uint8_t midi_cc;
static const uint8_t HYSTERESIS = 3;
static const uint16_t READ_INTERVAL_MS = 20;
uint32_t last_read_time;
uint8_t adc_to_midi(uint16_t adc_value);
};
+50
View File
@@ -0,0 +1,50 @@
#include "expression_pedal.h"
#include "midi_transport.h"
#include <Arduino.h>
ExpressionPedal::ExpressionPedal(uint8_t adc_pin)
: adc_pin(adc_pin)
, current_value(0)
, last_sent_value(255)
, midi_channel(1)
, midi_cc(4)
, last_read_time(0)
{
}
void ExpressionPedal::begin() {
Serial.printf("[EXP] Expression pedal on ADC1_CH4 (GPIO%d)\n", adc_pin);
analogReadResolution(12);
pinMode(adc_pin, INPUT);
delay(10);
uint16_t initial = analogRead(adc_pin);
current_value = adc_to_midi(initial);
last_sent_value = current_value;
Serial.printf("[EXP] Initial ADC: %d -> MIDI: %d\n", initial, current_value);
}
void ExpressionPedal::update(UsbMidiTransport& midi) {
uint32_t now = millis();
if (now - last_read_time < READ_INTERVAL_MS) return;
last_read_time = now;
uint16_t raw = analogRead(adc_pin);
uint8_t new_value = adc_to_midi(raw);
if (new_value > current_value + HYSTERESIS ||
new_value + HYSTERESIS < current_value ||
(new_value != current_value && (new_value == 0 || new_value == 127))) {
current_value = new_value;
}
if (current_value != last_sent_value) {
last_sent_value = current_value;
midi.send_cc(midi_channel, midi_cc, current_value);
Serial.printf("[EXP] CC%d: %d\n", midi_cc, current_value);
}
}
uint8_t ExpressionPedal::adc_to_midi(uint16_t adc_value) {
uint32_t scaled = (uint32_t)adc_value * 127 / 4095;
return (uint8_t)scaled;
}
+11 -1
View File
@@ -10,6 +10,7 @@
#include "led_stub.h" #include "led_stub.h"
#include "switch_stub.h" #include "switch_stub.h"
#include "app_task.h" #include "app_task.h"
#include "expression_pedal.h"
PixelStompMux mux(12, 10, 11, 9); PixelStompMux mux(12, 10, 11, 9);
@@ -18,6 +19,7 @@ DefaultSwitchStub switch_driver;
UsbMidiTransport midi_transport; UsbMidiTransport midi_transport;
AppTask controller(&led_driver, &switch_driver, &midi_transport); AppTask controller(&led_driver, &switch_driver, &midi_transport);
ExpressionPedal exp_pedal(5);
TaskHandle_t midi_task_handle = NULL; TaskHandle_t midi_task_handle = NULL;
@@ -26,6 +28,7 @@ void midi_task(void* parameter) {
while (true) { while (true) {
midi_transport.update(); midi_transport.update();
exp_pedal.update(midi_transport);
vTaskDelay(1); vTaskDelay(1);
} }
} }
@@ -83,6 +86,9 @@ void handle_serial_command(const String& cmd) {
mux.set_led_color(1, 255, 255, 255); mux.set_led_color(1, 255, 255, 255);
mux.show(); mux.show();
Serial.println("[CMD] Pixel 1 WHITE (max brightness)"); Serial.println("[CMD] Pixel 1 WHITE (max brightness)");
} else if (cmd == "exp") {
uint16_t raw = analogRead(5);
Serial.printf("[CMD] EXP GPIO5 ADC=%d MIDI=%d\n", raw, exp_pedal.get_value());
} else if (cmd == "usb") { } else if (cmd == "usb") {
Serial.printf("[CMD] USB mounted: %s\n", TinyUSBDevice.mounted() ? "YES" : "NO"); Serial.printf("[CMD] USB mounted: %s\n", TinyUSBDevice.mounted() ? "YES" : "NO");
Serial.printf("[CMD] USB ready: %s\n", TinyUSBDevice.ready() ? "YES" : "NO"); Serial.printf("[CMD] USB ready: %s\n", TinyUSBDevice.ready() ? "YES" : "NO");
@@ -126,6 +132,7 @@ void handle_serial_command(const String& cmd) {
Serial.println(" read - raw button read"); Serial.println(" read - raw button read");
Serial.println(" red/green/blue - solid colour"); Serial.println(" red/green/blue - solid colour");
Serial.println(" pixel0/pixel1 - single pixel test"); Serial.println(" pixel0/pixel1 - single pixel test");
Serial.println(" exp - expression pedal ADC/MIDI value");
Serial.println(" usb - USB connection status and descriptor info"); Serial.println(" usb - USB connection status and descriptor info");
Serial.println(" gpiotest - raw GPIO pin diagnostic"); Serial.println(" gpiotest - raw GPIO pin diagnostic");
Serial.println(" rawled - bit-bang WS2812 (no library)"); Serial.println(" rawled - bit-bang WS2812 (no library)");
@@ -328,7 +335,7 @@ void setup() {
Serial.println("================================="); Serial.println("=================================");
Serial.println(" Loopy MIDI Controller v0.1"); Serial.println(" Loopy MIDI Controller v0.1");
Serial.println(" Phase 1: USB MIDI"); Serial.println(" Phase 1: USB MIDI + Expression Pedal");
Serial.println(" Board: ESP32-S3-WROOM-1"); Serial.println(" Board: ESP32-S3-WROOM-1");
Serial.println("================================="); Serial.println("=================================");
@@ -346,6 +353,9 @@ void setup() {
Serial.println("[INIT] Initializing USB MIDI..."); Serial.println("[INIT] Initializing USB MIDI...");
midi_transport.begin(); midi_transport.begin();
Serial.println("[INIT] Initializing Expression Pedal...");
exp_pedal.begin();
Serial.println("[INIT] Registering MIDI callbacks..."); Serial.println("[INIT] Registering MIDI callbacks...");
controller.begin(); controller.begin();