#include <FastLED.h>
#include <LiquidCrystal.h>
#include <EEPROM.h>
#define NUM_LEDS 29
#define RIGHT_LED_PIN 11
#define LEFT_LED_PIN 12
#define RIGHT_INDICATOR_PIN A0
#define LEFT_INDICATOR_PIN A1
#define BRAKE_PIN A2
#define RUNNING_LIGHTS_PIN A3
#define THRESHOLD_VOLTAGE 800
#define RS_PIN 10
#define ENABLE_PIN 9
#define SCREEN7_PIN 7
#define SCREEN6_PIN 6
#define SCREEN5_PIN 5
#define SCREEN4_PIN 4
#define BTN_UP 3
#define BTN_DOWN 2
#define ROTARY_ENCODER_SW 0
#define ROTARY_ENCODER_CLK 8
#define ROTARY_ENCODER_DT 1
LiquidCrystal lcd(RS_PIN, ENABLE_PIN, SCREEN4_PIN, SCREEN5_PIN, SCREEN6_PIN, SCREEN7_PIN);
CRGB leftLeds[NUM_LEDS];
CRGB rightLeds[NUM_LEDS];
bool rightIndicatorOn = false;
bool pastRightIndicatorOn = false;
bool leftIndicatorOn = false;
bool pastLeftIndicatorOn = false;
uint8_t endIndexRight = 0;
uint8_t endIndexLeft = 0;
static unsigned long lastUpdate = 0;
static unsigned long blinkerLeftLastUpdate = 0;
static unsigned long blinkerRightLastUpdate = 0;
static unsigned long blinkersOffTimerLeft = -1;
static unsigned long blinkersOffTimerRight = -1;
uint8_t indicatorDelay = 10; // user modifiable : blinker's speed
uint8_t hazardDelay = 10; // user modifiable : hazard lights speed
int blinkersDelay = 700; // user modifiable : delay turning off the blinker
static unsigned long blinkersOffDelay = 100; // user modifiable : fix weird glitch when car does [blinker on, blinker off, quickly blinker on, quickly blinker off]
int startupAnimationVal = 1; //user modifiable : change the startup animation
int startupAnimationSpeed = 10; // user modifiable
int animationTileSize = 3; // user modifiable /!\ a large size or a size inferior to 1 can breake (not tested)
String valNames[] = {"indicatorDelay", "hazardDelay", "blinkersDelay", "blinkersOffDelay", "startupAnimation", "animationSpeed", "tileSize"};
int screenVarPtr = 0;
int upBtnPressed = 0;
int downBtnPressed = 0;
int lastSW;
int lastCLK;
static unsigned long lastSave = 0;
uint8_t saveDelay = 1000;
void setup() {
// put your setup code here, to run once:
lcd.begin(16,2);
lcd.print(valNames[screenVarPtr]);
displayScreenVar();
FastLED.addLeds<WS2812, LEFT_LED_PIN, GRB>(leftLeds, NUM_LEDS);
FastLED.addLeds<WS2812, RIGHT_LED_PIN, GRB>(rightLeds, NUM_LEDS);
FastLED.setBrightness(255);
pinMode(RIGHT_INDICATOR_PIN, INPUT);
pinMode(LEFT_INDICATOR_PIN, INPUT);
pinMode(BRAKE_PIN, INPUT);
pinMode(RUNNING_LIGHTS_PIN, INPUT);
pinMode(BTN_UP, INPUT_PULLUP);
pinMode(BTN_DOWN, INPUT_PULLUP);
pinMode(ROTARY_ENCODER_SW, INPUT_PULLUP);
pinMode(ROTARY_ENCODER_DT, INPUT);
pinMode(ROTARY_ENCODER_CLK, INPUT);
lastSW = digitalRead(ROTARY_ENCODER_SW);
lastCLK = digitalRead(ROTARY_ENCODER_CLK);
//Serial.begin(9600);
uint8_t test = 0;
EEPROM.get(0, test);
//Serial.println(test);
if(test != 197){
uint8_t myNum = 197;
EEPROM.put(0, myNum);
EEPROM.put(1, indicatorDelay);
EEPROM.put(2, hazardDelay);
EEPROM.put(3, blinkersDelay);
EEPROM.put(5, startupAnimationVal);
EEPROM.put(7, startupAnimationSpeed);
EEPROM.put(9, animationTileSize);
EEPROM.put(11, blinkersOffDelay);
} else {
EEPROM.get(1, indicatorDelay);
EEPROM.get(2, hazardDelay);
EEPROM.get(3, blinkersDelay);
EEPROM.get(5, startupAnimationVal);
EEPROM.get(7, startupAnimationSpeed);
EEPROM.get(9, animationTileSize);
EEPROM.get(11, blinkersOffDelay);
}
startupAnimation(startupAnimationVal); // user modifiable : comment to disable startup animation, change number to change animation (if multiple animation exist)
}
void loop() {
// put your main code here, to run repeatedly:
updateScreen();
for(int i=0; i<NUM_LEDS; ++i){
rightLeds[i].nscale8(255); // Full brightness
leftLeds[i].nscale8(255); // Full brightness
}
int rightIndicatorVoltage = analogRead(RIGHT_INDICATOR_PIN);
int leftIndicatorVoltage = analogRead(LEFT_INDICATOR_PIN);
int brakeVoltage = analogRead(BRAKE_PIN);
int runningLightsVoltage = analogRead(RUNNING_LIGHTS_PIN);
/*Serial.print("voltage right : ");
Serial.println(rightIndicatorVoltage);
Serial.print("voltage left : ");
Serial.println(leftIndicatorVoltage);*/
if(brakeVoltage > THRESHOLD_VOLTAGE){ // if brake is on
if(!rightIndicatorOn && millis() - blinkerRightLastUpdate > blinkersDelay){
for(int i=0; i<NUM_LEDS; ++i){
rightLeds[i] = CRGB::Red;
}
}
if(!leftIndicatorOn && millis() - blinkerLeftLastUpdate > blinkersDelay){
for(int i=0; i<NUM_LEDS; ++i){
leftLeds[i] = CRGB::Red;
}
}
} else if(runningLightsVoltage > THRESHOLD_VOLTAGE){
if(!rightIndicatorOn && millis() - blinkerRightLastUpdate > blinkersDelay){
for(int i=0; i<NUM_LEDS; ++i){
rightLeds[i] = CRGB::Red;
rightLeds[i].nscale8(128); // Half brightness
}
}
if(!leftIndicatorOn && millis() - blinkerLeftLastUpdate > blinkersDelay){
for(int i=0; i<NUM_LEDS; ++i){
leftLeds[i] = CRGB::Red;
leftLeds[i].nscale8(128); // Half brightness
}
}
} else {
//Serial.println(millis() - blinkerRightLastUpdate > blinkersDelay);
//Serial.println(millis() - blinkerLeftLastUpdate > blinkersDelay);
if(!rightIndicatorOn && millis() - blinkerRightLastUpdate > blinkersDelay){
for(int i=0; i<NUM_LEDS; ++i){
rightLeds[i] = CRGB::Black;
}
}
if(!leftIndicatorOn && millis() - blinkerLeftLastUpdate > blinkersDelay){
for(int i=0; i<NUM_LEDS; ++i){
leftLeds[i] = CRGB::Black;
}
}
}
if(rightIndicatorVoltage <= THRESHOLD_VOLTAGE){
if(blinkersOffTimerRight == -1){
blinkersOffTimerRight = millis();
}
if(millis() - blinkersOffTimerRight > blinkersOffDelay){
if(leftIndicatorOn && rightIndicatorOn){
endIndexLeft = 0;
}
rightIndicatorOn = false;
endIndexRight = 0;
}
}
if(leftIndicatorVoltage <= THRESHOLD_VOLTAGE){
if(blinkersOffTimerLeft == -1){
blinkersOffTimerLeft = millis();
}
if(millis() - blinkersOffTimerLeft > blinkersOffDelay){
if(leftIndicatorOn && rightIndicatorOn){
endIndexRight = 0;
}
leftIndicatorOn = false;
endIndexLeft = 0;
}
}
if(rightIndicatorVoltage > THRESHOLD_VOLTAGE && leftIndicatorVoltage > THRESHOLD_VOLTAGE){ // if hazard is on
//turnAllOff();
if(!rightIndicatorOn || !leftIndicatorOn){
rightIndicatorOn = true;
leftIndicatorOn = true;
resetIndicator();
}
hazards();
blinkerLeftLastUpdate = millis();
blinkerRightLastUpdate = millis();
blinkersOffTimerLeft = -1;
blinkersOffTimerRight = -1;
FastLED.show();
return;
} else if(rightIndicatorVoltage > THRESHOLD_VOLTAGE){ // if right blinker
fill_solid(rightLeds, NUM_LEDS, CRGB::Black); // Turn off right LEDs
if(!rightIndicatorOn){
rightIndicatorOn = true;
resetIndicator();
}
blinker(rightLeds, &endIndexRight);
blinkerRightLastUpdate = millis();
blinkersOffTimerRight = -1;
FastLED.show();
return;
} else if(leftIndicatorVoltage > THRESHOLD_VOLTAGE){ // if left blinker
fill_solid(leftLeds, NUM_LEDS, CRGB::Black); // Turn off left LEDs
if(!leftIndicatorOn){
leftIndicatorOn = true;
resetIndicator();
}
blinker(leftLeds, &endIndexLeft);
blinkerLeftLastUpdate = millis();
blinkersOffTimerLeft = -1;
FastLED.show();
return;
}
FastLED.show(); // Call FastLED.show() here
}
void updateScreen(){
if(!digitalRead(BTN_UP) && !upBtnPressed){
upBtnPressed = 1;
--screenVarPtr;
if(screenVarPtr < 0){
screenVarPtr = 6;
}
lcd.clear();
lcd.setCursor(0,0);
lcd.print(valNames[screenVarPtr]);
displayScreenVar();
}
if(digitalRead(BTN_UP) && upBtnPressed){
upBtnPressed = 0;
}
if(!digitalRead(BTN_DOWN) && !downBtnPressed){
downBtnPressed = 1;
++screenVarPtr;
if(screenVarPtr > 6){
screenVarPtr = 0;
}
lcd.clear();
lcd.setCursor(0,0);
lcd.print(valNames[screenVarPtr]);
displayScreenVar();
}
if(digitalRead(BTN_DOWN) && downBtnPressed){
downBtnPressed = 0;
}
if(!upBtnPressed && !downBtnPressed && digitalRead(BTN_UP) && digitalRead(BTN_DOWN)){
if(digitalRead(ROTARY_ENCODER_SW) != lastSW){
lastSW = digitalRead(ROTARY_ENCODER_SW);
delay(10);
}
if(digitalRead(ROTARY_ENCODER_CLK) != lastCLK){
lastCLK = digitalRead(ROTARY_ENCODER_CLK);
if(digitalRead(ROTARY_ENCODER_CLK) == LOW){
if(digitalRead(ROTARY_ENCODER_CLK) != digitalRead(ROTARY_ENCODER_DT)){
changeScreenVar(1);
} else {
changeScreenVar(-1);
}
displayScreenVar();
}
}
}
if(millis() - lastSave > saveDelay){
saveToMemory();
lastSave = millis();
}
}
void saveToMemory(){
EEPROM.put(1, indicatorDelay);
EEPROM.put(2, hazardDelay);
EEPROM.put(3, blinkersDelay);
EEPROM.put(5, startupAnimationVal);
EEPROM.put(7, startupAnimationSpeed);
EEPROM.put(9, animationTileSize);
EEPROM.put(11, blinkersOffDelay);
}
void changeScreenVar(int inc){
switch(screenVarPtr){
case 0:
indicatorDelay += inc;
break;
case 1:
hazardDelay += inc;
break;
case 2:
blinkersDelay += inc;
break;
case 3:
blinkersOffDelay += inc;
break;
case 4:
startupAnimationVal += inc;
break;
case 5:
startupAnimationSpeed += inc;
break;
case 6:
animationTileSize += inc;
break;
default:
break;
}
}
void displayScreenVar(){
switch(screenVarPtr){
case 0:
lcd.setCursor(0,1);
lcd.print(" ");
lcd.setCursor(0,1);
lcd.print(indicatorDelay);
break;
case 1:
lcd.setCursor(0,1);
lcd.print(" ");
lcd.setCursor(0,1);
lcd.print(hazardDelay);
break;
case 2:
lcd.setCursor(0,1);
lcd.print(" ");
lcd.setCursor(0,1);
lcd.print(blinkersDelay);
break;
case 3:
lcd.setCursor(0,1);
lcd.print(" ");
lcd.setCursor(0,1);
lcd.print(blinkersOffDelay);
break;
case 4:
lcd.setCursor(0,1);
lcd.print(" ");
lcd.setCursor(0,1);
lcd.print(startupAnimationVal);
break;
case 5:
lcd.setCursor(0,1);
lcd.print(" ");
lcd.setCursor(0,1);
lcd.print(startupAnimationSpeed);
break;
case 6:
lcd.setCursor(0,1);
lcd.print(" ");
lcd.setCursor(0,1);
lcd.print(animationTileSize);
break;
default:
lcd.setCursor(0,1);
lcd.print(" ");
lcd.setCursor(0,1);
lcd.print("error");
break;
}
}
void blinker(CRGB* leds, uint8_t* endIndex){
indicatorAnimation(leds, *endIndex);
if (millis() - lastUpdate > indicatorDelay && *endIndex < NUM_LEDS) {
lastUpdate = millis();
*endIndex += 1;
}
}
void turnAllOff(){
for(int i=0; i<NUM_LEDS; ++i){
rightLeds[i] = CRGB::Black;
leftLeds[i] = CRGB::Black;
}
}
void resetIndicator() {
endIndexRight = 0; // Reset endIndexRight to NUM_LEDS
endIndexLeft = 0; // Reset endIndexLeft to NUM_LEDS
}
void hazards() {
indicatorAnimation(rightLeds, endIndexRight);
indicatorAnimation(leftLeds, endIndexLeft);
if (millis() - lastUpdate > hazardDelay && endIndexRight < NUM_LEDS) {
lastUpdate = millis();
endIndexRight += 1;
endIndexLeft += 1;
}
}
void indicatorAnimation(CRGB* leds, uint8_t endIndex) {
fill_solid(leds, NUM_LEDS, CRGB::Black); // Turn off right LEDs
for (int i = 0; i < endIndex; ++i) {
leds[i] = CHSV(78, 255, 255); // More greenish color
}
return;
}
void startupAnimation(int animation){
switch(animation){// add animation functions in here
case 3:
animation3();
break;
case 2:
animation2();
break;
default:
animation1();
break;
}
}
void animation1(){
int rightIndex = 0;
for(int k=0; k<NUM_LEDS; k+=animationTileSize){
for(int i=NUM_LEDS-animationTileSize; i>=k; --i){
fill_solid(rightLeds+k, NUM_LEDS-k, CRGB::Black);
fill_solid(leftLeds+k, NUM_LEDS-k, CRGB::Black);
for(int j=i; j<i+animationTileSize; ++j){
rightLeds[j] = CRGB::Red;
leftLeds[j] = CRGB::Red;
}
FastLED.show();
delay(startupAnimationSpeed);
}
}
fill_solid(rightLeds+NUM_LEDS-animationTileSize, animationTileSize, CRGB::Red);
fill_solid(leftLeds+NUM_LEDS-animationTileSize, animationTileSize, CRGB::Red);
FastLED.show();
delay(750);
fill_solid(rightLeds+NUM_LEDS-NUM_LEDS % animationTileSize, NUM_LEDS % animationTileSize, CRGB::Black);
fill_solid(leftLeds+NUM_LEDS-NUM_LEDS % animationTileSize, NUM_LEDS % animationTileSize, CRGB::Black);
FastLED.show();
for(int i = NUM_LEDS-(NUM_LEDS % animationTileSize) - animationTileSize; i>=0; i-=animationTileSize){
for(int j=i+1; j<NUM_LEDS; ++j){
fill_solid(rightLeds+i, NUM_LEDS-i, CRGB::Black);
fill_solid(leftLeds+i, NUM_LEDS-i, CRGB::Black);
for(int k=j; k<NUM_LEDS && k-j<animationTileSize; ++k){
rightLeds[k] = CRGB::Red;
leftLeds[k] = CRGB::Red;
}
FastLED.show();
delay(startupAnimationSpeed);
}
}
fill_solid(rightLeds+NUM_LEDS-animationTileSize, animationTileSize, CRGB::Black);
fill_solid(leftLeds+NUM_LEDS-animationTileSize, animationTileSize, CRGB::Black);
FastLED.show();
for(int i=0; i<256; ++i){
fill_solid(leftLeds, NUM_LEDS, CRGB(i, 0, 0));
fill_solid(rightLeds, NUM_LEDS, CRGB(i, 0, 0));
FastLED.show();
delay(3);
}
}
void animation2(){
fill_solid(rightLeds, NUM_LEDS, CRGB::Black);
fill_solid(leftLeds, NUM_LEDS, CRGB::Black);
for(int i=0; i<animationTileSize; ++i){
rightLeds[i] = CRGB::Red;
leftLeds[i] = CRGB::Red;
FastLED.show();
delay(startupAnimationSpeed);
}
for(int i=-1; i<NUM_LEDS; ++i){
fill_solid(rightLeds, NUM_LEDS, CRGB::Black);
fill_solid(leftLeds, NUM_LEDS, CRGB::Black);
for(int j=i+1; j<animationTileSize+i+1 && j<NUM_LEDS; ++j){
rightLeds[j] = CRGB::Red;
leftLeds[j] = CRGB::Red;
}
FastLED.show();
delay(startupAnimationSpeed);
}
delay(startupAnimationSpeed*3);
for(int i=0; i<animationTileSize; ++i){
rightLeds[i] = CRGB::Red;
leftLeds[i] = CRGB::Red;
FastLED.show();
delay(startupAnimationSpeed);
}
for(int i=-1; i<NUM_LEDS; ++i){
fill_solid(rightLeds, NUM_LEDS, CRGB::Black);
fill_solid(leftLeds, NUM_LEDS, CRGB::Black);
for(int j=i+1; j<animationTileSize+i+1 && j<NUM_LEDS; ++j){
rightLeds[j] = CRGB::Red;
leftLeds[j] = CRGB::Red;
}
FastLED.show();
delay(startupAnimationSpeed);
}
delay(startupAnimationSpeed*80);
fill_solid(rightLeds, NUM_LEDS, CRGB::Black);
fill_solid(leftLeds, NUM_LEDS, CRGB::Black);
for(int i=0; i<NUM_LEDS; ++i){
rightLeds[i] = CRGB::Red;
leftLeds[i] = CRGB::Red;
FastLED.show();
delay(startupAnimationSpeed*6);
}
delay(1500);
}
void animation3(){
for(int i=0; i<NUM_LEDS; ++i){
int led = random(0, NUM_LEDS);
while(rightLeds[led] != CRGB::Black){
led = random(0, NUM_LEDS);
}
double j = 2;
while(j<255){
rightLeds[led] = CRGB(j, 0, 0);
leftLeds[led] = CRGB(j, 0, 0);
j*=1.3;
FastLED.show();
delay(4);
}
}
delay(750);
for(int i=255; i>=0; i-=6){
fill_solid(rightLeds, NUM_LEDS, CRGB(i, 0, 0));
fill_solid(leftLeds, NUM_LEDS, CRGB(i, 0, 0));
FastLED.show();
}
delay(2);
for(int i=0; i<256; i+=6){
fill_solid(rightLeds, NUM_LEDS, CRGB(i, 0, 0));
fill_solid(leftLeds, NUM_LEDS, CRGB(i, 0, 0));
FastLED.show();
}
for(int i=255; i>=0; i-=6){
fill_solid(rightLeds, NUM_LEDS, CRGB(i, 0, 0));
fill_solid(leftLeds, NUM_LEDS, CRGB(i, 0, 0));
FastLED.show();
}
delay(2);
for(int i=0; i<256; i+=6){
fill_solid(rightLeds, NUM_LEDS, CRGB(i, 0, 0));
fill_solid(leftLeds, NUM_LEDS, CRGB(i, 0, 0));
FastLED.show();
}
delay(1500);
}