/* NEW SKETCH 8x8 WS2812B LED Matrix - Scrolling Text "0 WORLD" with Start/Stop Switch Hardware: Seeed XIAO ESP32C3 Data Pin: GPIO2 Switch Pin: GPIO3 (connect switch between GPIO3 and GND) */ #include #define DATA_PIN 2 #define SWITCH_PIN 3 // GPIO3 — connect switch between GPIO3 and GND #define NUM_LEDS 64 #define BRIGHTNESS 20 #define SCROLL_DELAY 80 // ms between scroll steps Adafruit_NeoPixel strip(NUM_LEDS, DATA_PIN, NEO_GRB + NEO_KHZ800); // Text color #define TEXT_R 255 #define TEXT_G 255 #define TEXT_B 255 // 5x7 Font const uint8_t font5x7[][5] = { { 0b1111111, 0b0001000, 0b0001000, 0b1111111, 0b0000000 }, // H { 0b1111111, 0b1001001, 0b1001001, 0b1000001, 0b0000000 }, // E { 0b1111111, 0b1000000, 0b1000000, 0b1000000, 0b0000000 }, // L { 0b1111111, 0b1000000, 0b1000000, 0b1000000, 0b0000000 }, // L { 0b0111110, 0b1000001, 0b1000001, 0b0111110, 0b0000000 }, // O { 0b0000000, 0b0000000, 0b0000000, 0b0000000, 0b0000000 }, // (space) { 0b1111111, 0b0100000, 0b0010000, 0b0100000, 0b1111111 }, // W { 0b0111110, 0b1000001, 0b1000001, 0b0111110, 0b0000000 }, // O { 0b1111111, 0b0001001, 0b0011001, 0b1100110, 0b0000000 }, // R { 0b1111111, 0b1000000, 0b1000000, 0b1000000, 0b0000000 }, // L { 0b1111111, 0b1000001, 0b1000001, 0b0111110, 0b0000000 }, // D }; const int NUM_CHARS = 11; const int TOTAL_COLS = NUM_CHARS * 6; uint8_t scrollBuf[7][NUM_CHARS * 6]; // --- State --- bool animRunning = false; // is animation playing? int scrollOffset = -8; // current scroll position // --- Debounce --- bool lastSwitchState = HIGH; bool switchState = HIGH; unsigned long lastDebounceTime = 0; #define DEBOUNCE_DELAY 50 void buildScrollBuffer() { for (int c = 0; c < NUM_CHARS; c++) { for (int col = 0; col < 5; col++) { uint8_t colData = font5x7[c][col]; for (int row = 0; row < 7; row++) { scrollBuf[row][c * 6 + col] = (colData >> row) & 1; } } for (int row = 0; row < 7; row++) { scrollBuf[row][c * 6 + 5] = 0; } } } void displayFrame(int offset) { strip.clear(); for (int col = 0; col < 8; col++) { int srcCol = offset + col; for (int row = 0; row < 7; row++) { uint8_t lit = 0; if (srcCol >= 0 && srcCol < TOTAL_COLS) { lit = scrollBuf[row][srcCol]; } if (lit) { int ledIndex; if (col % 2 == 0) { ledIndex = col * 8 + row; } else { ledIndex = col * 8 + (7 - row); } strip.setPixelColor(ledIndex, strip.Color(TEXT_R, TEXT_G, TEXT_B)); } } } strip.show(); } // Read switch with debounce — returns true on a LOW transition (button press) bool switchPressed() { bool reading = digitalRead(SWITCH_PIN); if (reading != lastSwitchState) { lastDebounceTime = millis(); } lastSwitchState = reading; if ((millis() - lastDebounceTime) > DEBOUNCE_DELAY) { if (reading != switchState) { switchState = reading; if (switchState == LOW) { // Active LOW — pressed return true; } } } return false; } void setup() { Serial.begin(115200); pinMode(SWITCH_PIN, INPUT_PULLUP); // Internal pull-up; switch connects GPIO3 → GND strip.begin(); strip.setBrightness(BRIGHTNESS); strip.clear(); strip.show(); buildScrollBuffer(); Serial.println("Ready. Press switch to start/stop."); } void loop() { // Always check the switch, even mid-scroll if (switchPressed()) { animRunning = !animRunning; Serial.println(animRunning ? "▶ Started" : "⏹ Stopped"); if (!animRunning) { // Clear display immediately on stop strip.clear(); strip.show(); scrollOffset = -8; // Reset position for next start } } if (animRunning) { displayFrame(scrollOffset); scrollOffset++; // Reset after full scroll if (scrollOffset >= TOTAL_COLS) { scrollOffset = -8; } // Non-blocking delay — still checks switch during scroll pause unsigned long start = millis(); while (millis() - start < SCROLL_DELAY) { if (switchPressed()) { animRunning = false; strip.clear(); strip.show(); scrollOffset = -8; Serial.println("⏹ Stopped"); break; } } } }