Arduino seems to brick itself after time

So i am putting together a project for my motorbike that will incorporate turn signals, a tachometer, and a speedometer. I do not believe my problem lies in the code but i will post it along with the schematic for the circuit. I am using an Arduino Mega 2560 for this project.

The turn signals are run with just a basic 5v relay and the power to activate the turn signals is taken from the motorcycle as it is switched.

The speedometer is just a reed switch with a pull down resistor to ground and the voltage is from the 5v pin of the Arduino.

The tachometer signal is taken from my electronic ignition off of the transistor (goes from 0v to +5v) that triggers the grounding of the coil so it can arc.

The display is a 128x64 OLED display i got from adafruit. it uses the sda and scl pins, ive made sure the correct drivers were installed to run the display and i'm fairly sure that it works.

I've used a frequency spectrum analyzer to see what kind of noise the motorcycle/ignition coil creates and it ranges from 500KHz up to 2GHz. I made a pretty basic low pass filter as shown in the schematic to filter out anything above 500KHz in hopes that would solve my issue but it did not.

Now to my issue, when i run the board from a regulated clean power supply, or from my computer usb, or from the barrel jack, the program runs fine. The OLED display updates and i can swap through the modes etc. But when i start the bike and use the bikes power to power the Arduino it will only work for maybe 15-30 seconds before either the relays stop switching, or the display stops updating and i'll have to turn it off and back on for it to work for another 15-30 seconds.

I'm at my wits end as to what the problem could be, and appreciate any help you guys can offer. Thanks.

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 4
Adafruit_SSD1306 display1(OLED_RESET);

#define XPOS 0
#define YPOS 1
#define DELTAY 2


const int hallPin=2;
const unsigned long sampleTime=1000;
const int maxRPM = 10200; 

int counter = 1;
int buttonPin = 3;

const int RightSwitch = 5;
const int RightBlink = 6;
const int LeftSwitch = 4;  
const int LeftBlink = 7;

long RightSwitchMillis = 0;                             //Time stamp for when the switch last went to 1(HIGH). Initially set at 0(LOW)
int RightSwitchVal = 0;                                 //Monitors the value of the Right Blinker switch, whether 1 or 0 (HIGH or LOW). Initially set at 0(LOW)
int previousRightSwitchVal = 0;                         //Monitors the value of the Right Blinker switch from last time it was checked. Initally set at 0(LOW)
int RightBlinkState = LOW;                              //State of the Right Blinker Light. Initally set to LOW(Off)
long RightFlashMillis = 0;                              //Time stamp for when rapid flash last went to 1(LOW), initially set at O(LOW)

long LeftSwitchMillis = 0;
int LeftSwitchVal = 0;
int previousLeftSwitchVal = 0;
int LeftBlinkState = LOW;
long LeftFlashMillis = 0;

long interval = 1000;                                   // interval at which to blink (milliseconds)

#define reed A0
float radius = 13.6;                                    // tire radius (in inches)- CHANGE THIS FOR YOUR OWN BIKE
int reedVal;
long timer = 0;
float mph = 0.00;
float distance=0.00;
float circumference;
boolean backlight;
int maxReedCounter = 70;                                //fuck with this to test out max mph, was originally 100.
int reedCounter;
s
void setup()
{
  display1.begin(SSD1306_SWITCHCAPVCC, 0x3C);          // display 1 op adres 0x3C
  display1.clearDisplay();
  display1.setTextColor(WHITE);
  
  pinMode(buttonPin, INPUT);
  pinMode(RightBlink, OUTPUT);
  pinMode(RightSwitch, INPUT);
 
  pinMode(LeftBlink, OUTPUT);
  pinMode(LeftSwitch, INPUT);

  pinMode(hallPin,INPUT);
  
  reedCounter = maxReedCounter;
  circumference = 2*3.14*radius;
  pinMode(reed, INPUT);

  cli();
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1  = 0;
  OCR1A = 1999;                                         // = (1/1000) / ((1/(16*10^6))*8) - 1  
  TCCR1B |= (1 << WGM12); 
  TCCR1B |= (1 << CS11);    
  TIMSK1 |= (1 << OCIE1A);
  sei();//allow interrupts
  //END TIMER SETUP

  Serial.begin(9600);
} 

