Click here to Skip to main content
15,880,469 members
Articles / Internet of Things / Arduino

Motorcycle Signal Lights

Rate me:
Please Sign up or sign in to vote.
4.98/5 (23 votes)
30 Jun 2018CPOL7 min read 32.5K   450   8   23
Using an Arduino Nano to control all the working lights on an old 1978 Suzuki GS500

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 brake lights, but this one I wanted to be different. You can see the build here if you are 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.

Image 1

The above drawing ended up looking like the following below:

Image 2

Image 3

Image 4

Image 5

Here a snippit of the full electrical (there is a full version of it in the zip file as a PDF and PNG file)

Image 6

Using the Code

First off button de-bouncing! There are actually 2 versions of the button de-bouncing done: one for the brake switches and the other for turn signals and high/low beam selector. All of the inputs are set HIGH, by setting this pin to ground (through the frame chassis), this tells the code that the brake 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 threshold, we know this is likely a solid state. if the state now also matches _on (LOW) then the brake lights should come on.

C++
bool isBrake() {
  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 brake 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 brake 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.

C++
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.

C++
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.

C++
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 ridden a Motorcycle on the road or been behind one, you know that sometimes, 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.

C++
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 cancelled, because of a
      //secondary push to either button
      currTS = none;
    }
  }
}

Getting the Driver's Attention Behind You

Flash them brakes obnoxiously, at least 10 times to clue them in that you are stopping, and then go solid brake. This is another timer/ counter function to determine the state of the brake lights. The function gets the current millisecond timestamp, if the brakes are pressed and a pointer to a on/off value for flashing, and return is we are still in flash state.

C++
bool isBrakeFlashTime(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 brake, reset the counter
  else if(flshcntr <= _flshCntrMax)
    return true; //has brake and still has flashes

  return false; //fall though, not in flash
}

Multiuse Brake Light

The brake light is split up into 4 sections: outer ring, inner ring, left half, right half (Rear Left Turn signal, Rear Left Brake, Rear Right Brake and Rear Right Turn signal), so with the 4 sections I can:

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

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

C++
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:

C++
scrcnt =++scrcnt % 4;

That basically resets the count to 0 on the fourth 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:

C++
void loop() {
  //====reset the watchdog on each loop===
  wdt_reset();

  //=====Get the statuses=====
  bool B = isBrake();
  bool L = isLeftTurn();
  bool R = isRightTurn();
  setHiLowBeam();
 
  //====update the timers====
  unsigned long curr = millis();
  int blnk = isBlinkTime(curr);
  int rearflashstate;
  bool brkflsh = isBrakeFlashTime(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 concede 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. A text file is now included in the zip file with all the parts purchased from Digi-Key and Amazon, (although some of it is stock for my workshop).  I will try to get a detailed electrical diagram posted in PDF form as soon as I have time. Let me know what you think There is now a full electrical diagram included in the zip file as a PDF and PNG file.

History

  • 1.0: Initial post
  • 1.1: fixed spelling and added parts text file to Bike_lights_v3b.zip file
  • 1.2: added filnal electrical diagram to zip file and includded a snippit of it in the text

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Van Doren Sales
United States United States
I've been a professional developer since the late 90's and got hired on with Doubl-Kold in may of 2000 through February of 2018. I have since moved on to Van Doren Sales for new challenges. I have a few core languages under my belt, but D-lang has gotten my interest lately.

I currently have interest into more embedded systems, as they continue to fascinate me more than any other technologies currently.

The majority of my free time is spent on my wife and kids, and numerous other hobbies.

Comments and Discussions

 
QuestionModify for positive ground? Pin
Member 1602302720-Aug-23 18:44
Member 1602302720-Aug-23 18:44 
QuestionApply this project to Model A Ford or similar old car with sketchy electrical system? Pin
Member 1529491221-Jul-21 5:26
Member 1529491221-Jul-21 5:26 
AnswerRe: Apply this project to Model A Ford or similar old car with sketchy electrical system? Pin
Matt McGuire22-Jul-21 14:34
professionalMatt McGuire22-Jul-21 14:34 
GeneralRe: Apply this project to Model A Ford or similar old car with sketchy electrical system? Pin
Member 1529491223-Jul-21 11:34
Member 1529491223-Jul-21 11:34 
GeneralRe: Apply this project to Model A Ford or similar old car with sketchy electrical system? Pin
Matt McGuire24-Jul-21 9:09
professionalMatt McGuire24-Jul-21 9:09 
GeneralMessage Closed Pin
24-Jul-21 10:24
Member 1529491224-Jul-21 10:24 
QuestionWarning ? Pin
Sly6665127-Dec-19 1:56
Sly6665127-Dec-19 1:56 
AnswerRe: Warning ? Pin
Sly6665131-Dec-19 2:47
Sly6665131-Dec-19 2:47 
QuestionDon't cancel at stop lights Pin
Member 880237229-Jun-18 9:56
Member 880237229-Jun-18 9:56 
AnswerRe: Don't cancel at stop lights Pin
Matt McGuire29-Jun-18 12:31
professionalMatt McGuire29-Jun-18 12:31 
QuestionQuestion regarding the programming Pin
Nick__J27-Jun-18 20:45
Nick__J27-Jun-18 20:45 
AnswerRe: Question regarding the programming Pin
Matt McGuire29-Jun-18 12:43
professionalMatt McGuire29-Jun-18 12:43 
QuestionGreat article Pin
KLPounds27-Jun-18 12:41
KLPounds27-Jun-18 12:41 
AnswerRe: Great article Pin
Matt McGuire27-Jun-18 17:15
professionalMatt McGuire27-Jun-18 17:15 
GeneralMy vote of 5 Pin
Member 1069926127-Jun-18 10:59
Member 1069926127-Jun-18 10:59 
GeneralRe: My vote of 5 Pin
Matt McGuire27-Jun-18 17:29
professionalMatt McGuire27-Jun-18 17:29 
QuestionSpelling Pin
David Radcliffe27-Jun-18 3:29
David Radcliffe27-Jun-18 3:29 
AnswerRe: Spelling Pin
Matt McGuire27-Jun-18 6:31
professionalMatt McGuire27-Jun-18 6:31 
GeneralMotorcycle Pin
dan!sh 26-Jun-18 20:31
professional dan!sh 26-Jun-18 20:31 
GeneralRe: Motorcycle Pin
Matt McGuire27-Jun-18 6:30
professionalMatt McGuire27-Jun-18 6:30 
GeneralMy vote of 5 Pin
raddevus26-Jun-18 2:28
mvaraddevus26-Jun-18 2:28 
GeneralRe: My vote of 5 Pin
Matt McGuire27-Jun-18 6:24
professionalMatt McGuire27-Jun-18 6:24 
GeneralRe: My vote of 5 Pin
raddevus27-Jun-18 7:48
mvaraddevus27-Jun-18 7:48 
GeneralRe: My vote of 5 Pin
Matt McGuire27-Jun-18 18:19
professionalMatt McGuire27-Jun-18 18:19 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.