Go Down

Topic: Arduino or sensor speed problems? (Read 3880 times) previous topic - next topic

graham7

This is my first post on here. I have hit some problems with my first serious project on Arduino and would appreciate some advice.

The device I am working on is to be mounted in a competition vehicle used for sprints and hillclimbs. These consist of a timed run over a course lasting up to 2 minutes. The objective is to get some feedback on performance during the run by dividing the course into 4 and comparing the time for each section to the previous best time. If it is better a green LED will light. The time difference to the best run will also be shown on an LCD.

The hardware consists of strip board Arduino with a green LED connected to pin 7 and a red LED on pin 13. There is also  16X2 LCD. I am using an A1120EUA-T Hall Effect Switch which senses a magnet glued to the wheel rim. This has a 10K pull up resistor connected across the positive and signal pins. It is connected to pin 2 and I am using an interrupt. There is also a button to change the course length on pin 8.

Everything is working fine when I spin the wheel on the car with it jacked up in the garage or when I drive very slowly, so the logic of my sketch would appear to work. When I drive at faster speeds the sector times will often miss being recorded resulting in nonsense results.

The maximum speed of the wheel is about 1800 RPM. It stops working at speeds well below this. Is the problem likely to be in that I have too much code in the sketch for the Arduino to process between interrupts or should I be using a different sensor? Perhaps a reed switch? I have run a test with the code to  light  the LED's commented out but this seems to make no difference.

I hope that I have explained this adequately. Any advice would be much appreciated.

(2nd part of the sketch is in the next post)

Code: [Select]
#include <LiquidCrystal.h>
#include <EEPROM.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 6);
const int inputPin1 = 2; //Interrupt pin with sensor attacehd
const int inputPin2 = 8; //Running button
const int ledPin = 13; //Running LEDS
const int fastPin = 7; //faster light
unsigned long int  starttime; //time when the run starts
unsigned int wheelRevs = 0; //Number of wheel revolutions
boolean Running = false; //boolean to control if wheel revs are being recorded
int courselength = 100;       //length of sourse in meters. Change this for each venue (Gurston)
const float wheelcirc =1.81;        //wheel circumference
int courserevs;                //number of time the wheel turns for the whole run
int sector1;                   //number of times the wheel turns in sector 1
int sector2;                  //number of times the wheel turns in sector 2
int sector3;                  //number of times the wheel turns in sector 3
int Button2Press = 0;         //variable to hold the state of the running button
float  s1time;     //Recorded time for sector 1
float  s2time;     //Recorded time for sector 2
float  s3time;     //Recorded time for sector 3
float  s4time;  //Recorded time total
float s1fastest = 0; //fastest sector times, used to calculate target time
float s2fastest = 0;
float s3fastest = 0;
float s4fastest = 0;
int val = 0;
int bounceCheck = 0;
float delta;  //running time difference
float s1fastestrun = 0; //sector times in fastest run - usd for calculating deltas to compare fastest run to current run
float s2fastestrun = 0;
float s3fastestrun = 0;
float s4fastestrun = 0;
float fastesttotal = 0;
//variables to write to EEPROM
word s1fastestw;
word s2fastestw;
word s3fastestw;
word s4fastestw;
word s1fastestrunw;
word s2fastestrunw;
word s3fastestrunw;
word s4fastestrunw;
word fastesttotalw;
int course = 1;


