#include <FastLED.h>
#include <SD.h>
#include <AsyncTimer.h>
#define LED_PIN 21 // NeoPixel data line (update as per your setup)
#define CS_PIN 5 // Chip Select (CS) pin for the SD card
#define NUM_LEDS 692
#define MAX_LINES 10
#define LINE_DELAY 2000 // 2-second delay between lines
#define LETTER_DELAY 400 // 400ms delay between letters
CRGB leds[NUM_LEDS];
String* textLines = nullptr;
int numLines = 0;
struct LEDGroup {
int start;
int end;
bool active;
};
struct Letter {
char name;
const int* groups;
int groupCount;
};
LEDGroup groups[] = {
{ 0, 45, false }, // Digit 1
{ 46, 91, false }, // Digit 2
{ 92, 110, false }, // Digit 3
{ 111, 129, false }, // Digit 4
{ 130, 175, false }, // Digit 5
{ 176, 221, false }, // Digit 6
{ 222, 240, false }, // Digit 7
{ 241, 259, false }, // Digit 8
{ 260, 307, false }, // Digit 9
{ 308, 355, false }, // Digit 10
{ 356, 374, false }, // Digit 11
{ 375, 393, false }, // Digit 12
{ 394, 441, false }, // Digit 13
{ 442, 489, false }, // Digit 14
{ 490, 535, false }, // Digit 15
{ 536, 581, false }, // Digit 16
{ 582, 604, false }, // Digit 17
{ 605, 613, false }, // Digit 18
{ 614, 636, false }, // Digit 19
{ 637, 659, false }, // Digit 20
{ 660, 668, false }, // Digit 21
{ 669, 691, false } // Digit 22
};
// Active groups for each letter
int groupsA[] = { 0, 1, 2, 3, 4, 5, 10, 11 };
int groupsB[] = { 0, 1, 2, 3, 6, 7, 11, 14, 15 };
int groupsC[] = { 2, 3, 4, 5, 6, 7 };
int groupsD[] = { 0, 1, 2, 3, 6, 7, 14, 15 };
int groupsE[] = { 2, 3, 4, 5, 6, 7, 10 };
int groupsF[] = { 2, 3, 4, 5, 10 };
int groupsG[] = { 0, 2, 3, 4, 5, 6, 7, 11 };
int groupsH[] = { 0, 1, 4, 5, 10, 11 };
int groupsI[] = { 2, 3, 6, 7, 14, 15 };
int groupsK[] = { 0, 4, 5, 10, 11, 13 };
int groupsL[] = { 4, 5, 6, 7 };
int groupsM[] = { 0, 1, 4, 5, 9, 13 };
int groupsN[] = { 0, 1, 4, 5, 8, 9 };
int groupsO[] = { 0, 1, 2, 3, 4, 5, 6, 7 };
int groupsP[] = { 1, 2, 3, 4, 5, 10, 11 };
int groupsQ[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
int groupsR[] = { 1, 2, 3, 4, 5, 8, 10, 11 };
int groupsS[] = { 0, 2, 3, 6, 7, 9, 11 };
int groupsT[] = { 2, 3, 14, 15 };
int groupsU[] = { 0, 1, 4, 5, 6, 7 };
int groupsV[] = { 4, 5, 12, 13 };
int groupsW[] = { 0, 1, 4, 5, 8, 12 };
int groupsX[] = { 8, 9, 12, 13 };
int groupsY[] = { 1, 4, 10, 11, 15 };
int groupsZ[] = { 2, 3, 6, 7, 12, 13 };
// Define all letters
Letter letters[] = {
{ 'A', groupsA, sizeof(groupsA) / sizeof(groupsA[0]) },
{ 'B', groupsB, sizeof(groupsB) / sizeof(groupsB[0]) },
{ 'C', groupsC, sizeof(groupsC) / sizeof(groupsC[0]) },
{ 'D', groupsD, sizeof(groupsD) / sizeof(groupsD[0]) },
{ 'E', groupsE, sizeof(groupsE) / sizeof(groupsE[0]) },
{ 'F', groupsF, sizeof(groupsF) / sizeof(groupsF[0]) },
{ 'G', groupsG, sizeof(groupsG) / sizeof(groupsG[0]) },
{ 'H', groupsH, sizeof(groupsH) / sizeof(groupsH[0]) },
{ 'I', groupsI, sizeof(groupsI) / sizeof(groupsI[0]) },
{ 'K', groupsK, sizeof(groupsK) / sizeof(groupsK[0]) },
{ 'L', groupsL, sizeof(groupsL) / sizeof(groupsL[0]) },
{ 'M', groupsM, sizeof(groupsM) / sizeof(groupsM[0]) },
{ 'N', groupsN, sizeof(groupsN) / sizeof(groupsN[0]) },
{ 'O', groupsO, sizeof(groupsO) / sizeof(groupsO[0]) },
{ 'P', groupsP, sizeof(groupsP) / sizeof(groupsP[0]) },
{ 'Q', groupsQ, sizeof(groupsQ) / sizeof(groupsQ[0]) },
{ 'R', groupsR, sizeof(groupsR) / sizeof(groupsR[0]) },
{ 'S', groupsS, sizeof(groupsS) / sizeof(groupsS[0]) },
{ 'T', groupsT, sizeof(groupsT) / sizeof(groupsT[0]) },
{ 'U', groupsU, sizeof(groupsU) / sizeof(groupsU[0]) },
{ 'V', groupsV, sizeof(groupsV) / sizeof(groupsV[0]) },
{ 'W', groupsW, sizeof(groupsW) / sizeof(groupsW[0]) },
{ 'X', groupsX, sizeof(groupsX) / sizeof(groupsX[0]) },
{ 'Y', groupsY, sizeof(groupsY) / sizeof(groupsY[0]) },
{ 'Z', groupsZ, sizeof(groupsZ) / sizeof(groupsZ[0]) }
};
// Function to find a letter by name
Letter* findLetter(char letter) {
for (int i = 0; i < sizeof(letters) / sizeof(Letter); i++) {
if (letters[i].name == letter) {
return &letters[i];
}
}
return nullptr;
}
AsyncTimer timer;
int currentLine = 0; // Tracks the current line
int currentLetter = 0; // Tracks the current letter in the line
void setup() {
FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
Serial.begin(9600);
Serial.println("hej");
initializeSDCard();
startTextAnimation();
}
void loop() {
timer.handle(); // Handle all timer events
EVERY_N_MILLIS(100) {
// Update LEDs based on group flags
for (int i = 0; i < sizeof(groups) / sizeof(groups[0]); i++) {
if (groups[i].active) {
// Turn on LEDs in the active group
for (int j = groups[i].start; j <= groups[i].end; j++) {
leds[j] = CRGB::Red;
}
} else {
// Turn off LEDs in the inactive group
for (int j = groups[i].start; j <= groups[i].end; j++) {
leds[j] = CRGB::Black;
}
}
}
}
FastLED.show(); // Update the LEDs
}
// Function to light up each letter in a line
void lightUpNextLetter() {
if (currentLine >= numLines) {
currentLine = 0; // Loop back to the first line after the last
}
String line = textLines[currentLine];
if (currentLetter < line.length()) {
char letter = line[currentLetter];
lightUpLetter(letter); // Light up the current letter
currentLetter++;
timer.setTimeout(lightUpNextLetter, LETTER_DELAY); // Schedule the next letter
} else {
lightUpLetter(' ');
currentLetter = 0; // Reset letter index
currentLine++;
timer.setTimeout(lightUpNextLetter, LINE_DELAY); // Delay before the next line
}
}
// Function to light up a letter
void lightUpLetter(char letter) {
// Turn off all groups initially
for (int i = 0; i < sizeof(groups) / sizeof(groups[0]); i++) {
groups[i].active = false;
}
// Find the letter and activate the corresponding groups
Letter* l = findLetter(letter);
if (l != nullptr) {
for (int i = 0; i < l->groupCount; i++) {
int groupIndex = l->groups[i];
groups[groupIndex].active = true;
}
}
}
// Start the text animation
void startTextAnimation() {
timer.setTimeout(lightUpNextLetter, 0); // Start immediately
}
void initializeSDCard() {
Serial.print("Initializing SD card... ");
if (!SD.begin(CS_PIN)) {
Serial.println("Card initialization failed!");
while (true);
}
Serial.println("initialization done.");
// Open and read the file containing text
File textFile = SD.open("text.csv");
if (textFile) {
textLines = new String[MAX_LINES]; // Allocate memory for the lines
while (textFile.available() && numLines < MAX_LINES) {
String line = textFile.readStringUntil('\n');
line.trim(); // Remove trailing whitespace
if (line.length() > 0) {
textLines[numLines++] = line; // Store the line
}
}
textFile.close();
} else {
Serial.println("Error opening text.csv!");
}
Serial.println("Lines loaded from SD card:");
for (int i = 0; i < numLines; i++) {
Serial.println(textLines[i]);
}
}