FastLED: Turn signal coding

I'm using an Arduino Uno (Elegoo) and FastLED to animate turn signals onto WS2812B strips. I have two strips, left and right, for the turn signals. These are going onto a motorcycle. I'm using buck converters on the turn signals and brake to send a 3V3 high to pins A0,A1, and A2.

I have a simple(ish) program to do this. I have it set up so that when pin A0 or A1 is high, the corresponding strip runs a For loop that sends a soft orange light pulsing down the strip. This works perfectly except one thing.

A turn signal on a bike will read as "H,L,H,L,H,L......." until the turn signal switch is turned off. There are a few ways I think I can get around this.

One way would be to have it run the loop for (x)ms so that there is a delay between reads. Once there has been a Low for two(three?) consecutive reads, then it goes back to white. This would work pretty well, but it seems pretty sloppy. Not only will this stay on for a time after I've turned the turn signal off, but there is a potential for a mismatch between pulses from the input in the case that the space width varies slightly between reads.

Another way is for the arduino to read the input, run the loop for the animation once, pause, read the input again, and repeat until both reads are 0, then it returns to the DRL function.

I copied and modified code from the "ColorPalette" example from the FastLED library to do most of this. I think that my code could be significantly simplified, and I don't necessarily need to use palettes to achieve what I need, it's just what I found.

How can I do either option? To be honest, I've been poring over this code for a while and it seems to get more confusing the more I look into it. I think there is a lot of redundancy that could be trimmed down. I couldn't figure out where to upload my sketch, but here's my full code.

#include <FastLED.h>

#define LED_PIN_R 5
#define LED_PIN_L 6
#define BRIGHTNESS 200
#define LED_TYPE WS2812B
#define COLOR_ORDER GRB
#define NUM_STRIPS 2
#define NUM_LEDS_PER_STRIP 60
#define NUM_LEDS NUM_LEDS_PER_STRIP * NUM_STRIPS
#define RturnPin A0
#define LturnPin A1
#define brakePin A2

//set up array
CRGB leds[NUM_LEDS];

#define UPDATES_PER_SECOND 100
int blink = 0;

// const int LturnPin = 8;
// const int RturnPin = 9;
// const int brakePin = 7;
int LturnVal = 0;
int RturnVal = 0;
int brakeVal = 0;
// bool lTurn;
// bool rTurn;
// bool brake;


CRGBPalette16 currentPalette;
TBlendType currentBlending;


void setup() {


  delay(3000);  // power-up safety delay
  //Add the LED
  FastLED.addLeds<LED_TYPE, LED_PIN_R, COLOR_ORDER>(leds, 0, NUM_LEDS_PER_STRIP).setCorrection(TypicalLEDStrip);
  FastLED.addLeds<LED_TYPE, LED_PIN_L, COLOR_ORDER>(leds, NUM_LEDS_PER_STRIP, NUM_LEDS_PER_STRIP).setCorrection(TypicalLEDStrip);

  FastLED.setBrightness(BRIGHTNESS);
  pinMode(LturnPin, INPUT);
  pinMode(RturnPin, INPUT);
  pinMode(brakePin, INPUT);
  currentPalette = RainbowColors_p;
  currentBlending = LINEARBLEND;
}


