Motorcycle signal lights – DEVELOPPARADISE
25/06/2018

Motorcycle signal lights

Introduction

So a few years ago I started building custom motorcycles in my free time as a way to relax after work. the thing about custom bikes is that they are usually in someway original in looks. the first two bikes built did have an Arduino running the turn signals and working with the break lights, but this one I wanted to be different. you can see the build here if your curious.

I wanted all the lighting to be LED ran, but online options were pretty poor, so I had to build all my own marker lights to fit with the look of the bike

Although the “finished” product is still going through revisions, it’s stable enough to use with my summer daily driver.

Background

The Arduino code is fairly straight forward; it is not optimized yet, uses no external libraries other than <avr/wdt.h> for a watchdog timer. 

The circuit is a little more complicated, but not too bad to wire up. The reasons all the extra parts had to be added, instead of using the Arduino to run the lights directly; each pin can only handle about 20ma @ 5vdc. The super bright white LEDs I sourced from Digi-key consumed about 20ma apiece. even if ran in series to reduce the amp load, you still drop around 2vdc per LED, limiting each output pin to 2 LEDs, and that was not enough for what i needed. so some PNP Darlington transistors were added to the mix so i could pump out a total of 1.0A per output if i needed to. 

Here’s a rough sketch of what was built. (I will update this article with an actual cad drawing as soon as i can)

Motorcycle signal lights

The above drawing ended up looking like the following below.

Motorcycle signal lights

Motorcycle signal lights

Motorcycle signal lights

Motorcycle signal lights

 

Using the code

First off button de-bouncing! There are actually 2 versions of the button de-bouncing done: one for the break switches and the other for turn signals and high/low beam selector. All of the input are set HIGH, by setting this pin to ground (through the frame chassis) this tells the code that the break is pushed.

But, you can get a lot of false positives as the switch is almost activated or the bike goes over a bump. so there needs to be a delay time where we have to see the input in a constant state for x amount of time; I’ve found around 100ms works just fine.

The code below will set a time stamp every time the input switches state, if the timestamp exceeds the 100ms thresh hold, we know this is likely a solid state. if the state now also matches _on (LOW) then the break lights should come on.

bool isBreak() {   static long lasttm;   static int last;   int state = digitalRead(switchB);   bool ret = false;    if (last != state) {     lasttm = millis();     last = state;   }    if ((millis() - lasttm) > 100) {     ret = last == _on;   }    return ret; }

letting go of the break switch will flip the state and return false, no time delay.

The turn signal buttons have a slightly different flavor. The code is similar to the break logic, but now I only want the one instance when the button is initially pressed to trigger some other code; this is handled by the triggered boolean and the code will only return true one time for each time it is pressed, kind of like pressing the power button on your computer.

bool isLeftTurn() {   static long lasttm;   static int last;   static bool triggered;   int state = digitalRead(switchL);   bool ret = false;    if (last != state) {     lasttm = millis();     last = state;     triggered = true;   }    if ((millis() - lasttm) > 100 && triggered) {     ret = last == _on;     triggered = false;   }   return ret; }

The setHiLowBeam works pretty much the same except it handles the output side also

void setHiLowBeam() {   static long lasttm;   static int last;   static bool triggered;   int state = digitalRead(switchH);    if (last != state ) {     lasttm = millis();     last = state;     triggered = true;   }    if ((millis() - lasttm) > 100 && triggered && last==_on) {     digitalWrite(HILOW, digitalRead(HILOW) == LOW ? HIGH : LOW);     triggered = false;   } }

Turn Signal Timers

From the main loop it will call a simple function taking the current millisecond time stamp and switching back and forth the ison variable, the calling code just wants to know what state the blinker should be in, even if the turn signal is not currently running.

int isBlinkTime(unsigned long curr){   static unsigned long fut_time;   static int ison;    if (curr > fut_time) {     ison = !ison;     fut_time = curr+_blinktime;   }   return(ison ? _off : _on); }

If you have ever ridding a Motorcycle on the road or been behind one, you know that some times we forget to turn off those pesky blinkers, sometimes for miles and miles, so this next function will check for a 3 minute timeout and weather to start a turnsignal sequence.

The L & R (left & right) are gotten from asking the momentary isLeftTurn and isRightTurn functions from the main loop. If the blinker exceeds the fut_time time stamp it will set currTS to none and cancel the signal.

If either the left or right was pressed and we are not in sequence then start a new one with the next timeout. pressing either button in sequence also cancels the sequence.