ISR(TIMER1_COMPA_vect) 
{                                                     //Interrupt at freq of 1kHz to measure reed switch
  reedVal = digitalRead(reed);                        //get val of A0
  if (reedVal)
    {                                                 //if reed switch is closed
    if (reedCounter == 0)
    {                                                 //min time between pulses has passed
      mph = (56.8*float(circumference))/float(timer); //calculate miles per hour
      timer = 0;                                      //reset timer
      reedCounter = maxReedCounter;                   //reset reedCounter
    }
    else
    {
      if (reedCounter > 0)
      {                                               //don't let reedCounter go negative
        reedCounter -= 1;                             //decrement reedCounter
      }
    }
  }
  else
  {                                                   //if reed switch is open
    if (reedCounter > 0)
    {                                                 //don't let reedCounter go negative
      reedCounter -= 1;                               //decrement reedCounter
    }
  }
  if (timer > 2000)
  {
    mph = 0;                                          //if no new pulses from reed switch- tire is still, set mph to 0
  }
  else
  {
    timer += 1;                                       //increment timer
  } 
  if(digitalRead(2)==LOW)
  {
    distance +=mph;
  }
 else
  {
    distance=0;
  }
}


void loop()
{
 //Handle input
 int currentState = digitalRead(buttonPin);
 if(currentState == 1)
 {
   //Reset count if over max mode number
   counter ++;
   delay(250);
   if(counter == 4)
   {
     counter = 1;
   }
 }
 if(counter == 1)
 {
   unsigned long currentMillis = millis();              //initiallizes the clock
 
                                                      //Right Blinker Code
 RightSwitchVal = digitalRead(RightSwitch);           //read and store Right Blinker switch status
 
 if ((RightSwitchVal == HIGH) && (previousRightSwitchVal == LOW)) //compares current switch status to previous status
   {
    previousRightSwitchVal = RightSwitchVal;          //sets the current status as the old status in preparation for the next check
    RightSwitchMillis = currentMillis;                //sets a time stamp at the current time
   }

   if((RightSwitchVal == HIGH) && (currentMillis - RightFlashMillis > interval/4)) //if the Right Blink switch is HIGH but less than half a second has elapsed,                                                                                                                                
        {                                                                          // this checks if the Right Blink switch is HIGH and more than one-fifteinth
       RightFlashMillis = currentMillis; // saves current time                     //of a second has elapsed since the last time previousMillis10 was reset.
       
        if (RightBlinkState == LOW)
           RightBlinkState = HIGH;
         else
           RightBlinkState = LOW;                    //if more than one-fiftieth of a second has elapsed since the time stamp was reset, reverse the state of the Right Blinker.
           
         digitalWrite(RightBlink, RightBlinkState);  // set the Right Blinker with the state of the variable:
        }
   
         
   if (RightSwitchVal == LOW)                        //checks if the Blinker switch is still on or not. If it is off, the following occurs...
        {
          previousRightSwitchVal = RightSwitchVal;   //makes sure the previousBrakeSwitch value is reset to off when the switch is in fact off.
          RightBlinkState = HIGH;                    //resets the blinker
          digitalWrite(RightBlink, RightBlinkState); //Overall, if the Right Blinker switch is off, keep the Right Blinker off.
        }
       
                                                     //Left Blinker Code
 LeftSwitchVal = digitalRead(LeftSwitch);
 
 if ((LeftSwitchVal == HIGH) && (previousLeftSwitchVal == LOW))
   {
    previousLeftSwitchVal = LeftSwitchVal;
    LeftSwitchMillis = currentMillis;
   }


   if((LeftSwitchVal == HIGH) && (currentMillis - LeftFlashMillis > interval/4))                                                                                                                                
        {
       LeftFlashMillis = currentMillis;
       
        if (LeftBlinkState == LOW)
           LeftBlinkState = HIGH;
         else
           LeftBlinkState = LOW;
           
         digitalWrite(LeftBlink, LeftBlinkState);
        }
   
         
   if (LeftSwitchVal == LOW)
        {
          previousLeftSwitchVal = LeftSwitchVal;
          LeftBlinkState = HIGH;
          digitalWrite(LeftBlink, LeftBlinkState);
        }
  
  //print MPH once a second
  display1.clearDisplay();
  display1.setTextSize(2);
  display1.setCursor(0,0);
  display1.print ("MPH");
  display1.setCursor(0,25);
  display1.setTextSize(5);
  display1.print(mph);
  display1.display();
 }

 if(counter == 2)
 {
  int rpm=getRPM();
  display1.clearDisplay();
  display1.setTextSize(2);
  display1.setCursor(0,0);
  display1.print ("RPM");
  display1.setCursor(0,25);
  display1.setTextSize(5);
  display1.print(rpm,DEC); //PUT RPM VARIABLE TO BE DISPLAYED
  display1.display();
 }
}


