/*
Name : Adyen IPP Staging Terminal State Indicator
Version : 0.8
Date : 2025-03-22
Author : Bas van Ritbergen <[email protected]>
Note ON VARIOUS MCU's:
RP2040 : Make sure to use Mbed OS setup for the MCU
ATmega(AVR) : No remarks
ESP32-C3 : No remarks
*/
// Basic defaults
#define CONFIG_IDENTIFIER "ADYENLED"
#define IDENTIFIER_DEFAULT "STGSTRIP"
#define NUM_LEDS_PER_STRIP_DEFAULT 48
#define NUM_STRIPS_DEFAULT 6
#define NUM_GROUPS_PER_STRIP_DEFAULT 6
#define SPACER_WIDTH_DEFAULT 1
#define MAX_TERMINALS 48
#define BLINK_INTERVAL 200
#define COLOR_STATE_1 CRGB::Green
#define COLOR_STATE_2 CRGB::DarkOrange
#define COLOR_STATE_3 CRGB::Red
#define COLOR_STATE_4 0x000080
#define DEBUG true
// ###########################################################################
// No configurable items below
#define VERSION 1.8
// Define max length for Input/Ouput buffers:
#define MAX_INPUT_LEN 20
#define MAX_OUTPUT_LEN 60
// Auto-set MCU type
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
#define AVR 1
#define MCU "AVR"
#undef RP2040
#undef ESP32
#endif
#if defined(ARDUINO_ARCH_RP2040)
#define RP2040 1
#undef AVR
#undef ESP32
#define MCU "RP2040"
#endif
#if defined(ARDUINO_ARCH_ESP32C3) || defined(ESP_IDF_VERSION) || defined(ESP_PLATFORM)
#define ESP32 1
#undef RP2040
#undef AVR
#define MCU "ESP32"
#endif
#ifndef AVR
#ifndef RP2040
#ifndef ESP32
#define MCU "Unknown"
#undef RP2040
#undef AVR
#undef ESP32
#endif
#endif
#endif
// We definitely need these libraries
#include <Arduino.h>
#include <FastLED.h>
// Setting for ATmega (AVR) based MCU's
#ifdef AVR
#include <avr/wdt.h>
#include <EEPROM.h>
#define HAS_EEPROM true
#ifndef BOARD_NAME
#define BOARD_NAME "Unknown AVR"
#endif
#endif
// Settings for RP2040 MCU
#ifdef RP2040
#include "LittleFS_Mbed_RP2040.h"
#include "mbed.h"
#define HAS_LittleFS true
#define _LFS_LOGLEVEL_ 1
#define RP2040_FS_SIZE_KB 64
#define FORCE_REFORMAT false
#define CONFIG_FILENAME MBED_LITTLEFS_FILE_PREFIX"/config.bin"
LittleFS_MBED *mbed_FS;
#endif
// Settinhs for ESP32 based MCU's
#ifdef ESP32
#include <Arduino.h>
#include <EEPROM.h>
#define BOARD_NAME "ESP32-C3 "
#define HAS_EEPROM true
#endif
// Figure MCU type/serial
#include <MicrocontrollerID.h>
char MCUid [41];
// Declare LedStrip control arrays
//CRGB leds[NUM_STRIPS_DEFAULT+1][NUM_LEDS_PER_STRIP_DEFAULT];
CRGB leds[NUM_STRIPS_DEFAULT+1][288];
// Config data is conviently stored in a struct (to easy store and retrieve from EEPROM/Flash)
// Set defaults, they will be overwritten by load from EEPROM
struct LedData {
//char *identifier = nullptr;
char identifier[16] = IDENTIFIER_DEFAULT;
uint16_t numLedsPerStrip = NUM_LEDS_PER_STRIP_DEFAULT;
uint8_t numStrips = NUM_STRIPS_DEFAULT;
uint8_t numGroupsPerStrip = NUM_GROUPS_PER_STRIP_DEFAULT;
uint8_t spacerWidth = SPACER_WIDTH_DEFAULT;
uint16_t blinkinterval = BLINK_INTERVAL;
CRGB state_color[5] = {CRGB::Black, COLOR_STATE_1, COLOR_STATE_2, COLOR_STATE_3, COLOR_STATE_4 };
} LedConfig = {};
// Input data from serial is stores in an array for further processing.
char inputBuffer[MAX_INPUT_LEN + 1]; // +1 for null terminator
uint8_t bufferIndex = 0;
// For blinking feature we need some extra global parameters
bool BlinkState[MAX_TERMINALS] = {0};
CRGB BlinkColor[MAX_TERMINALS] = {0};
bool blink=false;
uint32_t lastToggleTimes;
// For heartbeat-led on RP2040-zero (array with 1 position)
CRGB cpuled[1];
// HelpText
const char HelpText[720] PROGMEM =
" Syntax:\n"
" H This help\n"
" T<TermID>:<State> Set Terminal state. TermID: 1-48 and State: 0-9\n"
" X Set all states to off (same as sending'A:0'\n"
" A:<State> Set state for all Terminals, state (0-9)\n"
" D Show current configuration\n"
" C<iltswb>:value Set config for [i]dentifier, [l]eds per shelve, [t]erminals per shelve, amount of [s]helves, spacer-[w]idth, [b]link-interval.\n"
" S Store current configuration in EEPROM/FLASH\n"
" L Load stored cofiguration from EEPROM/FLASH\n"
" R Reboot controller (Disconnects serial!)\n"
" W Show startup loop\n"
"\n";
// All the messages are stored in program-memory to save CPU-memory (especially for AVR type mcu's)
const uint8_t MSG_MAX_NUMBER = 36;
const uint8_t MSG_MAX_SIZE = 50;
const char messages [MSG_MAX_NUMBER] [MSG_MAX_SIZE] PROGMEM = {
{ "-=[ USB LedStrip ]=-\n" }, // 0
{ "Invalid state, use 0-9\n" }, // 1
{ "Invalid Terminal-ID, use 1-48\n" }, // 2
{ "Syntax error: Use T<TERMINAL-ID>:<STATE>\n" }, // 3
{ "Syntax error: Use A:<STATE>\n" }, // 4
{ "\nDisplay configuration:\n" }, // 5
{ "\nSave configuration: " }, // 6
{ "Configuration saved in EEPROM.\n" }, // 7
{ "\nReset all Terminal states.\n" }, // 8
{ "\nShowing startup loop.\n" }, // 9
{ "\nERROR: command unknown.\n" }, // 10
{ "\nSYNTAX ERROR: Use <A-Z>, H for help.\n" }, // 11
{ "Identifier : " }, // 12
{ "LEDs per shelve : " }, // 13
{ "Terminals per shelve : " }, // 14
{ "Amount of shelves : " }, // 15
{ "Spacer width : " }, // 16
{ "Blinking interval : " }, // 17
{ "\nNo EEPROM or FLASH on this MCU\n" }, // 18
{ "\nRebooting contoller...\n" }, // 19
{ "\nLoad configuration: " }, // 20
{ "All terminals set to state" }, // 21
{ "Configuration loaded\n" }, // 22
{ "* Opening Failed" }, // 23
{ "* Writing failed" }, // 24
{ "ERROR, configfile is empty" }, // 25
{ "Valid config found in EEPROM, loading it.\n" }, // 26
{ "No config stored in EEPROM, using defaults.\n\n" },// 27
{ "Configuration stored in EEPROM.\n" }, // 28
{ "Version " }, // 29
{ "MCU : " }, // 30
{ "SN : " }, // 31
{ " compiled for " }, // 32
{ "\nTesting EEPROM availability & contents\n" }, // 33
{ "Failed to initialise EEPROM, Restarting...\n" }, // 34
{ "LITTLEFS Mount Failed, Restarting...\n" } // 35
};
// Setup MCU
void setup() {
// Setup SB -serial port
Serial.begin(115200);
Serial.setTimeout(0);
// Initialize status led, set to blue to show we are waitong for input
#ifdef AVR
FastLED.addLeds<WS2812B, 0, GRB>(cpuled, 1);
#endif
#ifdef RP2040
FastLED.addLeds<WS2812B, 16, GRB>(cpuled, 1);
#endif
#ifdef ESP32
FastLED.addLeds<WS2812B, 10, GRB>(cpuled, 1);
#endif
cpuled[0] = CRGB::Blue;
FastLED.show();
// DEBUG('Ready, waiting fo serial to become active.');
while (!Serial) {
// wait for serial port to connect. Needed for native USB-serial port only.
// Flash the status led green to indicate we are ready to start.
delay(750);
cpuled[0] = CRGB::Green;
FastLED.show();
delay(750);
cpuled[0] = CRGB::Black;
FastLED.show();
}
// Set led to blue and Show welcome to let us know the controller is booting.
// Also show some details abour the MCU and the codeversion
cpuled[0] = CRGB::Blue;
FastLED.show();
printMSG(0);
printMSG(29);
Serial.print(VERSION);
printMSG(32);
Serial.println(MCU);
Serial.println();
printMSG(30);
Serial.println(BOARD_NAME);
printMSG(31);
MicroID.getUniqueIDString(MCUid,16);
Serial.println(MCUid);
// Initialise EEPROM if available
#ifdef HAS_EEPROM
printMSG(33);
#ifdef ESP32
if (!EEPROM.begin(100)) {
#endif
#ifdef AVR
EEPROM.begin();
if (!eeprom_is_ready()){
#endif
printMSG(34);
delay(1000);
RebootMCU();
}
loadConfiguration();
#endif
// Initialize LittleFS if available
#ifdef HAS_LittleFS
mbed_FS = new LittleFS_MBED();
if (!mbed_FS->init()) {
printMSG(35);
delay(1000);
RebootMCU();
}
loadConfiguration();
#endif
// Enable current config (default or loaded from EEPRON/Flash)
updateConfiguration();
//startupAnimation();
StartupLoop();
// Show help & config:
// ShowHelp();
displayConfiguration() ;
// Ready to go, show prompt.
Serial.print("> ");
}
// Main loop
void loop() {
while (Serial.available() > 0) {
CRGB curcol = cpuled[0];
cpuled[0] = CRGB::Blue;
FastLED.show();
char c = Serial.read();
if (c == '\n' || c == '\r') {
inputBuffer[bufferIndex] = '\0'; // Null-terminate the string
checkInput(inputBuffer);
Serial.print("> ");
bufferIndex = 0;
} else if ( c == 0x08 ) { // Backspace
inputBuffer[bufferIndex--] = '\0';
Serial.print(c);
Serial.print(' ');
Serial.print(c);
} else if (bufferIndex < MAX_INPUT_LEN - 1 && c >= 0x20 && c < 0x7E && c!= 0x5C && c!=0x60 ) { // only printable chars, -1 to leave space for null terminator at end.
inputBuffer[bufferIndex++] = c;
Serial.print(c);
}
cpuled[0] = curcol;
FastLED.show();
}
// delay(10);
SetLEDs();
}
void DEBUGLOG(String *message){
if (DEBUG == true) {
Serial.println(*message);
}
}
// This does not work :-( and that sucks
// char* ptrMSG (int i) {
// if (i <= MSG_MAX_NUMBER) {
// // sprintf(output,"Message: %d, ", i);
// // Serial.print(output);
// const char *ptr = reinterpret_cast<const char*>(&messages[i]);
// // sprintf(output,"Address: 0x%04X", ptr);
// // Serial.println(output);
// if (!ptr) {
// return 0;
// }
// return ptr;
// } else {
// return 0;
// }
// }
void printMSG (int i) {
if (i <= MSG_MAX_NUMBER) {
const char *ptr = reinterpret_cast<const char*>(&messages[i]);
char chr;
if (!ptr)
return;
while ((chr = pgm_read_byte(ptr++))) {
Serial.print(chr);
}
}
}
#ifdef HAS_EEPROM
void writeStringToEEPROM(int addrOffset, const String &strToWrite) {
byte len = strToWrite.length();
EEPROM.write(addrOffset, len);
int rlen = EEPROM.read(addrOffset);
for (int i = 0; i < len; i++) {
EEPROM.write(addrOffset + 1 + i, strToWrite[i]);
}
}
String readStringFromEEPROM(int addrOffset) {
int len = EEPROM.read(addrOffset);
char data[len + 1];
for (int i = 0; i < len; i++)
data[i] = EEPROM.read(addrOffset + 1 + i);
data[len] = '\0';
return (String) data;
}
void loadConfiguration() {
String check = readStringFromEEPROM(0x0);
if ( check == CONFIG_IDENTIFIER ) {
printMSG(26);
EEPROM.get(0x10, LedConfig);
} else {
printMSG(27);
}
}
void saveConfiguration() {
writeStringToEEPROM(0x0, CONFIG_IDENTIFIER);
EEPROM.put(0x10, LedConfig);
printMSG(28);
}
#endif
#ifdef HAS_LittleFS
char* readFile(const char * path) {
// Serial.print("Reading file: ");
// Serial.println(path);
FILE *file = fopen(path, "r");
if (!file) {
printMSG(23);
return 0;
}
fseek(file, 0, SEEK_END);
long fileSize = ftell(file);
fseek(file, 0, SEEK_SET);
if (fileSize == 0) {
printMSG(25);
fclose(file);
return 0;
}
char *data = new char[fileSize];
int pos=0;
char c;
uint32_t numRead = 1;
while (numRead) {
numRead = fread((uint8_t *) &c, sizeof(c), 1, file);
if (numRead)
data[pos++]=(char)c;
}
fclose(file);
return data;
}
bool writeFile(const char * path, const char * message, size_t messageSize) {
FILE *file = fopen(path, "w");
if (!file) {
printMSG(23);
return false;
}
if (!fwrite((uint8_t *) message, 1, messageSize, file)) {
printMSG(24);
return false;
}
fclose(file);
return true;
}
void loadConfiguration() {
char *buffer;
buffer = readFile(CONFIG_FILENAME);
// DeSeriallize buffer into LedData Struct
int structSize=sizeof(LedData);
memcpy(&LedConfig, buffer, structSize);
printMSG(22);
}
bool saveConfiguration() {
// Seriallize LedData Struct into char-array so we can save it
char buffer[sizeof(LedData)];
memcpy(buffer, &LedConfig, sizeof(LedData));
writeFile(CONFIG_FILENAME, buffer, sizeof(buffer));
}
#endif
#ifndef HAS_EEPROM
#ifndef HAS_LittleFS
void loadConfiguration() {
printMSG(18);
}
void saveConfiguration() {
printMSG(18);
}
#endif
#endif
void updateConfiguration() {
// delete[] leds;
// CRGB leds[LedConfig.numStrips][LedConfig.numLedsPerStrip];
// leds = new CRGB[LedConfig.numStrips][LedConfig.numLedsPerStrip];
// Code below does not work as the FastLED class expects the pin to be a constant :-/
// FastLED.addLeds<[LEDTYPE], [PIN], [RGB-ORDER]>( [LED-array], [NUMBER OF LEDS]);
// for (const int n = 0; n < LedConfig.numStrips; n++) {
// FastLED.addLeds<WS2812, n+2, GRB>(leds[n], LedConfig.numLedsPerStrip);
// }
#ifndef AVR
pinMode(2, OUTPUT);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
FastLED.addLeds<WS2812B, 2, GRB>(leds[1], LedConfig.numLedsPerStrip);
FastLED.addLeds<WS2812B, 3, GRB>(leds[2], LedConfig.numLedsPerStrip);
FastLED.addLeds<WS2812B, 4, GRB>(leds[3], LedConfig.numLedsPerStrip);
FastLED.addLeds<WS2812B, 5, GRB>(leds[4], LedConfig.numLedsPerStrip);
FastLED.addLeds<WS2812B, 6, GRB>(leds[5], LedConfig.numLedsPerStrip);
FastLED.addLeds<WS2812B, 7, GRB>(leds[7], LedConfig.numLedsPerStrip);
#endif
#ifndef RP2040
pinMode(2, OUTPUT);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
FastLED.addLeds<WS2812B, 2, GRB>(leds[1], LedConfig.numLedsPerStrip);
FastLED.addLeds<WS2812B, 3, GRB>(leds[2], LedConfig.numLedsPerStrip);
FastLED.addLeds<WS2812B, 4, GRB>(leds[3], LedConfig.numLedsPerStrip);
FastLED.addLeds<WS2812B, 5, GRB>(leds[4], LedConfig.numLedsPerStrip);
FastLED.addLeds<WS2812B, 6, GRB>(leds[5], LedConfig.numLedsPerStrip);
FastLED.addLeds<WS2812B, 7, GRB>(leds[6], LedConfig.numLedsPerStrip);
#endif
#ifdef ESP32
pinMode(2, OUTPUT);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
FastLED.addLeds<WS2812B, 0, GRB>(leds[1], LedConfig.numLedsPerStrip);
FastLED.addLeds<WS2812B, 1, GRB>(leds[2], LedConfig.numLedsPerStrip);
FastLED.addLeds<WS2812B, 2, GRB>(leds[3], LedConfig.numLedsPerStrip);
FastLED.addLeds<WS2812B, 3, GRB>(leds[4], LedConfig.numLedsPerStrip);
FastLED.addLeds<WS2812B, 4, GRB>(leds[5], LedConfig.numLedsPerStrip);
FastLED.addLeds<WS2812B, 5, GRB>(leds[6], LedConfig.numLedsPerStrip);
#endif
}
void checkInput(char input[16]) {
if (input[0] == 0) {
Serial.println("");
return;
}
char output[MAX_OUTPUT_LEN];
memset(output, '\0', sizeof(output));
int test=0;
int State;
int TermID;
// sprintf(output,"Input: [%s].",input);
// Serial.println(output);
char Command = input[0];
char *Data = input +1;
if (Command >= 'A' && Command <= 'Z') {
// sprintf(output,"Command [%c] with Data [%s]",Command, Data);
// Serial.println(output);
switch(Command) {
case 'C':
SetConfigParameters(Data);
break;
case 'T':
test = sscanf(Data, "%2d:%d", &TermID, &State);
if ( test == 2) {
if (TermID >= 1 && TermID <= MAX_TERMINALS) {
if (State >= 0 && State <= 9) {
setGroupState(TermID -1,State);
sprintf(output,"Terminal %d state set to %d", TermID, State);
Serial.println(output);
} else {
printMSG(1);
}
} else {
printMSG(2);
}
} else {
printMSG(3);
}
break;
case 'A':
test = sscanf(Data, ":%d", &State);
if (test == 1) {
if (State >= 0 && State <= 9) {
printMSG(21);
Serial.print(State);
for(int i=0; i < MAX_TERMINALS; i++){
setGroupState(i,State);
}
} else {
printMSG(1);
}
} else
printMSG(4);
break;
case 'D':
printMSG(5);
displayConfiguration();
break;
case 'S':
printMSG(6);
saveConfiguration();
break;
case 'L':
printMSG(20);
loadConfiguration();
updateConfiguration();
break;
case 'X':
printMSG(8);
for(int i=0; i < MAX_TERMINALS; i++){
setGroupState(i,0);
}
SetAllLEDs(CRGB::Black);
break;
case 'W':
printMSG(9);
StartupLoop();
break;
case 'R':
printMSG(19);
RebootMCU();
break;
case 'H':
ShowHelp();
break;
default:
printMSG(10);
break;
}
} else
printMSG(11);
}
void RebootMCU() {
#ifdef ESP32
ESP.restart();
#endif
#ifdef RP2040
NVIC_SystemReset();
#endif
#ifdef AVR
wdt_enable(WDTO_15MS);
while (1);
#endif
}
void ShowHelp(){
const char *ptr = reinterpret_cast<const char*>(&HelpText);
char chr;
if (!ptr)
return;
while ((chr = pgm_read_byte(ptr++))) {
Serial.print(chr);
}
}
void SetConfigParameters(char *Data){
// char output[MAX_OUTPUT_LEN];
char Parameter = Data[0];
char *Value = Data +2;
if (Data[1] == ':') {
switch (Parameter) {
case 'i':
Serial.println();
printMSG(12);
// if sizeof(Value)
strcpy(LedConfig.identifier,Value);
Serial.println(LedConfig.identifier);
break;
case 'l':
Serial.println();
printMSG(13);
LedConfig.numLedsPerStrip = atoi(Value);
Serial.println(LedConfig.numLedsPerStrip);
break;
case 't':
Serial.println();
printMSG(14);
LedConfig.numGroupsPerStrip = atoi(Value);
Serial.println(LedConfig.numGroupsPerStrip);
break;
case 's':
Serial.println();
printMSG(15);
LedConfig.numStrips = atoi(Value);
Serial.println(LedConfig.numStrips);
break;
case 'w':
Serial.println();
printMSG(16);
LedConfig.spacerWidth = atoi(Value);
Serial.println(LedConfig.spacerWidth);
break;
case 'b':
Serial.println();
printMSG(17);
LedConfig.blinkinterval = atoi(Value);
Serial.println(LedConfig.blinkinterval);
break;
default:
printMSG(11);
break;
}
updateConfiguration();
} else {
printMSG(11);
}
}
void displayConfiguration() {
char output[MAX_OUTPUT_LEN];
memset(output, '\0', sizeof(output));
printMSG(12);
sprintf(output,"%s",LedConfig.identifier);
Serial.println(output);
printMSG(13);
sprintf(output,"%d",LedConfig.numLedsPerStrip);
Serial.println(output);
printMSG(14);
sprintf(output,"%d",LedConfig.numGroupsPerStrip);
Serial.println(output);
printMSG(15);
sprintf(output,"%d",LedConfig.numStrips);
Serial.println(output);
printMSG(16);
sprintf(output,"%d",LedConfig.spacerWidth);
Serial.println(output);
printMSG(17);
sprintf(output,"%d",LedConfig.blinkinterval);
Serial.println(output);
Serial.println();
}
void setGroupState(int group, int state) {
switch (state) {
case 0:
BlinkState[group] = false;
BlinkColor[group] = CRGB::Black;
break;
case 1:
case 2:
case 3:
case 4:
BlinkState[group] = false;
BlinkColor[group] = LedConfig.state_color[state];
break;
case 5:
case 6:
case 7:
case 8:
BlinkState[group] = true;
BlinkColor[group] = LedConfig.state_color[state -4];
break;
case 9:
BlinkColor[group] = CRGB::White;
break;
}
}
void SetLEDGroupColor(int group, CRGB color) {
int stripIndex = (group / LedConfig.numGroupsPerStrip)+1;
int groupIndex = group % LedConfig.numGroupsPerStrip;
int startLEDIndex = groupIndex * (LedConfig.numLedsPerStrip / LedConfig.numGroupsPerStrip);
int widthLED = (LedConfig.numLedsPerStrip / LedConfig.numGroupsPerStrip) - LedConfig.spacerWidth;
fill_solid(&(leds[stripIndex][startLEDIndex]), widthLED, color);
}
void FadeAll(int StartLed=1, int EndLed=LedConfig.numLedsPerStrip) {
for(int i = StartLed; i < EndLed; i++)
for(int n = 0; n < LedConfig.numStrips; n++)
leds[n][i].nscale8(200);
}
void StartupLoop() {
// Boot animmation only on 1st strip.
// Blue ><><
uint8_t DELAY (LedConfig.numLedsPerStrip / 8);
CRGB color = 0x0080FF;
for(int i = 0; i < LedConfig.numLedsPerStrip/2; i+=1) {
for(int n = 0; n < LedConfig.numStrips; n++) {
leds[n][i] = color;
leds[n][LedConfig.numLedsPerStrip -i -1] = color;
}
FastLED.show();
FadeAll(0,LedConfig.numLedsPerStrip);
delay(DELAY);
}
// White Flash
SetAllLEDs(CRGB::White);
FastLED.show();
delay(75);
for(int i = 0; i < 16; i++) {
FadeAll(0,LedConfig.numLedsPerStrip);
FastLED.show();
delay(65);
}
// All Off
SetAllLEDs(CRGB::Black);
}
void SetAllLEDs(CRGB color){
for(int n = 0; n < LedConfig.numStrips; n++)
fill_solid(leds[n], LedConfig.numLedsPerStrip, color);
FastLED.show();
}
void SetLEDs() {
// Check if it's time to toggle each group's state
if ( millis() - lastToggleTimes >= LedConfig.blinkinterval ) {
lastToggleTimes = millis(); // Update last toggle time
blink = blink == 0 ? 1 : 0;
cpuled[0] = blink ==1 ? CRGB::Red : CRGB::Black;
for(int i=0; i < LedConfig.numGroupsPerStrip*LedConfig.numStrips; i++){
if (BlinkState[i] == true) {
if (blink == true){
SetLEDGroupColor(i, BlinkColor[i]);
} else {
SetLEDGroupColor(i, CRGB::Black);
}
} else {
SetLEDGroupColor(i, BlinkColor[i]);
}
}
FastLED.show();
}
}