void setup(){
  pinMode(9, OUTPUT);
  analogWrite(9, 50);
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  pinMode(ledPin, OUTPUT);   //declare LED as output
  pinMode(fastPin, OUTPUT);
  pinMode(inputPin1, INPUT);  //declare button as input
  pinMode(inputPin2, INPUT);  //declare button 2 as input
  Serial.begin(9600);
  digitalWrite(inputPin1,HIGH);
  course = EEPROM.read(1);
  if(digitalRead(inputPin2) == LOW){
    if(course == 3){
      course = 1;
    }
    else{
      course++;
    }
    EEPROM.write(1,course);
    for (int i = 2; i < 512; i++)
      EEPROM.write(i, 0);
  }
  if( course == 1){
    courselength = 100;
    courserevs = int(courselength/wheelcirc);  //Calculate number of wheel revolutions for the course.
    sector1 = .25*courserevs;
    sector2 = .5*courserevs;
    sector3 = .75*courserevs;
    lcd.print("Lavendon");
  }
  if (course == 2){
    courselength = 1435;
    courserevs = int(courselength/wheelcirc);  //Calculate number of wheel revolutions for the course.
    sector1 = 350/wheelcirc;
    sector2 = (350 + 268)/wheelcirc;
    sector3 = (350 + 268 + 347)/wheelcirc;
    lcd.print("Curborough");
  }
  if( course == 3){
    courselength = 3815;
    courserevs = int(courselength/wheelcirc);  //Calculate number of wheel revolutions for the course.
    sector1 = 1262/wheelcirc;
    sector2 = (1262 + 953)/wheelcirc;
    sector3 = (1262 + 953 + 815)/wheelcirc;
    lcd.print("Goodwood");
  }
  lcd.setCursor(0,1);
  lcd.print(courselength);
  lcd.print(" metres");
  delay(3000);
  lcd.clear();
  s1fastestrun = (word(EEPROM.read(2),EEPROM.read(3)));
  s2fastestrun = (word(EEPROM.read(4),EEPROM.read(5)));
  s3fastestrun = (word(EEPROM.read(6),EEPROM.read(7)));
  s4fastestrun = (word(EEPROM.read(8),EEPROM.read(9)));
  s1fastest = (word(EEPROM.read(10),EEPROM.read(11)));
  s2fastest = (word(EEPROM.read(12),EEPROM.read(13)));
  s3fastest = (word(EEPROM.read(14),EEPROM.read(15)));
  s4fastest = (word(EEPROM.read(16),EEPROM.read(17)));
  fastesttotal = (s1fastestrun+s2fastestrun+s3fastestrun+s4fastestrun);
  attachInterrupt(0,buttonPush,RISING); //Interrupt to detect when the sensor is tripped
  lcd.print("setup complete");
  delay(3000);
}
void buttonPush(){ //Interrupt loop called by interrupt
  ++wheelRevs;

}/code]





graham7