void loop() {
  whiteOut();
  FastLED.show();
  LturnVal = digitalRead(LturnPin);
  RturnVal = digitalRead(RturnPin);
  brakeVal = digitalRead(brakePin);
  //Serial.println(LturnVal);
  //Serial.println(RturnVal);
  static uint8_t startIndex = 0;
  startIndex = startIndex + 1; /* motion speed */
  DRL(startIndex);

  if (LturnVal == 1 & brakeVal == 1) {
    turnSignalBrake();
    leftTurn(startIndex);
    FastLED.show();
    //FastLED.delay(1000 / UPDATES_PER_SECOND);
  }
  if (RturnVal == 1 & brakeVal == 1) {
    turnSignalBrake();
    rightTurn(startIndex);
    FastLED.show();
    //FastLED.delay(1000 / UPDATES_PER_SECOND);
  } else if (RturnVal && !brakeVal) {

    turnSignal();
    rightTurn(startIndex);
    // FastLED.delay(1000);
    FastLED.show();
    // FastLED.delay(1000 / UPDATES_PER_SECOND);
  } else if (LturnVal && !brakeVal) {

    turnSignal();
    leftTurn(startIndex);
    // FastLED.delay(1000);
    FastLED.show();
    //FastLED.delay(1000 / UPDATES_PER_SECOND);
  }

  if (brakeVal == 1 && LturnVal == 0 && RturnVal == 0) {
    fill_solid(leds, NUM_LEDS, CRGB::Red);
    FastLED.show();
    FastLED.delay(100);

  } else {
    whiteOut();
    FastLED.show();
  }
}

void rightTurn(uint8_t colorIndex) {
  uint8_t brightness = 200;
  //Right Turn Signal
  for (int i = 0; i < 60; ++i) {
    leds[i] = ColorFromPalette(currentPalette, colorIndex, brightness, currentBlending);
    colorIndex += 1;
    //break;
  }
}
void leftTurn(uint8_t colorIndex) {
  uint8_t brightness = 200;
  //Left Turn Signal
  for (int i = 60; i < 120; ++i) {
    leds[i] = ColorFromPalette(currentPalette, colorIndex, brightness, currentBlending);
    colorIndex += 1;
    //break;
  }
}

void DRL(uint8_t colorIndex) {
  uint8_t brightness = 200;
  //Left Turn Signal
  for (int i = 0; i < NUM_LEDS; ++i) {
    leds[i] = ColorFromPalette(currentPalette, colorIndex, brightness, currentBlending);
    colorIndex += 1;
  }
}

void turnSignal() {
  // 'black out' all 16 palette entries...
  fill_solid(currentPalette, 16, CRGB::Black);
  // and set every fourth one to white.
  int i = 0;

  currentPalette[i] = CRGB::DarkOrange;
  i = i + 4;
  currentPalette[i] = CRGB::DarkOrange;
  i = i + 4;
  currentPalette[i] = CRGB::DarkOrange;
  i = i + 4;
  currentPalette[i] = CRGB::DarkOrange;
}
void turnSignalBrake() {
  // 'black out' all 16 palette entries...
  fill_solid(leds, NUM_LEDS, CRGB::Red);
  // FastLED.show();
  fill_solid(currentPalette, 16, CRGB::Red);
  // and set every fourth one to white.
  int i = 0;

  currentPalette[i] = CRGB::DarkOrange;
  i = i + 4;
  currentPalette[i] = CRGB::DarkOrange;
  i = i + 4;
  currentPalette[i] = CRGB::DarkOrange;
  i = i + 4;
  currentPalette[i] = CRGB::DarkOrange;
}

void brake() {
  // fill_solid( currentPalette, 16, CRGB::Red);
  //   uint8_t brightness = 255;
  // //Left Turn Signal
  // for( int i = 0; i < NUM_LEDS; ++i)
  // {
  //     leds[i] = ColorFromPalette( currentPalette, colorIndex, brightness, currentBlending);
  //     colorIndex += 1;
  // }
  /* Function for*/


  fill_solid(leds, NUM_LEDS, CRGB::Red);
  FastLED.show();
  //FastLED.delay(100);
}

void whiteOut() {
  fill_solid(currentPalette, 16, CRGB::White);
}

#define LED_PIN_R 5
#define LED_PIN_L 6
#define BRIGHTNESS 200
#define LED_TYPE WS2812B
#define COLOR_ORDER GRB
#define NumLed_L 60
#define NumLed_R 60
#define RturnPin A0
#define LturnPin A1
#define brakePin A2

#include <FastLED.h>
CRGB ledsL[NumLed_L];
CRGB ledsR[NumLed_R];