int getRPM()
{
  // sample for sampleTime in millisecs
  int kount=0;
  boolean kflag=LOW;
  unsigned long currentTime=0;
  unsigned long startTime=millis();
  while (currentTime<=sampleTime)
  {
    if (digitalRead(hallPin)==HIGH)
    {
      kflag=HIGH;
    }
    if (digitalRead(hallPin)==LOW && kflag==HIGH)
    {
      kount++;
      kflag=LOW;
    }
    currentTime=millis()-startTime;
  }
  int kount2rpm = int(60000./float(sampleTime))*kount;
  return kount2rpm;
}

A couple of things come to mind here - the first is typically, you don't want to be driving relays direct from the Arduino outputs (and the coils should have a diode across them to help control the kick you get when you remove power from the coil). I would also put some signal conditioning on the input from the reed switch - at least a 10k resistor in series on the board and a small cap - wires running around vehicle electrical systems are exposed to all sorts of transients that can cause bad things to happen. A good design would condition all external inputs to the board for protection. You may also have an issue with how the grounds are connected - if the ground gets pulled up relative to some input, you can have issues - ground loops are typically a big issue in things that are connected together over some distance. I do like the 100 ohm resistor in series with the input to the regulator - that helps isolate noise on the 12v bus from the board although it would be good to make sure you are not pulling it too far down with the lights when they are on (your schematic shows the lights being powered AFTER the 100 ohm resistor. I would put the light power before the 100 ohm resistor straight off the 12v supply.

How much current do the relays need to energize? More than 4.2V and 20mA, you should switch to controlling a transistor to sink current thru the coil.

How much current is going thru the lights? Vin is after the barrel jack and the reverse polarity protection diode which is only rated for 1A.

"eci", how much current does that need?

CrossRoads:
How much current do the relays need to energize? More than 4.2V and 20mA, you should switch to controlling a transistor to sink current thru the coil.

How much current is going thru the lights? Vin is after the barrel jack and the reverse polarity protection diode which is only rated for 1A.

"eci", how much current does that need?

The turn signal lights are LEDs and at max draw around .1 or .2 amps max.

and i'm not sure about the relays. i would need to look at the package when i get home from work.

The ECI (electronic controlled ignition, sorry for not saying what that was) is a very low current/voltage system (5v max and around 5mA worth of current)

If the turn signal lights are LED's, I would not be bothering with a relay and switch them with a transistor (well, two actually one for level conversion and one on the high side switch) although there are some small solid state relays that could work well too - not dealing with the coils of a traditional relay is a plus.

Would the charging of that relays coil cause that much interference with the board? why is it then that board and circuit act fine with a power supply hooked up, but not when the motorcycles power is hooked up to it?

Would the charging of that relays coil cause that much interference with the board?

Yes, the "inductive kick" can completely destroy an Arduino. It is somewhat random, but really, just a matter of time.

Alright, ill get some transistors from the electronic shop at school and make up a switch and take those relays off then. because i bought a case for the mega to keep it inside of and i made my circuit with a pcb board which is mounted ontop of the case for the mega. So if the coil across that relay can cause interference enough to disrupt the board i can see it since its mounted right ontop of it.

Would taking the part of the code that powers the relays coil on and off do the same thing as just taking it out? If that's the case i've just done that and the problem still seems to persist with it "freezing up" after about 15 seconds.

