#include <FastLED.h>
#include <SD.h>
#include <AsyncTimer.h>
/* #include "LCDIC2.h" */
/* LCDIC2 lcd(0x27, 16, 2); */
#define POTENTIOMETER_R 0
#define POTENTIOMETER_G 1
#define POTENTIOMETER_B 2
#define POTENTIOMETER_SPEED 3
#define POTENTIOMETER_RANDOM_COLOR 4
#define POTENTIOMETER_NOISE 5
#define LED_PIN 3
#define CS_PIN 10
#define NUM_LEDS 30
#define MAX_LINES 10
CRGB leds[NUM_LEDS];
String* textLines = nullptr;
uint8_t numLines = 0;
uint8_t potRed;
uint8_t potGreen;
uint8_t potBlue;
uint16_t potSpeed = 400;
uint8_t potRandomColor;
uint8_t potNoise = 0;
uint16_t zPosition = 0;
// Grouping of LEDS and Letters
struct LEDGroup {
uint8_t start;
uint8_t end;
bool active;
};
struct Letter {
char name;
const uint8_t* groups;
uint8_t groupCount;
};
// LED groups definiition
LEDGroup groups[] = {
{ 0, 9, false }, { 10, 19, false }, { 20, 29, false }
};
// Active groups for each letter
const uint8_t groupsA[] = { 0, 1, 2};
const uint8_t groupsB[] = { 0};
const uint8_t groupsC[] = { 2};
// 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]) }
};
AsyncTimer timer;
uint8_t currentLine = 0;
uint16_t currentLetter = 0;
// Colors
DEFINE_GRADIENT_PALETTE(NorthernLightsPalette) {
0, 0, 207, 82,
62, 3, 46, 62,
143, 25, 100, 106,
192, 0, 198, 144,
255, 0, 223, 150
};
CRGBPalette16 currentPalette = CRGBPalette16(NorthernLightsPalette);
void setup() {
/* if (lcd.begin()) lcd.print("Tænker på dig!"); */
FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
Serial.begin(9600);
pinMode(POTENTIOMETER_R, OUTPUT);
pinMode(POTENTIOMETER_G, OUTPUT);
pinMode(POTENTIOMETER_B, OUTPUT);
pinMode(POTENTIOMETER_SPEED, OUTPUT);
pinMode(POTENTIOMETER_RANDOM_COLOR, OUTPUT);
pinMode(POTENTIOMETER_NOISE, OUTPUT);
initializeSDCard();
startTextAnimation();
}
void loop() {
timer.handle(); // Handle all timer events
for (uint8_t i = 0; i < 15; i++) {
lcd.setCursor(i, 1);
delay(250);
}
for (uint8_t i = 15; i > 0; i--) {
lcd.setCursor(i, 1);
delay(250);
}
/* --- READ POTENTIOMETERS --- */
potRed = map(analogRead(POTENTIOMETER_R), 0, 1023, 0, 255);
potGreen = map(analogRead(POTENTIOMETER_G), 0, 1023, 0, 255);
potBlue = map(analogRead(POTENTIOMETER_B), 0, 1023, 0, 255);
potSpeed = map(analogRead(POTENTIOMETER_SPEED), 0, 1023, 1000, 100);
potRandomColor = map(analogRead(POTENTIOMETER_RANDOM_COLOR), 0, 1023, 0, 255);
potNoise = map(analogRead(POTENTIOMETER_NOISE), 0, 1023, 0, 255);
EVERY_N_MILLIS(100) {
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++) {
uint8_t noise = inoise8(j * 50, 0, zPosition);
uint8_t dynamicHue = (millis() / 10 + j * 10) % 255;
CRGB randomColor = CHSV(dynamicHue, 255, 255);
CRGB color1 = blend(CRGB(potRed, potGreen, potBlue), randomColor, potRandomColor);
CRGB color2 = ColorFromPalette(currentPalette, noise);
leds[j] = blend(color1, color2, potNoise);
}
} else {
// Turn off LEDs in the inactive group
for (int j = groups[i].start; j <= groups[i].end; j++) {
leds[j] = CRGB::Black;
/* uint8_t randomN = random(0, 255);
if (randomN >= potRandomColor) {
uint8_t noise = inoise8(j * 50, 0, zPosition);
CRGB color1 = blend(CRGB(potRed, potGreen, potBlue), CRGB::Black, 200);
CRGB color2 = ColorFromPalette(currentPalette, noise);
leds[j] = blend(color1, color2, potNoise);
} else {
leds[j] = CRGB::Black;
} */
}
}
}
}
EVERY_N_MILLISECONDS(10) {
zPosition += 5;
}
FastLED.show(); // Update the LEDs
}
// 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;
}
// 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, potSpeed); // Set the next letter
} else {
lightUpLetter(' ');
currentLetter = 0; // Reset letter index
currentLine++;
timer.setTimeout(lightUpNextLetter, potSpeed * 4); // 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("abc.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 abc.csv!");
}
Serial.println("Lines loaded from SD card:");
for (int i = 0; i < numLines; i++) {
Serial.println(textLines[i]);
}
}