bool LturnVal = false;
bool RturnVal = false;
bool brakeVal = false;
byte Pos = 0;

void setup() {
  FastLED.addLeds<LED_TYPE, LED_PIN_R, COLOR_ORDER>(ledsL, NumLed_L).setCorrection(TypicalLEDStrip);
  FastLED.addLeds<LED_TYPE, LED_PIN_L, COLOR_ORDER>(ledsR, NumLed_R).setCorrection(TypicalLEDStrip);
  FastLED.setBrightness(BRIGHTNESS);
  pinMode(LturnPin, INPUT_PULLUP);  // This means when active the pin should be grounded
  pinMode(RturnPin, INPUT_PULLUP);  // but if signal comes not from Turn Signal Switch Lever then this sketch must be adopted
  pinMode(brakePin, INPUT_PULLUP);
  whiteOut();
  FastLED.show();
  delay(1000);
}

void loop() {
  LturnVal = digitalRead(LturnPin) == LOW;
  RturnVal = digitalRead(RturnPin) == LOW;
  brakeVal = digitalRead(brakePin) == LOW;
  Pos = (millis() % 1000UL) / 250UL;

  if (brakeVal)brake();
  else {
    fill_solid(ledsR, NumLed_R, CRGB::Black);
    fill_solid(ledsL, NumLed_L, CRGB::Black);
  }
  if (LturnVal)leftTurn();
  if (RturnVal)rightTurn();
  FastLED.show();
}

void rightTurn() {                              //Right Turn Signal
  //fill_solid(ledsR, NumLed_R, CRGB::Black);
  for (byte i = Pos; i < NumLed_R; i += 4)ledsR[i] = CRGB::DarkOrange;
}

void leftTurn() {                               //Left Turn Signal
  //fill_solid(ledsL, NumLed_L, CRGB::Black);
  for (byte i = Pos; i < NumLed_L; i += 4)ledsL[i] = CRGB::DarkOrange;
}

void brake() {
  fill_solid(ledsL, NumLed_L, CRGB::Red);
  fill_solid(ledsR, NumLed_R, CRGB::Red);
}

void whiteOut() {
  fill_solid(ledsL, NumLed_L, CRGB::White);
  fill_solid(ledsR, NumLed_R, CRGB::White);
}

What does the input read when the turn signal is turned off? Let's assume it's LOW.

To start the animation, you will be looking at a change in state from LOW to HIGH on the input. Once you have picked that up, you can start your animation.

To stop the animation, you will be looking for a LOW period of e.g. 2 seconds on the input.

You can implement this using a state machine with a few states (possibly 2). In the first state, you wait till the signal goes from LOW to HIGH. Once that is detected, you switch to the next state where you use a non-blocking animation and use a millis based timing to detect that a signal is LOW for the 2 seconds.

This (within your topic) IS the Proper Place.

There are easier and cheaper and arguably better ways to do that, if you are saying what you mean.

Can you post a schematic? A hand drawn diagram showing all what's connected and how, power supply included. Snap a celly or screen shot of you drawing.

a7

At what rate does the input blink?

Let's say it's ON for about 300 milliseconds and OFF for about 200 milliseconds. I would consider the blinker to be ON if the light was ON any time in the last 500 milliseconds:

  if (digitalRead(LturnPin))
    LastTimeOn_L = millis();

  if (millis() - LastTimeOn_L < 500)
  {
     // Left turn signal is ON
  }

I haven't really gotten that far. I figured I can adjust the timing once I put it on the bike. I'm already going to have to adjust for my switch to WS2811. That's exactly what I'm wanting, though. I'll play around with that. Thanks!