Code: [Select]
void loop(){               //This is to set the running status and light the LED

  if (Button2Press == 0){
    val = digitalRead(inputPin2);
    delay(100);
    bounceCheck =  digitalRead(inputPin2);
    if (val == bounceCheck){
      if (val == LOW){
        digitalWrite(ledPin,HIGH);
        lcd.clear();
        lcd.setCursor(0,0);
        lcd.print("READY TO START");
        lcd.setCursor(0,1);
        lcd.print("BEST ");
        lcd.print(fastesttotal/1000);
        Running = true;
        Button2Press = 1;
        wheelRevs = 0;
        delay(1000);
      }
    }
  }


  if (Button2Press == 1){    //This is to reset the running status and turn off the LED
    val = digitalRead(inputPin2);
    delay(100);
    bounceCheck =  digitalRead(inputPin2);
    if (val == bounceCheck){
      if (val == LOW){
        digitalWrite(ledPin,LOW);
        digitalWrite(fastPin,LOW);
        lcd.clear();
        lcd.print("LAST ");
        lcd.print(s1time/1000);
        lcd.setCursor(11,0);
        lcd.print(s2time/1000);
        lcd.setCursor(5,1);
        lcd.print(s3time/1000);
        lcd.setCursor(11,1);
        lcd.print(s4time/1000);
        Running = false;
        Button2Press = 2;
        delay(1000);
      }     
    }
  }
  if (Button2Press == 3){
    val = digitalRead(inputPin2);
    delay(100);
    bounceCheck =  digitalRead(inputPin2);
    if (val == bounceCheck){
      if (val == LOW){   
        lcd.clear();
        lcd.print("LAST ");
        lcd.print((s1time+s2time+s3time+s4time)/1000);
        lcd.setCursor(0,1);
        lcd.print("TARGET ");
        lcd.print((s1fastest+s2fastest+s3fastest+s4fastest)/1000);
        Button2Press = 0;
        delay(1000);
      }
    }
  }
  if (Button2Press == 2){    //This is to reset the running status and turn off the LED
    val = digitalRead(inputPin2);
    delay(100);
    bounceCheck =  digitalRead(inputPin2);
    if (val == bounceCheck){
      if (val == LOW){
        lcd.clear();
        lcd.print("BEST ");
        lcd.print(s1fastest/1000);
        lcd.setCursor(11,0);
        lcd.print(s2fastest/1000);
        lcd.setCursor(5,1);
        lcd.print(s3fastest/1000);
        lcd.setCursor(11,1);
        lcd.print(s4fastest/1000);     
        Button2Press = 3;
        delay(1000);
      }     
    }
  }   


  if (Running){
    if (wheelRevs == 1) {
      starttime = millis();  //Sets the clock running the first time the sensor is tripped
      // time = 0;
      digitalWrite(ledPin,LOW);
      lcd.clear();
      lcd.print("RUNNING");
    }

    if (wheelRevs == sector1){
      s1time = millis() - starttime;
      if (s1fastestrun > 1){
        lcd.clear();
        delta = (s1time - s1fastestrun)/1000;
        if (delta > 0){
          lcd.print("+");
        }
        lcd.print(delta);
      }
      if (s1fastest == 0){
        s1fastest = s1time;
      }
      if (s1time < s1fastest){
        s1fastest = s1time;
        digitalWrite(fastPin,HIGH);
      }
      if (s1time > s1fastest){
        digitalWrite(ledPin,HIGH);
      }
    }

    if (wheelRevs == sector2){
      s2time = millis() - starttime - s1time;
      if (s2fastestrun > 1){
        lcd.clear();
        delta = (s2time + s1time - s1fastestrun - s2fastestrun)/1000;
        if (delta > 0){
          lcd.print("+");
        }
        lcd.print(delta);
      }
      if (s2fastest == 0){
        s2fastest = s2time;
      }

      if (s2time < s2fastest){
        s2fastest = s2time;
        digitalWrite(fastPin,HIGH);
        digitalWrite(ledPin,LOW);
      }
      if (s2time > s2fastest){
        digitalWrite(ledPin,HIGH);
        digitalWrite(fastPin,LOW);
      }
    }
    if (wheelRevs == sector3){
      s3time = millis() - starttime - s2time - s1time;

      if (s3fastestrun > 1){
        lcd.clear();
        delta = (s1time + s2time + s3time - s1fastestrun - s2fastestrun - s3fastestrun)/1000;
        if (delta > 0){
          lcd.print("+");
        }
        lcd.print(delta);
      }
      if (s3fastest == 0){
        s3fastest = s3time;
      }



      if (s3time < s3fastest){
        s3fastest = s3time;
        digitalWrite(fastPin,HIGH);
        digitalWrite(ledPin,LOW);
      }
      if (s3time > s3fastest){
        digitalWrite(ledPin,HIGH);
        digitalWrite(fastPin,LOW);
      }
    }
    if (wheelRevs == courserevs){
      s4time = millis() -starttime - s3time - s2time - s1time;

      if (s4fastestrun > 1){
        lcd.clear();
        delta = (s4time + s3time + s2time + s1time - s1fastestrun - s2fastestrun - s3fastestrun - s4fastestrun)/1000;
        if (delta > 0){
          lcd.print("+");
        }
        lcd.print(delta);
      }
      if (s4fastest == 0){
        s4fastest = s4time;
      }
      digitalWrite(fastPin,LOW);
      if (s4time < s4fastest){
        s4fastest = s4time;
        digitalWrite(fastPin,HIGH);
      }

      Running = false;      //Automatically turn off at the finish
      delay(2000);
      digitalWrite(ledPin,HIGH);
      delay(5000);
      digitalWrite(fastPin,LOW);
      digitalWrite(ledPin,LOW);
      lcd.clear();
      lcd.print("Total=");
      lcd.print((s1time+s2time+s3time+s4time)/1000);
      lcd.setCursor(0,1);
      lcd.print("Delta=");
      lcd.print(delta);
      if (delta <= 0){
        s1fastestrun = s1time;
        s2fastestrun = s2time;
        s3fastestrun = s3time;
        s4fastestrun = s4time;
        fastesttotal = s1time+s2time+s3time+s4time;
      }
      s1fastestw = word(s1fastest);
      s2fastestw = word(s2fastest);
      s3fastestw = word(s3fastest);
      s4fastestw = word(s4fastest);
      s1fastestrunw = word(s1fastestrun);
      s2fastestrunw = word(s2fastestrun);
      s3fastestrunw = word(s3fastestrun);
      s4fastestrunw = word(s4fastestrun);
      EEPROM.write(2,highByte(s1fastestrunw));
      EEPROM.write(3,lowByte(s1fastestrunw));
      EEPROM.write(4,highByte(s2fastestrunw));
      EEPROM.write(5,lowByte(s2fastestrunw));
      EEPROM.write(6,highByte(s3fastestrunw));
      EEPROM.write(7,lowByte(s3fastestrunw));
      EEPROM.write(8,highByte(s4fastestrunw));
      EEPROM.write(9,lowByte(s4fastestrunw));
      EEPROM.write(10,highByte(s1fastestw));
      EEPROM.write(11,lowByte(s1fastestw));
      EEPROM.write(12,highByte(s2fastestw));
      EEPROM.write(13,lowByte(s2fastestw));
      EEPROM.write(14,highByte(s3fastestw));
      EEPROM.write(15,lowByte(s3fastestw));
      EEPROM.write(16,highByte(s4fastestw));
      EEPROM.write(17,lowByte(s4fastestw));
    }
  }
}
/code]

