Rewrite MUX: WS2812C LEDs (NeoPixel) + 74HC165 buttons
PixelStomp MUX uses: - WS2812C LEDs: one data line via DAT (GPIO 9), NeoPixel protocol - 74HC165 shift register: LD/CLK/DI for reading 8 button states Changes: - Use Adafruit NeoPixel library for LED control - Proper 74HC165 parallel-load shift-in for buttons - 8 switches + 8 LEDs (was incorrectly 10) - Diagnostic commands: dump, ledtest, red, green, blue, read
This commit is contained in:
+1
-1
@@ -27,7 +27,7 @@ class PixelStompMux;
|
|||||||
|
|
||||||
class DefaultLedStub : public LedStub {
|
class DefaultLedStub : public LedStub {
|
||||||
private:
|
private:
|
||||||
static const uint8_t NUM_LEDS = 10;
|
static const uint8_t NUM_LEDS = 8;
|
||||||
LedState led_states[NUM_LEDS];
|
LedState led_states[NUM_LEDS];
|
||||||
bool initialized;
|
bool initialized;
|
||||||
|
|
||||||
|
|||||||
+11
-10
@@ -1,22 +1,24 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <Adafruit_NeoPixel.h>
|
||||||
|
|
||||||
class PixelStompMux {
|
class PixelStompMux {
|
||||||
public:
|
public:
|
||||||
static const uint8_t NUM_INPUTS = 10;
|
static const uint8_t NUM_BUTTONS = 8;
|
||||||
static const uint8_t NUM_OUTPUTS = 10;
|
static const uint8_t NUM_LEDS = 8;
|
||||||
|
|
||||||
PixelStompMux(uint8_t dat_pin, uint8_t ld_pin, uint8_t clk_pin, uint8_t di_pin);
|
PixelStompMux(uint8_t dat_pin, uint8_t ld_pin, uint8_t clk_pin, uint8_t di_pin);
|
||||||
|
|
||||||
void begin();
|
void begin();
|
||||||
|
|
||||||
uint16_t read_buttons();
|
uint8_t read_buttons();
|
||||||
bool is_button_pressed(uint8_t index);
|
bool is_button_pressed(uint8_t index);
|
||||||
|
|
||||||
void write_leds(uint16_t led_state);
|
void set_led_color(uint8_t index, uint8_t r, uint8_t g, uint8_t b);
|
||||||
void set_led(uint8_t index, bool on);
|
void set_led_brightness(uint8_t brightness);
|
||||||
void clear_all();
|
void clear_all();
|
||||||
|
void show();
|
||||||
|
|
||||||
void dump();
|
void dump();
|
||||||
|
|
||||||
@@ -26,10 +28,9 @@ private:
|
|||||||
uint8_t pin_clk;
|
uint8_t pin_clk;
|
||||||
uint8_t pin_di;
|
uint8_t pin_di;
|
||||||
|
|
||||||
uint16_t last_button_state;
|
Adafruit_NeoPixel* strip;
|
||||||
uint16_t current_led_state;
|
|
||||||
|
|
||||||
void shift_out(uint16_t data, uint8_t bits);
|
uint8_t last_button_state;
|
||||||
uint16_t shift_in(uint8_t bits);
|
|
||||||
void pulse_pin(uint8_t pin, uint8_t level, uint16_t duration_us = 1);
|
uint8_t shift_in_74hc165();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class PixelStompMux;
|
|||||||
|
|
||||||
class DefaultSwitchStub : public SwitchStub {
|
class DefaultSwitchStub : public SwitchStub {
|
||||||
private:
|
private:
|
||||||
static const uint8_t NUM_SWITCHES = 10;
|
static const uint8_t NUM_SWITCHES = 8;
|
||||||
SwitchState switch_states[NUM_SWITCHES];
|
SwitchState switch_states[NUM_SWITCHES];
|
||||||
bool initialized;
|
bool initialized;
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ framework = arduino
|
|||||||
|
|
||||||
lib_deps =
|
lib_deps =
|
||||||
adafruit/Adafruit TinyUSB Library@^2.0.0
|
adafruit/Adafruit TinyUSB Library@^2.0.0
|
||||||
|
adafruit/Adafruit NeoPixel@^1.12.0
|
||||||
|
|
||||||
build_flags =
|
build_flags =
|
||||||
-DARDUINO_USB_MODE=1
|
-DARDUINO_USB_MODE=1
|
||||||
|
|||||||
+35
-18
@@ -19,34 +19,47 @@ void DefaultLedStub::set_mux(PixelStompMux* mux) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DefaultLedStub::begin() {
|
void DefaultLedStub::begin() {
|
||||||
Serial.println("[LED] Using PixelStomp MUX for LED output");
|
Serial.println("[LED] Using WS2812C via PixelStomp MUX");
|
||||||
initialized = true;
|
initialized = true;
|
||||||
|
|
||||||
|
if (!mux_ptr) {
|
||||||
|
Serial.println("[LED] ERROR: No MUX configured");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Serial.println("[LED] Startup animation...");
|
Serial.println("[LED] Startup animation...");
|
||||||
|
|
||||||
|
uint32_t colors[] = {
|
||||||
|
0xFF0000, // Red
|
||||||
|
0x00FF00, // Green
|
||||||
|
0x0000FF, // Blue
|
||||||
|
0xFFFF00, // Yellow
|
||||||
|
0xFF00FF, // Magenta
|
||||||
|
0x00FFFF, // Cyan
|
||||||
|
0xFFFFFF, // White
|
||||||
|
};
|
||||||
|
int num_colors = sizeof(colors) / sizeof(colors[0]);
|
||||||
|
|
||||||
|
for (int c = 0; c < num_colors; c++) {
|
||||||
|
uint32_t color = colors[c];
|
||||||
|
uint8_t r = (color >> 16) & 0xFF;
|
||||||
|
uint8_t g = (color >> 8) & 0xFF;
|
||||||
|
uint8_t b = color & 0xFF;
|
||||||
|
|
||||||
for (int i = 0; i < NUM_LEDS; i++) {
|
for (int i = 0; i < NUM_LEDS; i++) {
|
||||||
if (mux_ptr) {
|
mux_ptr->set_led_color(i, r, g, b);
|
||||||
mux_ptr->set_led(i, true);
|
|
||||||
}
|
}
|
||||||
Serial.printf("[LED] LED %d ON\n", i);
|
mux_ptr->show();
|
||||||
delay(150);
|
Serial.printf("[LED] Colour %d: R=%d G=%d B=%d\n", c, r, g, b);
|
||||||
}
|
delay(200);
|
||||||
|
|
||||||
delay(300);
|
|
||||||
|
|
||||||
for (int i = NUM_LEDS - 1; i >= 0; i--) {
|
|
||||||
if (mux_ptr) {
|
|
||||||
mux_ptr->set_led(i, false);
|
|
||||||
}
|
|
||||||
Serial.printf("[LED] LED %d OFF\n", i);
|
|
||||||
delay(100);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clear_all();
|
||||||
Serial.println("[LED] Startup complete");
|
Serial.println("[LED] Startup complete");
|
||||||
}
|
}
|
||||||
|
|
||||||
void DefaultLedStub::set_led_state(uint8_t note, uint8_t channel, uint8_t velocity) {
|
void DefaultLedStub::set_led_state(uint8_t note, uint8_t channel, uint8_t velocity) {
|
||||||
if (!initialized) return;
|
if (!initialized || !mux_ptr) return;
|
||||||
|
|
||||||
uint8_t led_index = note_to_index(note);
|
uint8_t led_index = note_to_index(note);
|
||||||
|
|
||||||
@@ -57,9 +70,13 @@ void DefaultLedStub::set_led_state(uint8_t note, uint8_t channel, uint8_t veloci
|
|||||||
led_states[led_index].active = (velocity > 0);
|
led_states[led_index].active = (velocity > 0);
|
||||||
led_states[led_index].timestamp = millis();
|
led_states[led_index].timestamp = millis();
|
||||||
|
|
||||||
if (mux_ptr) {
|
if (velocity > 0) {
|
||||||
mux_ptr->set_led(led_index, velocity > 0);
|
uint8_t brightness = map(velocity, 1, 127, 30, 255);
|
||||||
|
mux_ptr->set_led_color(led_index, brightness, brightness, brightness);
|
||||||
|
} else {
|
||||||
|
mux_ptr->set_led_color(led_index, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
mux_ptr->show();
|
||||||
|
|
||||||
Serial.printf("[LED] Note %d -> LED %d Ch %d Vel %d (%s)\n",
|
Serial.printf("[LED] Note %d -> LED %d Ch %d Vel %d (%s)\n",
|
||||||
note, led_index, channel, velocity,
|
note, led_index, channel, velocity,
|
||||||
|
|||||||
+27
-27
@@ -31,45 +31,45 @@ void handle_serial_command(const String& cmd) {
|
|||||||
if (cmd == "dump" || cmd == "d") {
|
if (cmd == "dump" || cmd == "d") {
|
||||||
mux.dump();
|
mux.dump();
|
||||||
} else if (cmd == "ledon") {
|
} else if (cmd == "ledon") {
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
mux.set_led(i, true);
|
mux.set_led_color(i, 255, 255, 255);
|
||||||
delay(50);
|
|
||||||
}
|
}
|
||||||
|
mux.show();
|
||||||
Serial.println("[CMD] All LEDs ON");
|
Serial.println("[CMD] All LEDs ON");
|
||||||
} else if (cmd == "ledoff") {
|
} else if (cmd == "ledoff") {
|
||||||
mux.clear_all();
|
mux.clear_all();
|
||||||
} else if (cmd == "ledtest") {
|
} else if (cmd == "ledtest") {
|
||||||
for (int i = 0; i < 10; i++) {
|
uint32_t colors[] = {0xFF0000, 0x00FF00, 0x0000FF, 0xFFFF00, 0xFF00FF, 0x00FFFF};
|
||||||
mux.set_led(i, true);
|
for (int c = 0; c < 6; c++) {
|
||||||
delay(200);
|
uint8_t r = (colors[c] >> 16) & 0xFF;
|
||||||
mux.set_led(i, false);
|
uint8_t g = (colors[c] >> 8) & 0xFF;
|
||||||
delay(100);
|
uint8_t b = colors[c] & 0xFF;
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
mux.set_led_color(i, r, g, b);
|
||||||
}
|
}
|
||||||
|
mux.show();
|
||||||
|
delay(300);
|
||||||
|
}
|
||||||
|
mux.clear_all();
|
||||||
Serial.println("[CMD] LED test complete");
|
Serial.println("[CMD] LED test complete");
|
||||||
} else if (cmd == "read") {
|
} else if (cmd == "read") {
|
||||||
uint16_t raw = mux.read_buttons();
|
uint8_t raw = mux.read_buttons();
|
||||||
Serial.printf("[CMD] Raw button state: 0x%03X (binary: ", raw);
|
Serial.printf("[CMD] Raw: 0x%02X (", raw);
|
||||||
for (int i = 9; i >= 0; i--) {
|
for (int i = 7; i >= 0; i--) {
|
||||||
Serial.print((raw >> i) & 1);
|
Serial.print((raw >> i) & 1);
|
||||||
}
|
}
|
||||||
Serial.println(")");
|
Serial.println(")");
|
||||||
} else if (cmd == "probe") {
|
} else if (cmd == "red") {
|
||||||
Serial.println("[CMD] Probing MUX pins...");
|
for (int i = 0; i < 8; i++) mux.set_led_color(i, 255, 0, 0);
|
||||||
Serial.printf(" DAT (GPIO %d) = %d\n", 9, digitalRead(9));
|
mux.show();
|
||||||
Serial.printf(" LD (GPIO %d) = %d\n", 10, digitalRead(10));
|
} else if (cmd == "green") {
|
||||||
Serial.printf(" CLK (GPIO %d) = %d\n", 11, digitalRead(11));
|
for (int i = 0; i < 8; i++) mux.set_led_color(i, 0, 255, 0);
|
||||||
Serial.printf(" DI (GPIO %d) = %d\n", 12, digitalRead(12));
|
mux.show();
|
||||||
|
} else if (cmd == "blue") {
|
||||||
Serial.println(" Toggling LD pin...");
|
for (int i = 0; i < 8; i++) mux.set_led_color(i, 0, 0, 255);
|
||||||
for (int i = 0; i < 5; i++) {
|
mux.show();
|
||||||
digitalWrite(10, HIGH);
|
|
||||||
delayMicroseconds(100);
|
|
||||||
digitalWrite(10, LOW);
|
|
||||||
delayMicroseconds(100);
|
|
||||||
}
|
|
||||||
Serial.printf(" DI after LD toggle: %d\n", digitalRead(12));
|
|
||||||
} else {
|
} else {
|
||||||
Serial.println("[CMD] Commands: dump, ledon, ledoff, ledtest, read, probe");
|
Serial.println("[CMD] Commands: dump, ledon, ledoff, ledtest, read, red, green, blue");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+42
-78
@@ -3,116 +3,85 @@
|
|||||||
|
|
||||||
PixelStompMux::PixelStompMux(uint8_t dat, uint8_t ld, uint8_t clk, uint8_t di)
|
PixelStompMux::PixelStompMux(uint8_t dat, uint8_t ld, uint8_t clk, uint8_t di)
|
||||||
: pin_dat(dat), pin_ld(ld), pin_clk(clk), pin_di(di),
|
: pin_dat(dat), pin_ld(ld), pin_clk(clk), pin_di(di),
|
||||||
last_button_state(0), current_led_state(0) {
|
strip(nullptr), last_button_state(0) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PixelStompMux::begin() {
|
void PixelStompMux::begin() {
|
||||||
pinMode(pin_dat, OUTPUT);
|
|
||||||
pinMode(pin_ld, OUTPUT);
|
pinMode(pin_ld, OUTPUT);
|
||||||
pinMode(pin_clk, OUTPUT);
|
pinMode(pin_clk, OUTPUT);
|
||||||
pinMode(pin_di, INPUT_PULLUP);
|
pinMode(pin_di, INPUT_PULLUP);
|
||||||
|
|
||||||
digitalWrite(pin_dat, LOW);
|
|
||||||
digitalWrite(pin_ld, LOW);
|
digitalWrite(pin_ld, LOW);
|
||||||
digitalWrite(pin_clk, LOW);
|
digitalWrite(pin_clk, LOW);
|
||||||
|
|
||||||
|
strip = new Adafruit_NeoPixel(NUM_LEDS, pin_dat, NEO_GRB + NEO_KHZ800);
|
||||||
|
strip->begin();
|
||||||
|
strip->setBrightness(50);
|
||||||
|
strip->clear();
|
||||||
|
strip->show();
|
||||||
|
|
||||||
Serial.printf("[MUX] Init DAT=%d LD=%d CLK=%d DI=%d\n",
|
Serial.printf("[MUX] Init DAT=%d LD=%d CLK=%d DI=%d\n",
|
||||||
pin_dat, pin_ld, pin_clk, pin_di);
|
pin_dat, pin_ld, pin_clk, pin_di);
|
||||||
|
Serial.printf("[MUX] 74HC165 for %d buttons, WS2812C for %d LEDs\n",
|
||||||
Serial.println("[MUX] GPIO probe:");
|
NUM_BUTTONS, NUM_LEDS);
|
||||||
Serial.printf(" DI (GPIO %d) reads: %d (should be 1 with pullup)\n",
|
|
||||||
pin_di, digitalRead(pin_di));
|
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
digitalWrite(pin_clk, HIGH);
|
|
||||||
delayMicroseconds(10);
|
|
||||||
digitalWrite(pin_clk, LOW);
|
|
||||||
delayMicroseconds(10);
|
|
||||||
}
|
}
|
||||||
Serial.printf(" DI after 3 CLK pulses: %d\n", digitalRead(pin_di));
|
|
||||||
|
|
||||||
|
uint8_t PixelStompMux::read_buttons() {
|
||||||
digitalWrite(pin_ld, HIGH);
|
digitalWrite(pin_ld, HIGH);
|
||||||
delayMicroseconds(10);
|
delayMicroseconds(5);
|
||||||
digitalWrite(pin_ld, LOW);
|
digitalWrite(pin_ld, LOW);
|
||||||
delayMicroseconds(10);
|
delayMicroseconds(5);
|
||||||
Serial.printf(" DI after LD pulse: %d\n", digitalRead(pin_di));
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t PixelStompMux::read_buttons() {
|
last_button_state = shift_in_74hc165();
|
||||||
pulse_pin(pin_ld, HIGH);
|
return last_button_state;
|
||||||
delayMicroseconds(10);
|
|
||||||
pulse_pin(pin_ld, LOW);
|
|
||||||
delayMicroseconds(10);
|
|
||||||
|
|
||||||
uint16_t state = shift_in(NUM_INPUTS);
|
|
||||||
last_button_state = state;
|
|
||||||
|
|
||||||
return state;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PixelStompMux::is_button_pressed(uint8_t index) {
|
bool PixelStompMux::is_button_pressed(uint8_t index) {
|
||||||
if (index >= NUM_INPUTS) return false;
|
if (index >= NUM_BUTTONS) return false;
|
||||||
|
uint8_t state = read_buttons();
|
||||||
uint16_t state = read_buttons();
|
|
||||||
return !(state & (1 << index));
|
return !(state & (1 << index));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PixelStompMux::write_leds(uint16_t led_state) {
|
void PixelStompMux::set_led_color(uint8_t index, uint8_t r, uint8_t g, uint8_t b) {
|
||||||
current_led_state = led_state;
|
if (index >= NUM_LEDS || !strip) return;
|
||||||
shift_out(led_state, NUM_OUTPUTS);
|
strip->setPixelColor(index, strip->Color(r, g, b));
|
||||||
|
|
||||||
pulse_pin(pin_ld, HIGH);
|
|
||||||
delayMicroseconds(10);
|
|
||||||
pulse_pin(pin_ld, LOW);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PixelStompMux::set_led(uint8_t index, bool on) {
|
void PixelStompMux::set_led_brightness(uint8_t brightness) {
|
||||||
if (index >= NUM_OUTPUTS) return;
|
if (strip) strip->setBrightness(brightness);
|
||||||
|
|
||||||
if (on) {
|
|
||||||
current_led_state |= (1 << index);
|
|
||||||
} else {
|
|
||||||
current_led_state &= ~(1 << index);
|
|
||||||
}
|
|
||||||
|
|
||||||
write_leds(current_led_state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PixelStompMux::clear_all() {
|
void PixelStompMux::clear_all() {
|
||||||
write_leds(0x0000);
|
if (strip) {
|
||||||
Serial.println("[MUX] All LEDs off");
|
strip->clear();
|
||||||
|
strip->show();
|
||||||
|
}
|
||||||
|
Serial.println("[MUX] LEDs cleared");
|
||||||
|
}
|
||||||
|
|
||||||
|
void PixelStompMux::show() {
|
||||||
|
if (strip) strip->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PixelStompMux::dump() {
|
void PixelStompMux::dump() {
|
||||||
uint16_t buttons = read_buttons();
|
uint8_t buttons = read_buttons();
|
||||||
Serial.printf("[MUX] Buttons: 0x%03X LEDs: 0x%03X\n", buttons, current_led_state);
|
Serial.printf("[MUX] Buttons: 0x%02X (binary: ", buttons);
|
||||||
|
for (int i = 7; i >= 0; i--) {
|
||||||
|
Serial.print((buttons >> i) & 1);
|
||||||
|
}
|
||||||
|
Serial.println(")");
|
||||||
|
|
||||||
for (int i = 0; i < NUM_INPUTS; i++) {
|
for (int i = 0; i < NUM_BUTTONS; i++) {
|
||||||
bool pressed = !(buttons & (1 << i));
|
bool pressed = !(buttons & (1 << i));
|
||||||
bool led_on = current_led_state & (1 << i);
|
Serial.printf(" [%d] %s\n", i, pressed ? "PRESS" : "off");
|
||||||
Serial.printf(" [%d] Button:%s LED:%s\n", i,
|
|
||||||
pressed ? "PRESS" : "off",
|
|
||||||
led_on ? "ON" : "off");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PixelStompMux::shift_out(uint16_t data, uint8_t bits) {
|
uint8_t PixelStompMux::shift_in_74hc165() {
|
||||||
for (int i = bits - 1; i >= 0; i--) {
|
uint8_t data = 0;
|
||||||
digitalWrite(pin_dat, (data >> i) & 1);
|
|
||||||
delayMicroseconds(1);
|
|
||||||
digitalWrite(pin_clk, HIGH);
|
|
||||||
delayMicroseconds(1);
|
|
||||||
digitalWrite(pin_clk, LOW);
|
|
||||||
delayMicroseconds(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t PixelStompMux::shift_in(uint8_t bits) {
|
for (int i = NUM_BUTTONS - 1; i >= 0; i--) {
|
||||||
uint16_t data = 0;
|
if (digitalRead(pin_di)) {
|
||||||
|
|
||||||
for (int i = bits - 1; i >= 0; i--) {
|
|
||||||
bool bit = digitalRead(pin_di);
|
|
||||||
if (bit) {
|
|
||||||
data |= (1 << i);
|
data |= (1 << i);
|
||||||
}
|
}
|
||||||
digitalWrite(pin_clk, HIGH);
|
digitalWrite(pin_clk, HIGH);
|
||||||
@@ -123,8 +92,3 @@ uint16_t PixelStompMux::shift_in(uint8_t bits) {
|
|||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PixelStompMux::pulse_pin(uint8_t pin, uint8_t level, uint16_t duration_us) {
|
|
||||||
digitalWrite(pin, level);
|
|
||||||
delayMicroseconds(duration_us);
|
|
||||||
}
|
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ DefaultSwitchStub::DefaultSwitchStub() : initialized(false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DefaultSwitchStub::begin() {
|
void DefaultSwitchStub::begin() {
|
||||||
Serial.println("[SW] Using PixelStomp MUX for button input");
|
Serial.println("[SW] Using 74HC165 via PixelStomp MUX");
|
||||||
initialized = true;
|
initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user