I tried doing that, but it doesn't change anything. Here's what I did:

 if (digitalRead(LturnPin) && digitalRead(brakePin)){
      LastTimeOn_L = millis();}

  if (millis() - LastTimeOn_L < 500)
  {
    // Left turn signal is ON
        turnSignalBrake();
        leftTurn(startIndex);
        FastLED.show();
  }

  
  if (digitalRead(RturnPin) && digitalRead(brakePin)){
        LastTimeOn_R = millis();}

  if (millis() - LastTimeOn_L < 500)
  {
    // Right turn signal is ON
        turnSignalBrake();
        rightTurn(startIndex);
        FastLED.show();
  }

I used your code in place of the first two if statements. These test two conditions instead of just one, but it should work the same for one condition. If the condition is met (in this case, brake and turn signal are HIGH), then it just goes back to white. It doesn't seem to call turnSignalBrake().

This is probably the cleanest solution, but it's not working. What happens is as soon as the input goes low, it goes back to white. I have two functions that do basically the same thing: DRL() and WhiteOut(). I for whatever reason put them in twice. I've since removed whiteOut(), as DRL() does the same thing, just with a For loop instead of using fill_solid. It seems as if the condition is being checked again before the delay, or a concurrent if statement is picking it up. Honestly, I can't figure out the flow of the if statements I have, when it comes to calling functions within an If loop.

Continuing from post #3, this should give you the idea what I was talking about. I haven't looked at the details of your codes to integrate a break light.

#define RturnPin A0
#define LturnPin A1
#define brakePin A2

// 2 seconds timeout
const uint32_t blinkTimeout = 2000UL;

void setup()
{
  Serial.begin(115200);
  pinMode(LturnPin, INPUT);
  pinMode(RturnPin, INPUT);
  pinMode(brakePin, INPUT);
}

enum class BLINKERMODE
{
  OFF,
  LEFT,
  RIGHT,
};

void loop()
{
  // remember mode
  static BLINKERMODE mode = BLINKERMODE::OFF;
  // remember last state of signals
  static uint8_t lastLeftState;
  static uint8_t lastRightState;
  // remember start time for timeout
  static uint32_t timeoutStarttime;

  uint8_t sigLeftState  = digitalRead(LturnPin);
  uint8_t sigRightState  = digitalRead(RturnPin);


  // only if we're not blinking
  if (mode == BLINKERMODE::OFF)
  {
    // if the left signal changed
    if (sigLeftState != lastLeftState)
    {
      lastLeftState = sigLeftState;
      // from LOW to HIGH
      if (sigLeftState == HIGH)
      {
        // indicate left turn
        mode = BLINKERMODE::LEFT;
        Serial.println(F("Left blinker active"));
      }
    }

    // if the right signal changed
    if (sigRightState != lastRightState)
    {
      lastRightState = sigRightState;
      // from LOW to HIGH
      if (sigRightState == HIGH)
      {
        // indicate right turn
        mode = BLINKERMODE::RIGHT;
        Serial.println(F("Right blinker active"));
      }
    }
  }

  if (mode == BLINKERMODE::LEFT)
  {
    // do non-blocking left turn animation
    //...
    //...

  }
  else if (mode == BLINKERMODE::RIGHT)
  {
    // do non-blocking right turn animation
    //...
    //...
  }
  else if (mode == BLINKERMODE::OFF)
  {
    // nothing to do
  }

  // if we are blinking
  if (mode != BLINKERMODE::OFF)
  {
    // if either signal is HIGH
    if (sigLeftState == HIGH || sigRightState == HIGH)
    {
      // reset the start time of the timeout
      timeoutStarttime = millis();
    }
    else
    {
      // if no signal for two seconds
      if (millis() - timeoutStarttime >= blinkTimeout)
      {
        // stop the animation
        //...
        //...
        mode = BLINKERMODE::OFF;
        Serial.println(F("Blinking canceled"));
      }
    }
  }
}

Not tested but compiles.

#define LED_TYPE WS2812B
#define COLOR_ORDER GRB
#define BRIGHTNESS 255
#define NumLed_L 60
#define NumLed_R 60

#define LED_PIN_R 5
#define LED_PIN_L 6
#define RturnPin A0
#define LturnPin A1
#define brakePin A2
#define PulseLength 1050 // half of blinking period + 50 milliseconds