AWOL

#2
Apr 16, 2012, 04:49 pm Last Edit: Apr 16, 2012, 04:53 pm by AWOL Reason: 1
Quote
if (wheelRevs == sector2){

And if you miss just one count?

(wheelRevs ought to be declared "volatile")

When I see stuff like this:
Code: [Select]
float s1fastestrun = 0; //sector times in fastest run - usd for calculating deltas to compare fastest run to current run
float s2fastestrun = 0;
float s3fastestrun = 0;
float s4fastestrun = 0;

I start to think "arrays".
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.
I speak for myself, not Arduino.

graham7

#3
Apr 16, 2012, 04:56 pm Last Edit: Apr 16, 2012, 04:58 pm by graham7 Reason: 1
Thanks for the quick reply. In most of my tests I have wheelrevs as "volatile". Unfortunately I have posted the one test sketch where it is not!

If one count is missed, that is if wheelrevs never equals sector 2, I will get a zero time for that sector. That seems to be what is happening.

Yes I  agree about arrays. Would that make the sketch run faster?

AWOL

"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.
I speak for myself, not Arduino.

graham7

I guess if I test >= it will record the time at every wheel rev after sector1. I only want the time exactly at sector1, sector2 etc.

sdturner

Are you missing pulses from the wheel sensor? or are you not moving from one sector to the next? If you are missing pulses you probably have a mechanical problem. At speed things are moving or vibrating.

I definitely agree that you should use if (wheelrevs >= xxx). If you get 2 pulses in the time it takes the Arduino to loop around you will miss the transition from one sector to another. 1800 RPM is 30 Hz, which means 33 mS between pulses. Arduino's are not very fast. You could very easily miss a pulse and the fact that it works at low speed, but not at high speed would support that theory.

You can correct for the extra pulses by keeping a running average of pulse time and subtracting out the extra pulses to get closer to your actual sector time.

Steve Turner

P.S. Should you be looking at LEDs and display while you are racing?

DuaneB

Hi,
   Arduino is not slow, 1800 rpm is only 30 events per second, which means that your Arduino can perform roughly 500,000 operations between revolutions this is several hundred times more than you need.

Its nice to see that you have a motor sports application, its close to my own interest. I don't know that you are taking the best approach.

Have a look at my RC/Kart lap timer, its uses infra red to time laps, you can create infra red beacons for about 2 dollars that you could position on the track for taking split times automatically.

Its small enough to strap to a steering wheel of a kart, but as I have found that I cannot always look at the wheel the most useful feature is a very loud buzzer. One buzz to confirm a lap/split has been captured and two pips if its a new best, it makes a huge difference to my RC driving, I cannot believe how much harder I work on a lap to get those two pips.

I am working on the project right now and plan to finish it within a few days at which point I will write it up in full, in the meantime check out the relevant blog posts here - 

http://rcarduino.blogspot.co.uk/2012/02/rc-lap-timer-go-kart-lap-timer-part-1.html

http://rcarduino.blogspot.co.uk/2012/02/rc-lap-timer-go-kart-lap-timer-part-2.html

http://rcarduino.blogspot.co.uk/2012/03/punk-consoles-and-ir-transmitters-555.html

Duane B

rcarduino.blogspot.com
Read this
http://rcarduino.blogspot.com/2012/04/servo-problems-with-arduino-part-1.html
then watch this
http://rcarduino.blogspot.com/2012/04/servo-problems-part-2-demonstration.html

Rcarduino.blogspot.com

graham7

Steve and Duane

Thanks very much to both of you for your comments. You seem to disagree about the speed of Arduino. Being quite new to this I would have thought that 1800 RPM is relatively slow. The idea of a running average is a good one. I will need to get my head around how to code it.

Duane, your work with your lap timer looks really interesting. My problem is that at the venues I compete at it will not be easy to place beacons around the course. The organisers use an infra red timing system at the start and finish. Your idea of a buzzer sounds good. My objective is the same, to get immediate feedback on the part of the course I have just driven. I will spend some time looking at your work and  get some ideas. You are at a much more advanced stage than me.

One other thought I have had is to build a test rig using a magnet mounted on an electric drill and use serial print to analyse what exactly is being missed at higher speeds.

Steve, I take your comment about looking at lights and displays while racing. I already have a shift light which is easy to see in my peripheral vision, I don't think another couple of LED's would be a problem. The display might be.

Thanks again both of you for your help. I haven't given up and will report how I get on here.

Graham





sdturner

1800 RPM (30 Hz) is slow for a microprocessor. My concern was that the the Arduino boot loader takes time and performs a lot of error checking. It's not a lot, but it can add up. If you are doing a lot of things with standard Arduino functions it might take more than 30 mS to complete the loop. I don't know if that's what's happening to you, but it could be.

You can check for this with some test code that logs the time at the beginning of the loop for a few cycles and either displays it or spits it out on a serial link.

If the code never misses a pulse then wheelrevs == xxx should work. If changing to >= fixes the problem then there is not much else it could be.

graham7

Thanks Steve for your further input. I have not had much spare time over the last few weeks to work any further on this as I have started my sprint competitions. Had a win last week without the help of Arduino!

But today I have done a couple of tests:

As per your suggestion I put a time log at the start of the main loop and guess what it is taking 100ms to get round the loop.This is what it looks like on the serial monitor:

time=7535
time=7636
time=7737
time=7837
time=7938

So that is where my problem is.

I also wrote a simple sketch to check the sensor which displays the number of wheel revs on the lcd. I then drove the car at various speeds over the same distance. The wheel revs where the same for each drive. So now I know that the sensor is working correctly.

Now I have to work on modifying the sketch so that the loop takes less than 33ms. I don't think that using >= will work any better as won't it exit the loop as soon as the next pulse is detected?

Thanks again Steve, You have set me on the right path now.

Graham



DuaneB

Hi,

33ms is forever in micro processor terms. My guess without seeing your latest code or giving more than a cursory glance to your previous code posted earlier in the topic is that the first problem is writing to the EEPROM, thats slow, you might be better off writing to an external SD Card which I believe is a lot faster.

Secondly the floating point maths is going to be slow.

I think I mentioned this project of mine to you before, I have made a lot more progress on it in the last few weeks -

http://rcarduino.blogspot.com/2012/05/lap-timer-preview.html

Duane B.

rcarduino.blogspot.com
Read this
http://rcarduino.blogspot.com/2012/04/servo-problems-with-arduino-part-1.html
then watch this
http://rcarduino.blogspot.com/2012/04/servo-problems-part-2-demonstration.html

Rcarduino.blogspot.com

PaulS

Quote
writing to the EEPROM, thats slow, you might be better off writing to an external SD Card which I believe is a lot faster.

You've got those backwards. Writing to internal memory is going to be orders of magnitude faster than writing to external hardware.

In any case, there should be no more than 4 writes to EEPROM, one for each sector, during the whole program run.
The art of getting good answers lies in asking good questions.

graham7

A little more work and I think I have found the problem in my code, the delay in my button debounce. The code is such that it will  go through one of the loops to check the button state every time. I took the delay out and the time to go round the loop is reduced to 10ms. Now it should catch every pulse.

if (Button2Press == 0){
   val = digitalRead(inputPin2);
   delay(100);
   bounceCheck =  digitalRead(inputPin2);
   if (val == bounceCheck){
     if (val == LOW){

I just need to avoid checking the button states when recording pulses.

Duane, your project looks great. when I get mine working I will share with you some more detail.

Paul, that's what I thought. I am writing to EEPROM after the end of the run when recording has finished. During the run the times are held as variables. Thanks for your comment.

Graham

DuaneB

Hi,
   Sticking with the EEPROM verses SD Card, this page suggests that an EEPROM write takes 3.3ms -

http://arduino.cc/en/Reference/EEPROMWrite

If that is anywhere near correct I am sure there are ways of getting data into an SD Card faster.

What gives ?

Duane B

rcarduino.blogspot.com
Read this
http://rcarduino.blogspot.com/2012/04/servo-problems-with-arduino-part-1.html
then watch this
http://rcarduino.blogspot.com/2012/04/servo-problems-part-2-demonstration.html

Rcarduino.blogspot.com

Go Up