void CheckTurnSignals(unsigned long curr, bool L, bool R){   static unsigned long fut_time;    if (curr >= fut_time) {     currTS = none;   }    if (L || R) {     if (currTS == none) {       //the turn signal needs to start       //and run for a max of 3 minutes       fut_time = curr + _timeout;       currTS = L ? left : right;     } else {       //the turn signal is being canceled, because of a       //secondary push to either button       currTS = none;     }   } }
Getting the driver’s attention behind you 
Flash them breaks obnoxiously, at least 10 times to clue them in that you are stopping, and then go solid break. this is another timer/ counter function to determine the state of the break lights. The function gets the current millisecond timestamp, if the breaks are pressed and a pointer to a on/off value for flashing, and return is we are still in flash state.
bool isBreakFlashTime(unsigned long curr, bool B,int *state){   static unsigned long fut_time;   static int flshcntr;   static int flserstate;        if (curr >=fut_time){     flserstate = !flserstate;     if(B && flserstate)flshcntr++;     fut_time=curr+_flshSleapTime;     }     *state = flserstate;     if (!B)     flshcntr = 0; //no break, reset the counter   else if(flshcntr <= _flshCntrMax)     return true; //has break and still has flashes    return false; //fall though, not in flash }

Multiuse break light

the break light is split up into 4 sections: outer ring, inner ring, left half, right half (Rear Left Turn signal, Rear Left Break, Rear Right Break and Rear Right Turn signal) so with the 4 sections i can:

  • solid full break (all lights on)
  • marker lights (outer ring on)
  • pulsing break (inner and outer flash back and forth)
  • Animated (more on this later)
  • left turn (RLTS flashing,  RRTS solid)
  • left turn with break (RLTS flashing, RRB & RRTS on solid)
  • right turn (RRTS flashing, RLTS solid)
  • right turn with break (RRTS flashing, RLB & RLTS on solid)

The animated turn signal “walks” the lights left or right across all 4 sets (unless the break is pressed), it’s kinda showy but it gets attention.

byte updateRearTurnBits(unsigned long curr){   static unsigned long fut_time;   static byte scrcnt;   static byte leftscroll;   static byte rightscroll;   if(curr>=fut_time){     fut_time = curr+_scrollTime;     scrcnt =++scrcnt % 4;     leftscroll = (leftscroll<<1) | (!scrcnt & 1);     rightscroll = (rightscroll >>1) | (!scrcnt<<7);   }   if(currTS == left)     return leftscroll;//scroll from right to left   else     return rightscroll; //scroll from left to right }

the code updates two bytes, one that travels left and one that travels right. each time the fut_time exceeds it updates the line:

scrcnt =++scrcnt % 4;

That basically resets the count to 0 on the forth count. I use this as a marker to flip the bits !scrcnt and if the bit I’m looking for (0x1 for left or 0x80 and pushing it into each sequence to make it the bytes scroll. then return the left scroll or right scroll depending on the current turn signal state.

The Loop

The main loop is probably the simplest of all the code:

void loop() {   //====reset the watchdog on each loop===   wdt_reset();    //=====Get the statuses=====   bool B = isBreak();   bool L = isLeftTurn();   bool R = isRightTurn();   setHiLowBeam();     //====update the timers====   unsigned long curr = millis();   int blnk = isBlinkTime(curr);   int rearflashstate;   bool brkflsh = isBreakFlashTime(curr,B,&rearflashstate);   byte rtb = updateRearTurnBits(curr);   CheckTurnSignals(curr,L,R);   //update all lights depending on the current state.  }

The top chunk will reset the watchdog timer and get the state of the inputs. the second part will get the current millisecond time and figure out the rest of the current state. I’m not going to bother showing the digital outputs triggering as it’s very basic, but if you want to see the rest download the code and try it out.

Points of Interest

One of the main reasons I wanted to share this is the hidden gotchas that happen when adding a mini computer to an old electronically noisy bike, where everything from the coils to the points make a lot of EM interference:

  • Optically isolate all your inputs and outputs if they are leaving the safety of the project box
  • Project box should be metal and grounded to the chassis.
  • De-bouncing inputs is critical when in the live world, so many false positives happen when just driving down the road.
  • Don’t rely on an Arduino voltage regulator alone, they are good, but not hefty enough to work with a lot of external circuits. Put a pre-regulator 7805 that can really handle the crazy mod swings of a motorcycle charging system.
  • Always use a watchdog timer. you might conceder that your code might be perfect, but outside forces like bad EM can lock up the little computer. The watchdog will at least reboot the computer and get it back up an running so the running lights will still work.

The watchdog timer code I changed a little came from https://forum.arduino.cc/index.php?topic=63651.0 by za_nic for credit.

If I’m happy with this version of the hardware, it will be reworked one more time to exclude the Arduino board and use a single ATmega 168A-PU chip as the next challenge, because I just bought a bunch and now they need a good use.

If anyone is interested I can also add the parts list purchased from Digi-key and Amazon. And will will try to get a detailed electrical diagram posted in PDF form as soon as I have time. Let me know what you think.

History

1.0 init