byte Pos = 0;

#include <FastLED.h>
CRGB ledsL[NumLed_L];
CRGB ledsR[NumLed_R];

void setup() {
  FastLED.addLeds<LED_TYPE, LED_PIN_R, COLOR_ORDER>(ledsL, NumLed_L).setCorrection(TypicalLEDStrip);
  FastLED.addLeds<LED_TYPE, LED_PIN_L, COLOR_ORDER>(ledsR, NumLed_R).setCorrection(TypicalLEDStrip);
  FastLED.setBrightness(BRIGHTNESS);
  pinMode(LturnPin, INPUT);  // This means the pin is EPU driven
  pinMode(RturnPin, INPUT);
  pinMode(brakePin, INPUT);
  whiteOut();
  FastLED.show();
  delay(1000);
}

void loop() {
  static bool LturnVal = false;
  static bool RturnVal = false;
  static bool brakeVal = false;
  static bool isTimeOut = false;
  static uint32_t awaiting = 0;

  if (digitalRead(RturnPin) == HIGH) {
    RturnVal = true;
    awaiting = millis();
  }
  if (digitalRead(LturnPin) == HIGH) {
    LturnVal = true;
    awaiting = millis();
  }
  brakeVal = digitalRead(brakePin) == HIGH;
  Pos = (millis() % 1000UL) / 250UL;

  if (millis() - awaiting > PulseLength) {
    LturnVal = false;
    RturnVal = false;
  }

  if (brakeVal)brake();
  else Black();

  if (LturnVal)leftTurn();
  if (RturnVal)rightTurn();
  if (LturnVal && RturnVal)Hazard();
  FastLED.show();
}

void rightTurn() {                              //Right Turn Signal
  //fill_solid(ledsR, NumLed_R, CRGB::Black);
  for (byte i = Pos; i < NumLed_R; i += 4)ledsR[i] = CRGB::DarkOrange;
}

void leftTurn() {                               //Left Turn Signal
  //fill_solid(ledsL, NumLed_L, CRGB::Black);
  for (byte i = Pos; i < NumLed_L; i += 4)ledsL[i] = CRGB::DarkOrange;
}

void brake() {
  fill_solid(ledsL, NumLed_L, CRGB::Red);
  fill_solid(ledsR, NumLed_R, CRGB::Red);
}

void whiteOut() {
  fill_solid(ledsL, NumLed_L, CRGB::White);
  fill_solid(ledsR, NumLed_R, CRGB::White);
}

void Hazard() {
  if ((millis() / 500UL) % 2) {
    fill_solid(ledsL, NumLed_L, CRGB::DarkOrange);
    fill_solid(ledsR, NumLed_R, CRGB::DarkOrange);
  }
  else Black();
}

void Black() {
  fill_solid(ledsR, NumLed_R, CRGB::Black);
  fill_solid(ledsL, NumLed_L, CRGB::Black);
}

I ended up using your code as a structure for mine. The delay works. I haven't hooked it up to the bike yet, but I don't forsee any major issues.

#define LED_TYPE WS2811
#define COLOR_ORDER RGB
#define BRIGHTNESS 120
#define NumLed_L 18
#define NumLed_R 18
#define UPDATES_PER_SECOND 100
#define LED_PIN_R 5
#define LED_PIN_L 6
#define RturnPin A0
#define LturnPin A1
#define brakePin A2
#define PulseLength 1050  // half of blinking period + 50 milliseconds

byte Pos = 0;

