/*
Name : Adyen IPP Staging Terminal State Indicator
Version : 0.8
Date : 2024-03-18
Author : Bas van Ritbergen <[email protected]>
*/
// #include <Arduino.h>
#include <FastLED.h>
// Do only 3 ledstrips to save RAM
#define do_half true
#ifdef AVR
#include <EEPROM.h>
#endif
#define CONFIG_IDENTIFIER "ADYENLED"
#define IDENTIFIER_DEFAULT "STGSTRIP"
#define NUM_LEDS_PER_STRIP_DEFAULT 48
#define NUM_STRIPS_DEFAULT 3
#define NUM_GROUPS_PER_STRIP_DEFAULT 6
#define SPACER_WIDTH_DEFAULT 1
#define MAX_TERMINALS 48
#define BLINK_INTERVAL 250
#define MAX_INPUT_LEN 15
#define MAX_OUTPUT_LEN 60
// CRGB::OrangeRed
CRGB colors[5] = {CRGB::Black, CRGB::Green, CRGB::DarkOrange, CRGB::Red, CRGB::Blue}; // Default colors for states 0, 1, 2, 3 and 4 (5-8 is same but blinking)
bool BlinkState[MAX_TERMINALS] = {0};
CRGB BlinkColor[MAX_TERMINALS] = {0};
bool blink=false;
uint32_t lastToggleTimes;
// Set Defaults, wil bve overwritten by load from EEPROM
char identifier[10] = IDENTIFIER_DEFAULT;
int numLedsPerStrip = NUM_LEDS_PER_STRIP_DEFAULT;
int numStrips = NUM_STRIPS_DEFAULT;
int numGroupsPerStrip = NUM_GROUPS_PER_STRIP_DEFAULT;
int spacerWidth = SPACER_WIDTH_DEFAULT;
//CRGB leds[numStrips][numLedsPerStrip];
//CRGB leds[NUM_STRIPS_DEFAULT][NUM_LEDS_PER_STRIP_DEFAULT];
CRGB leds1[NUM_LEDS_PER_STRIP_DEFAULT];
CRGB leds2[NUM_LEDS_PER_STRIP_DEFAULT];
CRGB leds3[NUM_LEDS_PER_STRIP_DEFAULT];
#if do_half==false
CRGB leds4[NUM_LEDS_PER_STRIP_DEFAULT];
CRGB leds5[NUM_LEDS_PER_STRIP_DEFAULT];
CRGB leds6[NUM_LEDS_PER_STRIP_DEFAULT];
#endif
struct LedData {
char identifier[10] = IDENTIFIER_DEFAULT;
int numLedsPerStrip = NUM_LEDS_PER_STRIP_DEFAULT;
int numStrips = NUM_STRIPS_DEFAULT;
int numGroupsPerStrip = NUM_GROUPS_PER_STRIP_DEFAULT;
int spacerWidth = SPACER_WIDTH_DEFAULT;
} LedConfig = {};
const char HelpText[500] 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<ltwi>:value Set config for leds per shelve, terminals per shelve, spacer-width, identifier.\n"
" S Store current configuration in EEPROM\n"
"\n"
;
const int MSG_MAX_NUMBER = 18;
const int MSG_MAX_SIZE = 48;
const char messages [MSG_MAX_NUMBER] [MSG_MAX_SIZE] PROGMEM = {
{ "-=[ USB LedStrip ]=-\n" },
{ "Invalid state, use 0-9\n" },
{ "Invalid Terminal-ID, use 1-48\n" },
{ "Syntax error: Use T<TERMINAL-ID>:<STATE>\n" },
{ "Syntax error: Use A:<STATE>\n" },
{ "Display configuration:\n" },
{ "Save configuration: " },
{ "Configuration saved in EEPROM.\n" },
{ "Reset all Terminal states.\n" },
{ "Startup loop shown.\n" },
{ "No such command.\n" },
{ "INVALD SYNTAX: Use <A-Z>, H for help.\n" },
{ "Identifier : " },
{ "LEDs per shelve : " },
{ "Groups per shelve : " },
{ "Shelves : " },
{ "Spacer width : " },
{ "No EEPROM on non AVR controller"}
};
void setup() {
Serial.begin(57600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
printMSG(0);
Serial.setTimeout(300);
// loadConfiguration();
//updateConfiguration();
//startupAnimation();
// StartupLoop();
// Show help & config:
ShowHelp();
displayConfiguration() ;
// Ready to go.:
Serial.println("> ");
}
void loop() {
if (Serial.available()) {
checkInput();
Serial.println("> ");
}
delay(10);
//SetLEDs();
}
// This does not work :-( and that sucks
// char* ptrMSG (int i) {
// if (i <= MSG_MAX_NUMBER) {
// // sprintf(output,"Message: %d, ", i);
// // Serial.print(output);
// char *ptr=((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) {
#ifdef AVR
char *ptr=((const char *) &messages[i]);
#else
char *ptr=((char *) &messages[i]);
#endif
char chr;
if (!ptr)
return;
while ((chr = pgm_read_byte(ptr++))) {
Serial.print(chr);
}
}
}
void writeStringToEEPROM(int addrOffset, const String &strToWrite) {
byte len = strToWrite.length();
EEPROM.write(addrOffset, len);
for (int i = 0; i < len; i++) {
EEPROM.write(addrOffset + 1 + i, strToWrite[i]);
}
}
String readStringFromEEPROM(int addrOffset) {
int newStrLen = EEPROM.read(addrOffset);
char data[newStrLen + 1];
for (int i = 0; i < newStrLen; i++) {
data[i] = EEPROM.read(addrOffset + 1 + i);
}
data[newStrLen] = '\0';
return String(data);
}
void loadConfiguration() {
#ifdef AVR
if ( readStringFromEEPROM(0) == CONFIG_IDENTIFIER ) {
EEPROM.get(10, LedConfig);
*identifier = &LedConfig.identifier;
numLedsPerStrip = LedConfig.numLedsPerStrip;
numStrips = LedConfig.numStrips;
numGroupsPerStrip = LedConfig.numGroupsPerStrip;
spacerWidth = LedConfig.spacerWidth;
}
#else
printMSG(18);
#endif
}
void saveConfiguration() {
#ifdef AVR
*LedConfig.identifier = &identifier;
LedConfig.numLedsPerStrip = numLedsPerStrip;
LedConfig.numStrips = numStrips;
LedConfig.numGroupsPerStrip = numGroupsPerStrip;
LedConfig.spacerWidth = spacerWidth;
writeStringToEEPROM(0, CONFIG_IDENTIFIER);
EEPROM.put(10, LedConfig);
// sprintf(output,"Configuration saved in EEPROM.");
printMSG(7);
//sprintf(output,ptrMSG(7));
#else
printMSG(17);
#endif
}
void updateConfiguration() {
*LedConfig.identifier = &identifier;
LedConfig.numLedsPerStrip = numLedsPerStrip;
LedConfig.numStrips = numStrips;
LedConfig.numGroupsPerStrip = numGroupsPerStrip;
LedConfig.spacerWidth = spacerWidth;
FastLED.addLeds<WS2812B, 2, GRB>(leds1, numLedsPerStrip);
FastLED.addLeds<WS2812B, 3, GRB>(leds2, numLedsPerStrip);
FastLED.addLeds<WS2812B, 4, GRB>(leds3, numLedsPerStrip);
#if do_half==false
FastLED.addLeds<WS2812B, 5, GRB>(leds4, numLedsPerStrip);
FastLED.addLeds<WS2812B, 6, GRB>(leds5, numLedsPerStrip);
FastLED.addLeds<WS2812B, 7, GRB>(leds6, numLedsPerStrip);
#endif
}
// void updateConfiguration() {
// for (int i = 0; i < numStrips; i++) {
// FastLED.addLeds<WS2812, LED_PIN_1 + i, GRB>(leds[i], numLedsPerStrip);
// }
// }
void checkInput() {
char input[MAX_INPUT_LEN];
char output[MAX_OUTPUT_LEN];
memset(input, '\0', sizeof(input));
memset(output, '\0', sizeof(output));
int test=0;
int State;
int TermID;
Serial.readBytesUntil('\n', input, MAX_INPUT_LEN);
Serial.flush();
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':
Serial.print("Config:");
char Parameter;
String Value;
test = sscanf(Data, "%c:%c", &Parameter, &Value);
if (test == 2) {
switch (Parameter) {
case 'l':
Serial.print("Set lets per shelve:");
Serial.print(Value);
numLedsPerStrip = Value.toInt();
Serial.println(numLedsPerStrip);
break;
case 'g':
Serial.print("Set terminals per shelve:");
numGroupsPerStrip = Value.toInt();
Serial.println(numGroupsPerStrip);
break;
case 's':
Serial.print("Set spacer width:");
spacerWidth = Value.toInt();
Serial.println(spacerWidth);
break;
case 'i':
Serial.print("Set identifier code:");
Serial.println(Value);
Value.toCharArray(identifier, 8);
break;
default:
Serial.println("Syntax error.");
break;
}
updateConfiguration();
} else {
sprintf(output,"Syntax parsing failed %d of 3 parameters detected.",test);
}
break;
case 'T':
Serial.print("Set Terminal:");
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':
Serial.print("Set All Terminals:");
test = sscanf(Data, ":%d", &State);
if (test == 1) {
if (State >= 0 && State <= 9) {
sprintf(output,"All terminals set to state %d.",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':
Serial.print("Save config:");
printMSG(6);
saveConfiguration();
break;
case 'X':
Serial.print("Reset all LEDS:");
printMSG(8);
for(int i=0; i < MAX_TERMINALS; i++){
setGroupState(i,0);
}
SetAllLEDs(CRGB::Black);
break;
case 'W':
Serial.print("Show startup loop:");
StartupLoop();
printMSG(9);
break;
case 'H':
Serial.print("Show help:");
ShowHelp();
break;
default:
printMSG(10);
break;
}
} else
printMSG(11);
}
void setGroupState(int group, int state) {
switch (state) {
case 0:
BlinkState[group]=false;
BlinkColor[group]=CRGB::Black;
break;
case 1:
BlinkState[group]=false;
BlinkColor[group]=colors[1];
break;
case 2:
BlinkState[group]=false;
BlinkColor[group]=colors[2];
break;
case 3:
BlinkState[group]=false;
BlinkColor[group]=colors[3];
break;
case 4:
BlinkState[group]=false;
BlinkColor[group]=colors[4];
break;
case 5:
BlinkState[group]=true;
BlinkColor[group]=colors[1];
break;
case 6:
BlinkState[group]=true;
BlinkColor[group]=colors[2];
break;
case 7:
BlinkState[group]=true;
BlinkColor[group]=colors[3];
break;
case 8:
BlinkState[group]=true;
BlinkColor[group]=colors[4];
break;
case 9:
BlinkColor[group]= CRGB::White;
break;
default:
break;
}
}
void SetLEDGroupColor(int group, CRGB color) {
int stripIndex = (group / numGroupsPerStrip)+1;
int groupIndex = group % numGroupsPerStrip;
int startLEDIndex = groupIndex * (numLedsPerStrip / numGroupsPerStrip);
int widthLED = (numLedsPerStrip / numGroupsPerStrip) - spacerWidth;
int endLEDIndex = startLEDIndex + widthLED;
// fill_solid(&(leds[stripIndex][startLEDIndex]), numLedsPerStrip / numGroupsPerStrip, color);
if (stripIndex == 1 ) {
fill_solid(&(leds1[startLEDIndex]), widthLED, color);
}
if (stripIndex == 2 ) {
fill_solid(&(leds2[startLEDIndex]), widthLED, color);
}
if (stripIndex == 3 ) {
fill_solid(&(leds3[startLEDIndex]), widthLED, color);
}
#if do_half==false
if (stripIndex == 4 ) {
fill_solid(&(leds4[startLEDIndex]), widthLED, color);
}
if (stripIndex == 5 ) {
fill_solid(&(leds5[startLEDIndex]), widthLED, color);
}
if (stripIndex == 6 ) {
fill_solid(&(leds6[startLEDIndex]), widthLED, color);
}
#endif
FastLED.show();
}
void displayConfiguration() {
char output[MAX_OUTPUT_LEN];
memset(output, '\0', sizeof(output));
printMSG(12);
sprintf(output,"%s",identifier);
Serial.println(output);
printMSG(13);
sprintf(output,"%d",numLedsPerStrip);
Serial.println(output);
printMSG(14);
sprintf(output,"%d",numGroupsPerStrip);
Serial.println(output);
printMSG(15);
sprintf(output,"%d",numStrips);
Serial.println(output);
printMSG(16);
sprintf(output,"%d",spacerWidth);
Serial.println(output);
}
void FadeAll(int StartLed=1, int EndLed=numLedsPerStrip) {
for(int i = StartLed; i < EndLed; i++) {
leds1[i].nscale8(200);
leds2[i].nscale8(200);
leds3[i].nscale8(200);
#if do_half==false
leds4[i].nscale8(200);
leds5[i].nscale8(200);
leds6[i].nscale8(200);
#endif
}
}
void StartupLoop() {
// Boot animmation only on 1st strip.
// Blue ><><
#define DELAY (numLedsPerStrip/8)
for(int i = 0; i < numLedsPerStrip/2; i+=1) {
leds1[i] = 0x0080FF;
leds1[numLedsPerStrip -i -1] = 0x0080FF;
leds2[i] = 0x0080FF;
leds2[numLedsPerStrip -i -1] = 0x0080FF;
leds3[i] = 0x0080FF;
leds3[numLedsPerStrip -i -1] = 0x0080FF;
#if do_half==false
leds4[i] = 0x0080FF;
leds4[numLedsPerStrip -i -1] = 0x0080FF;
leds5[i] = 0x0080FF;
leds5[numLedsPerStrip -i -1] = 0x0080FF;
leds6[i] = 0x0080FF;
leds6[numLedsPerStrip -i -1] = 0x0080FF;
#endif
FastLED.show();
FadeAll(0,numLedsPerStrip);
delay(DELAY);
}
// White Flash
SetAllLEDs(CRGB::White);
FastLED.show();
delay(75);
for(int i = 0; i < 16; i++) {
FadeAll(0,numLedsPerStrip);
FastLED.show();
delay(65);
}
// All Off
SetAllLEDs(CRGB::Black);
}
void SetAllLEDs(CRGB color){
fill_solid(leds1, numLedsPerStrip, color);
fill_solid(leds2, numLedsPerStrip, color);
fill_solid(leds3, numLedsPerStrip, color);
#if do_half==false
fill_solid(leds4, numLedsPerStrip, color);
fill_solid(leds5, numLedsPerStrip, color);
fill_solid(leds6, numLedsPerStrip, color);
#endif
FastLED.show();
}
void SetLEDs() {
// Check if it's time to toggle each group's state
if (millis() - lastToggleTimes >= BLINK_INTERVAL) { // Toggle every BLINK_INTERVAL ms
blink = blink == 0 ? 1 : 0;
lastToggleTimes = millis(); // Update last toggle time
for(int i=0; i < MAX_TERMINALS; i++){
if (BlinkState[i] == true) {
if (blink == true){
SetLEDGroupColor(i, BlinkColor[i]);
} else {
SetLEDGroupColor(i, CRGB::Black);
}
} else {
SetLEDGroupColor(i, BlinkColor[i]);
}
}
}
}
void ShowHelp(){
#ifdef AVR
char *ptr=((const char *) &HelpText);
#else
char *ptr=((char *) &HelpText);
#endif
char chr;
if (!ptr)
return;
while ((chr = pgm_read_byte(ptr++))) {
Serial.print(chr);
}
}