#include <SPI.h>
#include <TFT_eSPI.h>
#include <XPT2046_Touchscreen.h>
#include <Adafruit_NeoPixel.h>
// --- PIN MAPPING (CYD CONSTANTS) ---
#define XPT2046_IRQ 36
#define XPT2046_MOSI 32
#define XPT2046_MISO 39
#define XPT2046_CLK 25
#define XPT2046_CS 33
// --- WS2812B HARDWARE CONFIG ---
#define LED_PIN 27
#define NUM_LEDS 64
#define MATRIX_WIDTH 8
#define MATRIX_HEIGHT 8
// Initialize Adafruit NeoPixel instead of FastLED
Adafruit_NeoPixel matrix(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);
// --- INITIALIZE GRAPHICS & TOUCH ---
TFT_eSPI tft = TFT_eSPI();
SPIClass touchSPI = SPIClass(VSPI);
XPT2046_Touchscreen ts(XPT2046_CS, XPT2046_IRQ);
// --- APP STATES & GLOBAL PARAMETERS ---
uint8_t currentPattern = 0;
uint8_t globalBrightness = 128;
uint32_t selectedColor;
uint8_t manualGrid[64];
// Dynamic animation tracking
uint8_t effectHue = 0;
unsigned long lastEffectUpdate = 0;
int effectDelay = 30;
// UI Geometry mappings
const int gridStartX = 10;
const int gridStartY = 40;
const int cellSize = 22;
// Color Palette Definitions for Quick-Selection (RGB888 for NeoPixel)
uint32_t paletteColors[8];
uint16_t paletteTFT[] = {TFT_RED, TFT_GREEN, TFT_BLUE, TFT_YELLOW, TFT_CYAN, TFT_MAGENTA, TFT_WHITE, TFT_DARKGREY};
uint8_t selectedPaletteIdx = 0;
// Forward Declarations
void buildInterfaceFrame();
void drawInteractiveGrid();
void drawPatternButton();
void updateBrightnessSlider();
void handleTouchInputs();
void runPatternEngine(uint8_t patternId);
uint32_t Wheel(byte WheelPos);
void setup() {
Serial.begin(115200);
pinMode(21, OUTPUT); digitalWrite(21, HIGH);
// Define colors for NeoPixel API
paletteColors[0] = matrix.Color(255, 0, 0); // Red
paletteColors[1] = matrix.Color(0, 255, 0); // Green
paletteColors[2] = matrix.Color(0, 0, 255); // Blue
paletteColors[3] = matrix.Color(255, 255, 0); // Yellow
paletteColors[4] = matrix.Color(0, 255, 255); // Cyan
paletteColors[5] = matrix.Color(255, 0, 255); // Magenta
paletteColors[6] = matrix.Color(255, 255, 255); // White
paletteColors[7] = matrix.Color(0, 0, 0); // Black
selectedColor = paletteColors[0];
// Init Screen
tft.init();
tft.setRotation(1); // Landscape Layout
tft.fillScreen(0x10A2);
// Init Touch
touchSPI.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS);
ts.begin(touchSPI);
ts.setRotation(1);
// Init NeoPixel Matrix
matrix.begin();
matrix.setBrightness(globalBrightness);
matrix.clear();
matrix.show();
// Draw Static UI Framework
memset(manualGrid, 7, sizeof(manualGrid));
buildInterfaceFrame();
drawInteractiveGrid();
}
void loop() {
handleTouchInputs();
if (currentPattern > 0) {
if (millis() - lastEffectUpdate > effectDelay) {
runPatternEngine(currentPattern);
lastEffectUpdate = millis();
}
} else {
for (int i = 0; i < NUM_LEDS; i++) {
matrix.setPixelColor(i, paletteColors[manualGrid[i]]);
}
matrix.show();
}
}
// --- CORE INTERFACE DRAWING ENGINE ---
void buildInterfaceFrame() {
tft.setTextColor(TFT_WHITE, 0x10A2);
tft.drawFastHLine(0, 25, 320, 0x421B);
tft.drawString("MATRIX COMMAND CENTER", 10, 5, 2);
tft.drawString("EFFECT PATTERN", 200, 35, 1);
drawPatternButton();
tft.drawString("BRIGHTNESS", 200, 100, 1);
tft.fillRoundRect(200, 115, 110, 14, 4, TFT_BLACK);
updateBrightnessSlider();
tft.drawString("MANUAL COLOR SWATCH", 200, 145, 1);
for (int i = 0; i < 8; i++) {
tft.fillRect(200 + (i * 14), 160, 12, 12, paletteTFT[i]);
if (i == selectedPaletteIdx) {
tft.drawRect(199 + (i * 14), 159, 14, 14, TFT_YELLOW);
}
}
tft.fillRoundRect(200, 195, 52, 35, 4, 0x9000);
tft.setTextColor(TFT_WHITE);
tft.drawCentreString("CLEAR", 226, 205, 1);
tft.fillRoundRect(258, 195, 52, 35, 4, 0x04B0);
tft.drawCentreString("DRAW", 284, 205, 1);
}
void drawInteractiveGrid() {
for (int y = 0; y < MATRIX_HEIGHT; y++) {
for (int x = 0; x < MATRIX_WIDTH; x++) {
int idx = y * MATRIX_WIDTH + x;
tft.fillRect(gridStartX + (x * cellSize), gridStartY + (y * cellSize), cellSize - 2, cellSize - 2, paletteTFT[manualGrid[idx]]);
}
}
}
void drawPatternButton() {
tft.fillRoundRect(200, 50, 110, 38, 6, 0x2144);
tft.drawRoundRect(200, 50, 110, 38, 6, TFT_CYAN);
tft.setTextColor(TFT_GREEN, 0x2144);
String txt;
if (currentPattern == 0) txt = "0: DRAW MODE";
else txt = String(currentPattern) + ": PATTERN";
tft.drawCentreString(txt, 255, 61, 2);
}
void updateBrightnessSlider() {
tft.fillRoundRect(202, 117, 106, 10, 2, 0x2104);
int fillWidth = map(globalBrightness, 0, 255, 0, 106);
tft.fillRoundRect(202, 117, fillWidth, 10, 2, TFT_YELLOW);
}
// --- TOUCH SCREEN CONTROLLER ---
void handleTouchInputs() {
if (!ts.touched()) return;
TS_Point p = ts.getPoint();
int x = map(p.x, 200, 3700, 0, 320);
int y = map(p.y, 240, 3800, 0, 240);
// 1. Drawing Grid Zone
if (x >= gridStartX && x < (gridStartX + (MATRIX_WIDTH * cellSize)) &&
y >= gridStartY && y < (gridStartY + (MATRIX_HEIGHT * cellSize))) {
int cellX = (x - gridStartX) / cellSize;
int cellY = (y - gridStartY) / cellSize;
int idx = cellY * MATRIX_WIDTH + cellX;
if(currentPattern == 0 && idx >= 0 && idx < 64) {
manualGrid[idx] = selectedPaletteIdx;
tft.fillRect(gridStartX + (cellX * cellSize), gridStartY + (cellY * cellSize), cellSize - 2, cellSize - 2, paletteTFT[selectedPaletteIdx]);
}
delay(30);
}
// 2. Pattern Cycle button
if (x >= 200 && x <= 310 && y >= 50 && y <= 88) {
currentPattern++;
if (currentPattern > 10) currentPattern = 0;
drawPatternButton();
matrix.clear();
matrix.show();
delay(250);
}
// 3. Brightness Slider
if (x >= 200 && x <= 310 && y >= 110 && y <= 135) {
int rawVal = map(x, 200, 310, 0, 255);
globalBrightness = constrain(rawVal, 10, 255);
matrix.setBrightness(globalBrightness);
updateBrightnessSlider();
delay(30);
}
// 4. Palette Selection Line
if (x >= 200 && x <= 312 && y >= 155 && y <= 180) {
int clickIdx = (x - 200) / 14;
if(clickIdx >= 0 && clickIdx < 8) {
selectedPaletteIdx = clickIdx;
selectedColor = paletteColors[selectedPaletteIdx];
buildInterfaceFrame();
}
delay(150);
}
// 5. Action Buttons (CLEAR / DRAW)
if (y >= 195 && y <= 230) {
if (x >= 200 && x <= 252) { // CLEAR
memset(manualGrid, 7, sizeof(manualGrid));
drawInteractiveGrid();
matrix.clear();
matrix.show();
delay(200);
}
else if (x >= 258 && x <= 310) { // DRAW MODE
currentPattern = 0;
drawPatternButton();
drawInteractiveGrid();
delay(200);
}
}
}
// --- NEOPIXEL 10-PATTERN ENGINE ---
void runPatternEngine(uint8_t patternId) {
effectHue++;
switch (patternId) {
case 1: // Rainbow Wave
for(int i=0; i<NUM_LEDS; i++) {
matrix.setPixelColor(i, Wheel(((i * 256 / NUM_LEDS) + effectHue) & 255));
}
break;
case 2: // Fire Simulation
effectDelay = 60;
for (int i = 0; i < NUM_LEDS; i++) {
int r = random(50, 255);
int g = r / 3;
matrix.setPixelColor(i, matrix.Color(r, g, 0));
}
break;
case 3: // Cyberpunk Pulse
effectDelay = 30;
{
float pulse = (sin(millis() / 500.0) + 1.0) / 2.0; // 0.0 to 1.0
int val = 20 + (pulse * 235);
for(int i=0; i<NUM_LEDS; i++) {
matrix.setPixelColor(i, matrix.Color(val, 0, val / 2)); // Magenta-ish pulse
}
}
break;
case 4: // Plasma Flow
effectDelay = 20;
for (int x = 0; x < MATRIX_WIDTH; x++) {
for (int y = 0; y < MATRIX_HEIGHT; y++) {
int index = x + (y * MATRIX_WIDTH);
uint8_t colorIdx = (uint8_t)(sin((x * 0.5) + (effectHue * 0.1)) * 128 + 127) + (uint8_t)(cos((y * 0.5) + (effectHue * 0.1)) * 128 + 127);
matrix.setPixelColor(index, Wheel(colorIdx));
}
}
break;
case 5: // Neon Rain
effectDelay = 80;
// Fade operation
for(int i=0; i<NUM_LEDS; i++) {
uint32_t c = matrix.getPixelColor(i);
uint8_t r = (c >> 16) & 0xFF;
uint8_t g = (c >> 8) & 0xFF;
uint8_t b = c & 0xFF;
matrix.setPixelColor(i, matrix.Color(r*0.6, g*0.6, b*0.6));
}
matrix.setPixelColor(random(0, MATRIX_WIDTH), matrix.Color(0, 255, 0));
for (int y = MATRIX_HEIGHT - 1; y > 0; y--) {
for (int x = 0; x < MATRIX_WIDTH; x++) {
matrix.setPixelColor(x + (y * MATRIX_WIDTH), matrix.getPixelColor(x + ((y - 1) * MATRIX_WIDTH)));
}
}
break;
case 6: // Strobe Party
effectDelay = 60;
if (effectHue % 2 == 0) {
for(int i=0; i<NUM_LEDS; i++) matrix.setPixelColor(i, selectedColor);
} else {
matrix.clear();
}
break;
case 7: // Color Swirl Center
effectDelay = 25;
for(int i = 0; i < NUM_LEDS; i++) {
matrix.setPixelColor(i, Wheel((effectHue + (i * 4)) & 255));
}
break;
case 8: // Random Sparkles
effectDelay = 40;
for(int i=0; i<NUM_LEDS; i++) {
uint32_t c = matrix.getPixelColor(i);
uint8_t r = (c >> 16) & 0xFF; uint8_t g = (c >> 8) & 0xFF; uint8_t b = c & 0xFF;
matrix.setPixelColor(i, matrix.Color(r*0.8, g*0.8, b*0.8));
}
matrix.setPixelColor(random(0, NUM_LEDS), Wheel(random(0, 255)));
break;
case 9: // Larson Scanner
effectDelay = 30;
for(int i=0; i<NUM_LEDS; i++) {
uint32_t c = matrix.getPixelColor(i);
uint8_t r = (c >> 16) & 0xFF; uint8_t g = (c >> 8) & 0xFF; uint8_t b = c & 0xFF;
matrix.setPixelColor(i, matrix.Color(r*0.5, g*0.5, b*0.5));
}
{
int pos = (int)((sin(millis() / 200.0) + 1.0) * 0.5 * (NUM_LEDS - 1));
matrix.setPixelColor(pos, matrix.Color(255, 0, 0));
}
break;
case 10: // Ocean Waves
effectDelay = 40;
for(int i = 0; i < NUM_LEDS; i++) {
uint8_t wave = (uint8_t)((sin((effectHue + i) * 0.2) + 1.0) * 30);
matrix.setPixelColor(i, matrix.Color(0, wave + 40, wave + 150)); // Shades of Blue/Teal
}
break;
}
matrix.show();
}
// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
WheelPos = 255 - WheelPos;
if(WheelPos < 85) {
return matrix.Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
if(WheelPos < 170) {
WheelPos -= 85;
return matrix.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
WheelPos -= 170;
return matrix.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}Loading
esp32-2432s028r
esp32-2432s028r