#include <FastLED.h>
CRGB ledsL[NumLed_L];
CRGB ledsR[NumLed_R];
CRGBPalette16 currentPalette;
TBlendType currentBlending;
void setup() {
  FastLED.addLeds<LED_TYPE, LED_PIN_R, COLOR_ORDER>(ledsL, NumLed_L).setCorrection(TypicalLEDStrip);
  FastLED.addLeds<LED_TYPE, LED_PIN_L, COLOR_ORDER>(ledsR, NumLed_R).setCorrection(TypicalLEDStrip);
  FastLED.setBrightness(BRIGHTNESS);
  pinMode(LturnPin, INPUT);  // This means the pin is EPU driven
  pinMode(RturnPin, INPUT);
  pinMode(brakePin, INPUT);
  currentBlending = NOBLEND;
  White();
  FastLED.show();
  delay(1000);
}

void loop() {
  static bool LturnVal = false;
  static bool RturnVal = false;
  static bool brakeVal = false;
  static bool isTimeOut = false;
  static uint32_t awaiting = 0;
  static uint8_t startIndex = 0;
  startIndex = startIndex + 1; /* motion speed */

  if (digitalRead(RturnPin) == HIGH) {
    RturnVal = true;
    awaiting = millis();
  }
  if (digitalRead(LturnPin) == HIGH) {
    LturnVal = true;
    awaiting = millis();
  }
  brakeVal = digitalRead(brakePin);
  Pos = (millis() % 1000UL) / 250UL;

  if (millis() - awaiting > PulseLength) {
    LturnVal = false;
    RturnVal = false;
  }

  if (brakeVal && !LturnVal && !RturnVal) brake();
  else
    White();
  if (LturnVal) leftTurn(startIndex);
  FastLED.delay(250 / UPDATES_PER_SECOND);
  if (RturnVal) rightTurn(startIndex);
  FastLED.delay(1000 / UPDATES_PER_SECOND);
  //if (LturnVal && RturnVal) Hazard();
  FastLED.show();
}

void rightTurn(uint8_t colorIndex) {  //Right Turn Signal
  //fill_solid(ledsR, NumLed_R, CRGB::Black);
  uint8_t brightness = 200;
  if (digitalRead(brakePin)) fill_solid(currentPalette, 16, CRGB::Red);
  else
    fill_solid(currentPalette, 16, CRGB::Black);
  int n = 0;
  currentPalette[n] = CRGB::DarkOrange; n += 2;
  currentPalette[n] = CRGB::DarkOrange; n += 2;
  currentPalette[n] = CRGB::DarkOrange; n += 2;
  currentPalette[n] = CRGB::DarkOrange;
  for (int i = 0; i < NumLed_R; i++) {
    ledsR[i] = ColorFromPalette(currentPalette, colorIndex, brightness, currentBlending);
  }
}

void leftTurn(uint8_t colorIndex) {
  //fill_solid(ledsR, NumLed_L, CRGB::Black);
  uint8_t brightness = 200;
  if (digitalRead(brakePin)) fill_solid(currentPalette, 16, CRGB::Red);
  else
    fill_solid(currentPalette, 16, CRGB::Black);
  int n = 0;
  currentPalette[n] = CRGB::DarkOrange;
  n += 4;
  currentPalette[n] = CRGB::DarkOrange;
  n += 4;
  currentPalette[n] = CRGB::DarkOrange;
  n += 4;
  currentPalette[n] = CRGB::DarkOrange;
  for (int i = 0; i < NumLed_L; i++) {
    ledsL[i] = ColorFromPalette(currentPalette, colorIndex, brightness, currentBlending);
    colorIndex += 1;
  }
}

void brake() {
  fill_solid(ledsL, NumLed_L, CRGB::Red);
  fill_solid(ledsR, NumLed_R, CRGB::Red);
}

void White() {
  fill_solid(ledsR, NumLed_R, CRGB::White);
  fill_solid(ledsL, NumLed_L, CRGB::White);
}

Thanks to everyone who helped. This was my first experience on here and I was amazed at how many people relied.

If you wanted to cut this in the leftTurn function down

You could do:

for (int n = 0; n < 16; n += 4)
    currentPalette[n] = CRGB::DarkOrange;

And the same/similar in the other rightTurn function

Thanks! That cleans it up quite nicely!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.