I've used a frequency spectrum analyzer to see what kind of noise the motorcycle/ignition coil creates and it ranges from 500KHz up to 2GHz.

What?

Yeah, it's like that... :wink:

I don't think it's the problem, but it certainly isn't a good idea to do floating-point math inside the ISR. Just save the timer value, set a flag, and catch it in the main loop. Perform the calculation there.

Cheers,
/dev

So I have replaced the 2 5v relays with 2 tip 120’s and wired them up so that it just performs the grounding of the light while voltage is constantly supplied to it.

I start the bike up turn the arduino on and the same thing happens, it will calculate the mph or calculate the rpms or blink the lights and it just freezes after x amount of time.

Can you post a complete diagram, especially the power part.
It seems you’re powering a 5volt relay and display from the Mega’s 5volt rail, and are using a 100ohm resistor between 12volt and Vin. Did you calculate the volt-drop of that resistor.
Leo.

You need a lot of smoothing on the power supply. Typically a motorcycle or car kicks out a very dirty supply. I would use at least a tank circuit before the input jack, it is the last circuit in this page.
http://www.thebox.myzen.co.uk/Tutorial/De-coupling.html

I would also add some decoupling caps arround the circuit like the LCD .

You need to use a much smaller pull down resistor, try a 1K.

I would also put some ferrite inductors in line with the input switches.

Getting it interference free is hard.

Note that the data sheet says the processor should not be used for any automotive products.

I've not anything to say about the issue you're experiencing but I did raise an eyebrow about using that reed switch to detect RPM.

Apart from not letting AO float free when the reed switch is open (put a 100K resistor to +5v on it), the actual reed switch is quite slow [it could still be open by the time the next revolution came round] and mechanical (unless you have an optical one) so prone to fatigue and eventual failure. Not to mention switch bounce.

I would certainly replace that part of the circuit with a hall-effect transistor and a magnet - no moving parts, no bounce etc. Reed switches are fine for burglar alarms (door close switches) but I certainly would not use them on a motorcycle like this.

He is using the reed switch on the wheel for speed, not RPM of the engine. Not sure how fast a reed switch is (certainly slower than Hall effect etc.), but I would think it should be OK for a wheel - especially if it is in close to the hub so the exposure time to the magnet is longer than if it is out near the rim.

I'm not so sure. Check my maths:

Wheel radius 30cm (small-ish wheel, 60cm diameter [about 2ft] just for demo purposes)

Circumference = 188.5cm (2 pi r) or 1.8m travelled in one revolution.

So travelling at 80km/hour in 1 minute he would travel 1.33km, or 1330m.

So that's 1,330m / 1.8m = 738 wheel revolutions / min

Or 738 / 60 secs = roughly 12 revolutions a second.

Even if the wheel was 60cm radius (seems unlikely unless he's driving a monster bike) that still means about 6 revolutions per second. And hence 6 reed switch clicks a second.

Can a mechanical reed switch take this much punishment? And what about switch bounce? Just seems a brave choice but if my maths is wrong then maybe it works after all! Feel free to correct my dodgy maths!

Your math looks right. It is fast for a reed switch I agree (I am used to big bikes that have slower wheel speeds, but that is still fast and I agree that a hall effect with a magnet would be a better choice.)

Just FYI, to "brick" a device means to render it unbootable. It doesn't seem that you have done that.

Small update, I replaced the relays with 2 tip 120's and I am grounding the turn signals out as oppose to switching voltage with the relays. I also seemed to have gotten the wrong size capacitors from the shop at school and they gave me 100nf instead of 1nf so i corrected my low-pass filter. I also took all the wires out of the loom and made a twisted pair and wrapped the ground wire around all of them and put it back inside.

From the 5 minutes I had the motorcycle running before I had to leave for class I noticed that it stopped freezing like I had described earlier but now it seems to reset itself almost and it would go back to my MPH without hitting the button from the RPM portion of the code.

I'm thinking that maybe I need to drop my low-pass filter lower than 500kHz or could the power the alternator is creating possibly be dropping out for a brief moment and then resetting the board?