Speed measurement (Help Please)

Hi

I made a system that measure speed of cars based on infrared switches (sen 0019 Adjustable_Infrared_Sensor_Switch__SKU_SEN0019_-DFRobot ) , i'm using two of this sensors that detect when the car enter's and exit a predefined distance (2 mts) , the sensor triggers the millis function to capture the time and then calculate a total travel time which is used to calculate speed and then converted to Km/h.
My problem is that when the cars pass at a relative slow speed 10 to 30 Km/h everything is fine but for speeds > 30 like 40 70 or 100 Km/h it always give a 40 or around 40 Km/h (39.82 41.23)

hope you can help me with this i dont know why is not detecting fast speeds .

thanks

PD: the sensors are aimed at the body of the cars
i'm using 2 mts (2000 mm) intersensor distance
the variable "factor" is the 2000 mm * (a factor to convert mm and millis to Km/h)

here is my code

#include <Wire.h>
#include <UTFT.h>
#include <UTouch.h>


const int gate1Pin = 8;
const int gate2Pin = 9;
int previous ;
int previous2 ; 

long startTime;
long endTime;
long travelTime;
float factor = 7183.8;
float travelSpeed = 0; 
extern uint8_t SmallFont[];
extern uint8_t BigFont[];
extern uint8_t SevenSegNumFont[];

 UTFT   myGLCD(ITDB32S,38,39,40,41);   
  UTouch  myTouch(6,5,4,3,2);
 
void setup () {

  pinMode(gate1Pin, INPUT);
  pinMode(gate2Pin, INPUT);

 
  myGLCD.InitLCD();
  myGLCD.clrScr();
  myGLCD.setFont(SmallFont);
  myTouch.InitTouch();
  myTouch.setPrecision(PREC_MEDIUM);
  
  myGLCD.setColor(255, 0, 0);
  myGLCD.fillRect(0, 0, 319, 13);
  myGLCD.setColor(64, 64, 64);
  myGLCD.fillRect(0, 226, 319, 239);
  myGLCD.setColor(255, 255, 255);
  myGLCD.setBackColor(255, 0, 0);
  myGLCD.print("Medicion de Velocidad", CENTER, 1);
    
   Serial.begin(57600); 
}

void loop () {
  
  if(digitalRead(gate2Pin) == LOW && digitalRead(gate1Pin) == HIGH && startTime == 0 && endTime == 0 && previous == HIGH) // 
  { 
    startTime = millis();
  }
   if (digitalRead(gate2Pin) == HIGH && digitalRead(gate1Pin) == LOW && endTime == 0 && startTime != 0 && previous2 ==  HIGH)// 
    {
     endTime = millis(); 
    }
     if (digitalRead(gate2Pin) == LOW && digitalRead(gate1Pin) == LOW && endTime == 0 && startTime != 0 && previous2 == HIGH)
    {
      endTime = millis();
  }
  if (digitalRead(gate2Pin) == HIGH && digitalRead(gate1Pin) == HIGH)
    {
    }
    
  previous = digitalRead(gate2Pin);
  previous2 = digitalRead(gate1Pin);
  
 
  
  if (startTime != 0 && endTime != 0)
  {
     Serial.print(startTime);
  Serial.print(" === "); 
  Serial.print(endTime);
  Serial.print(" === "); 
    travelTime = endTime - startTime;
    travelSpeed = factor / travelTime ;     
    Serial.print(travelSpeed,2);
    Serial.print("  ");  
    startTime = 0;
    endTime = 0;
  }
    drawText();

}

void drawText()
{

  //myGLCD.clrScr();
    myGLCD.setBackColor(0, 0, 0);
    //myGLCD.setFont(SevenSegNumFont);
  myGLCD.setFont(BigFont);
  myGLCD.printNumF(travelSpeed,2,LEFT,60);
    /*myGLCD.print("00" , LEFT, 60);
    myGLCD.setFont(BigFont);
    myGLCD.print("o", 65, 95);
    myGLCD.setFont(SevenSegNumFont);
    myGLCD.print("00" , 82, 60);*/
    myGLCD.setFont(SmallFont);
    myGLCD.print("dV:0.01 Km/h" ,LEFT, 120);
    myGLCD.setFont(BigFont);
    myGLCD.print("Km/h", 160, 95);
    
    myGLCD.setColor(64, 64, 128);
    myGLCD.fillRoundRect(10, 180, 70, 220);
    myGLCD.setColor(255, 255, 255);
    myGLCD.drawRoundRect(10, 180, 70, 220);
    myGLCD.setBackColor(64, 64, 128);
    myGLCD.setFont(SmallFont);
    myGLCD.print("Save", 20, 190);
    myGLCD.setBackColor(0, 0, 0);
  
    myGLCD.setColor(64, 64, 128);
    myGLCD.fillRoundRect(80, 180, 140, 220);
    myGLCD.setColor(255, 255, 255);
    myGLCD.drawRoundRect(80, 180, 140, 220);
    myGLCD.setBackColor(64, 64, 128);
    myGLCD.setFont(SmallFont);
    myGLCD.print("Clear", 85, 190);
    myGLCD.setBackColor(0, 0, 0);
    
    myGLCD.setColor(64, 64, 128);
    myGLCD.fillRoundRect(150, 180, 210, 220);
    myGLCD.setColor(255, 255, 255);
    myGLCD.drawRoundRect(150, 180, 210, 220);
    myGLCD.setBackColor(64, 64, 128);
    myGLCD.setFont(SmallFont);
    myGLCD.print("Log", 155, 190);
    myGLCD.setBackColor(0, 0, 0);
  
    myGLCD.setColor(64, 64, 128);
    myGLCD.fillRoundRect(220, 180, 280, 220);
    myGLCD.setColor(255, 255, 255);
    myGLCD.drawRoundRect(220, 180, 280, 220);
    myGLCD.setBackColor(64, 64, 128);
    myGLCD.setFont(SmallFont);
    myGLCD.print("Hist", 225, 190);
    myGLCD.setBackColor(0, 0, 0);

}

Your IF statements don't make a whole lot of sense. I see what your doing in the first two but what about the last two?

Here is my method that is accurate to 120 MPH

#define FPS_to_MPH 0.68182 // scale factor
#define DISTANCE 35.5625 // distance between the two sensors or gates in feet.

const byte SensorOnePin = 2;
const byte SensorTwoPin = 3;

byte SensorOneState;
byte SensorTwoState;
float Speed = 0, MPH = 0, tmp = 0;
boolean GotSecondSensor = false;
unsigned long SensorOne_timer = 0, SensorTwo_timer = 0;

void setup() 
{
  Serial.begin(115200);
  pinMode(SensorOnePin, INPUT);
  pinMode(SensorTwoPin, INPUT);
}

void loop() 
{
  // This is using normal buttons to detect car passing through one gate to another,
  // this can be changed to use US sensors to replace the buttons. That part you will  
  // need to write yourself.
  
  SensorOneState = digitalRead(SensorOnePin);
  SensorTwoState = digitalRead(SensorTwoPin);

  if(SensorOneState && GotSecondSensor == false)  // car drives through first gate "sensor"
  {
    SensorOne_timer = millis(); // record time
    GotSecondSensor = true; // lockout this IF statement and unlock the next IF statement
  }
  
  if(SensorTwoState && GotSecondSensor == true) 
  {
    SensorTwo_timer = millis(); //record the time the car reaches the second gate
    Speed = GetSpeed(SensorOne_timer, SensorTwo_timer, DISTANCE); // send the times and the distance into the function.
    Serial.print("MPH: ");
    Serial.println(Speed);
    GotSecondSensor = false; // unlock first IF statement and lockout this IF statement.
  } 
}

float GetSpeed(unsigned long T1, unsigned long T2, float distance)
{
  MPH = distance * (FPS_to_MPH); // "(FPS_to_MPH)" -> conversion factor, feet per second to miles per hour
  tmp = (T2 - T1)/1000.00; // since the time we are using is in milliseconds, we need to convert milliseconds to seconds
  Serial.print("Time (seconds): ");
  Serial.println(tmp);
  return (MPH / tmp); //return the speed of the car in MPH
}

thanks for your help

i tried your code , did the changes to use ir sensors , got the same problem , could it be the response time of the sensors? , or maybe the distance between the two sensors ?

any ideas?

thanks

Show me how you changed the code.
1 km/h = 0.2778 m/s

40 km/h = ~11.11 m/s

It might be the sensors or it might be you calculations, I don't know.

I imagine the LCD display is eating a lot of processing time. I would only have it update the after it calculates a value.

This is an excellent place to use interrupts instead.

This is an excellent place to use interrupts instead.

You are 100% correct, which is why I also made it with interrupts. But a lot of new programmers don't know what they are or even how to use them, so it's best to give "simple" codes first.

Here is what my sketch looks like with interrupts. ( It can also measure from both directions)

#define FPS_to_MPH 0.68182 // scale factor
#define DISTANCE 35.5625 // distance between the two sensors or gates in feet.
#define swap(type, A, B) {type T = A; A = B; B = T;}

float Speed = 0, MPH = 0, tmp = 0;
boolean GotFirstSensor= false, GotSecondSensor = false;
unsigned long SensorOne_timer = 0, SensorTwo_timer = 0;

void setup() 
{
  Serial.begin(115200);
  attachInterrupt(0, SensorOneState, RISING); UNO pin 2
  attachInterrupt(1, SensorTwoState, RISING); UNO pin 3
}

void loop() 
{
  // This is using normal buttons to detect car passing through one gate to another,
  // this can be changed to use US sensors to replace the buttons. That part you will  
  // need to write yourself.

  if(GotFirstSensor & GotSecondSensor) //both must be true for this condition to be true
  {
    Speed = GetSpeed(SensorOne_timer, SensorTwo_timer, DISTANCE); // send the times and the distance into the function.
    Serial.print("MPH: ");
    Serial.println(Speed);
    ClearStates();
  } 
}

void SensorOneState()
{
  SensorOne_timer = millis(); //record the time the car reaches the first gate
  GotFirstSensor = true;
}

void SensorTwoState()
{
  SensorTwo_timer = millis(); //record the time the car reaches the second gate
  GotSecondSensor = true;
}

void ClearStates()
{
  GotFirstSensor= false;
  GotSecondSensor = false;
}

float GetSpeed(unsigned long T1, unsigned long T2, float distance)
{
  MPH = distance * (FPS_to_MPH); // "(FPS_to_MPH)" -> conversion factor, feet per second to miles per hour
  if(T1 > T2) // this here is what allows it to calculate the speed from both directions.
    swap(unsigned long, T1, T2);
    
  tmp = (T2 - T1)/1000.00; // since the time we are using is in milliseconds, we need to convert milliseconds to seconds
  Serial.print("Time (seconds): ");
  Serial.println(tmp);
  return (MPH / tmp); //return the speed of the car in MPH
}

OK thanks for your really really helpfull comments

at the end it seems that my code was the problem , i never use the interrupts before , i'm relatively new to arduino and microcontrolers , but i have experience in statistical and simulation programing.

Now about the project :

I read about the interrupts , and now i know what they do and how they work (i'm no expert but at least i know) , i haven´t been able to test this new metod with a real car but i made some simulation in my home that give good results ( got speeds > 90 Km/h)

I deleted everything but the necessary from the code , leave just the readings and calculations (for now )
i need to use the touch lcd to show and review data , also store to a sd card and a rtc module.
i know how to code that but my question is :
The arduino mega is capable of controlling all of that without messing up the readings or am i asking to much ?
if it can be done could you give me any tips on how to optimize this so i can have a accurate data.

i'll post the code in a few minutes i'm still tweaking somethings

The mega can do it but you just need to make sure your other components are compatible with each other. Make sure you get a touch screen that does not take up the same pins that the SD card does or the RTC, for they may not work. However most touch screens has SD card readers, so you should be good there.

At the end i rather use your code (hope you don't mind ) , it's more neatly than mine
i added at least the lcd functions and changed the interrupts pins
so far so good ?
i'm going to keep updating the code and it'll be great if you can take a look at it to know how is it going.

Thanks its been a really good help

#include <UTFT.h>
#include <UTouch.h>

#define MPS_to_KPH 3.6 // MPS to KPH
#define DISTANCE 1.9959 // mts
#define swap(type, A, B) {type T = A; A = B; B = T;}

float Speed = 0, KPH = 0, tmp = 0;
boolean GotFirstSensor= false, GotSecondSensor = false;
unsigned long SensorOne_timer = 0, SensorTwo_timer = 0;

extern uint8_t SmallFont[];
extern uint8_t BigFont[];

UTFT   myGLCD(ITDB32S,38,39,40,41);  
  UTouch  myTouch(6,5,4,3,2);
  
void setup() 
{
  Serial.begin(115200);
  attachInterrupt(5, SensorOneState, FALLING); //Mega pin 18
  attachInterrupt(4, SensorTwoState, FALLING); //Mega pin 19
  
  myGLCD.InitLCD();
  myGLCD.clrScr();
  myGLCD.setFont(SmallFont);
  myTouch.InitTouch();
  myTouch.setPrecision(PREC_MEDIUM);
  
  myGLCD.setColor(255, 0, 0);
  myGLCD.fillRect(0, 0, 319, 13);
  myGLCD.setColor(64, 64, 64);
  myGLCD.fillRect(0, 226, 319, 239);
  myGLCD.setColor(255, 255, 255);
  myGLCD.setBackColor(255, 0, 0);
  myGLCD.print("Speed Test", CENTER, 1);
  drawText ();
}

void loop() 
{
  // This is using normal buttons to detect car passing through one gate to another,
  // this can be changed to use US sensors to replace the buttons. That part you will  
  // need to write yourself.

  if(GotFirstSensor & GotSecondSensor) //both must be true for this condition to be true
  {
    Speed = GetSpeed(SensorOne_timer, SensorTwo_timer, DISTANCE); // send the times and the distance into the function.
    Serial.print("KM/h: ");
    Serial.println(Speed);
    myGLCD.setBackColor(0, 0, 0);
    myGLCD.setFont(BigFont);
    myGLCD.printNumF(Speed,2,LEFT,60);
    ClearStates();
  } 
  
}

void SensorOneState()
{
  SensorOne_timer = millis(); //record the time the car reaches the first gate
  GotFirstSensor = true;
}

void SensorTwoState()
{
  SensorTwo_timer = millis(); //record the time the car reaches the second gate
  GotSecondSensor = true;
}

void ClearStates()
{
  GotFirstSensor= false;
  GotSecondSensor = false;
}

float GetSpeed(unsigned long T1, unsigned long T2, float distance)
{
  KPH = distance * (MPS_to_KPH); // "(FPS_to_MPH)" -> conversion factor, feet per second to miles per hour
  if(T1 > T2) // this here is what allows it to calculate the speed from both directions.
    swap(unsigned long, T1, T2);
    
  tmp = (T2 - T1)/1000.0000; // since the time we are using is in milliseconds, we need to convert milliseconds to seconds
  Serial.print("Time (seconds): ");
  Serial.println(tmp,4);
  return (KPH / tmp); //return the speed of the car in MPH
}

void drawText()
{
    myGLCD.setBackColor(0, 0, 0);
    
    myGLCD.setFont(SmallFont);
    myGLCD.print("dV:0.01 Km/h" ,LEFT, 120);
    myGLCD.setFont(BigFont);
    myGLCD.print("Km/h", 160, 95);
    
    myGLCD.setColor(64, 64, 128);
    myGLCD.fillRoundRect(10, 180, 70, 220);
    myGLCD.setColor(255, 255, 255);
    myGLCD.drawRoundRect(10, 180, 70, 220);
    myGLCD.setBackColor(64, 64, 128);
    myGLCD.setFont(SmallFont);
    myGLCD.print("Save", 20, 190);
    myGLCD.setBackColor(0, 0, 0);
  
    myGLCD.setColor(64, 64, 128);
    myGLCD.fillRoundRect(80, 180, 140, 220);
    myGLCD.setColor(255, 255, 255);
    myGLCD.drawRoundRect(80, 180, 140, 220);
    myGLCD.setBackColor(64, 64, 128);
    myGLCD.setFont(SmallFont);
    myGLCD.print("Borrar", 85, 190);
    myGLCD.setBackColor(0, 0, 0);
    
    myGLCD.setColor(64, 64, 128);
    myGLCD.fillRoundRect(150, 180, 210, 220);
    myGLCD.setColor(255, 255, 255);
    myGLCD.drawRoundRect(150, 180, 210, 220);
    myGLCD.setBackColor(64, 64, 128);
    myGLCD.setFont(SmallFont);
    myGLCD.print("Log", 155, 190);
    myGLCD.setBackColor(0, 0, 0);
  
    myGLCD.setColor(64, 64, 128);
    myGLCD.fillRoundRect(220, 180, 280, 220);
    myGLCD.setColor(255, 255, 255);
    myGLCD.drawRoundRect(220, 180, 280, 220);
    myGLCD.setBackColor(64, 64, 128);
    myGLCD.setFont(SmallFont);
    myGLCD.print("Hist", 225, 190);
    myGLCD.setBackColor(0, 0, 0);

}

At the end i rather use your code (hope u don't mind ) , its more neatly than mine
i added at least the lcd functions and changed the interrupts pins
is it ok so far ?

Sure that's ok.

Might I make a suggestion for your display?

#include <UTFT.h>
#include <UTouch.h>

#define MPS_to_KPH 3.6 // MPS to KPH
#define DISTANCE 1.9959 // mts
#define swap(type, A, B) {type T = A; A = B; B = T;}

float Speed = 0, KPH = 0, tmp = 0;
boolean GotFirstSensor= false, GotSecondSensor = false;
unsigned long SensorOne_timer = 0, SensorTwo_timer = 0;

extern uint8_t SmallFont[];
extern uint8_t BigFont[];

UTFT   myGLCD(ITDB32S,38,39,40,41);  
UTouch  myTouch(6,5,4,3,2);

void setup() 
{
  //Serial.begin(115200);
  attachInterrupt(5, SensorOneState, FALLING); //Mega pin 18
  attachInterrupt(4, SensorTwoState, FALLING); //Mega pin 19
  
  myGLCD.InitLCD(LANDSCAPE);
  myGLCD.clrScr();
  myGLCD.setFont(SmallFont);
  myTouch.InitTouch(LANDSCAPE);
  myTouch.setPrecision(PREC_MEDIUM);
  myGLCD.fillScr(0,0,0);
  myGLCD.setColor(255, 0, 0);
  myGLCD.fillRect(0, 0, 319, 13);
  myGLCD.setColor(64, 64, 64);
  myGLCD.fillRect(0, 226, 319, 239);
  myGLCD.setColor(255, 255, 255);
  myGLCD.setBackColor(255, 0, 0);
  myGLCD.print("Speed Test", CENTER, 1);
  
  myGLCD.setBackColor(0, 0, 0);
  myGLCD.setFont(BigFont);
  myGLCD.print("KM/h:",0,60);
  myGLCD.print("Time:", 0, 30);
  drawText ();
}

void loop() 
{
  // This is using normal buttons to detect car passing through one gate to another,
  // this can be changed to use US sensors to replace the buttons. That part you will  
  // need to write yourself.

  if(GotFirstSensor & GotSecondSensor) //both must be true for this condition to be true
  {
    Speed = GetSpeed(SensorOne_timer, SensorTwo_timer, DISTANCE); // send the times and the distance into the function.
    //myGLCD.print("KM/h:",0,60);
    //myGLCD.printNumI(Speed, 60 ,120 );
    myGLCD.setBackColor(0, 0, 0);
    myGLCD.setFont(BigFont);
    myGLCD.printNumF(Speed,2,90,60);
    ClearStates();
  } 
  
}

void SensorOneState()
{
  SensorOne_timer = millis(); //record the time the car reaches the first gate
  GotFirstSensor = true;
}

void SensorTwoState()
{
  SensorTwo_timer = millis(); //record the time the car reaches the second gate
  GotSecondSensor = true;
}

void ClearStates()
{
  GotFirstSensor= false;
  GotSecondSensor = false;
}

float GetSpeed(unsigned long T1, unsigned long T2, float distance)
{
  KPH = distance * (MPS_to_KPH); // "(FPS_to_MPH)" -> conversion factor, feet per second to miles per hour
  if(T1 > T2) // this here is what allows it to calculate the speed from both directions.
    swap(unsigned long, T1, T2);
    
  tmp = (T2 - T1)/1000.0000; // since the time we are using is in milliseconds, we need to convert milliseconds to seconds
  //myGLCD.print("Time:", 0, 30);
  myGLCD.printNumF(tmp,4, 90, 30);
  return (KPH / tmp); //return the speed of the car in MPH
}

void drawText()
{
    myGLCD.setBackColor(0, 0, 0);
    
    myGLCD.setFont(SmallFont);
    myGLCD.print("dV:0.01 Km/h" ,LEFT, 120);
    myGLCD.setFont(BigFont);
    //myGLCD.print("Km/h", 160, 95);
    
    myGLCD.setColor(64, 64, 128);
    myGLCD.fillRoundRect(10, 180, 70, 220);
    myGLCD.setColor(255, 255, 255);
    myGLCD.drawRoundRect(10, 180, 70, 220);
    myGLCD.setBackColor(64, 64, 128);
    myGLCD.setFont(SmallFont);
    myGLCD.print("Save", 20, 190);
    myGLCD.setBackColor(0, 0, 0);
  
    myGLCD.setColor(64, 64, 128);
    myGLCD.fillRoundRect(80, 180, 140, 220);
    myGLCD.setColor(255, 255, 255);
    myGLCD.drawRoundRect(80, 180, 140, 220);
    myGLCD.setBackColor(64, 64, 128);
    myGLCD.setFont(SmallFont);
    myGLCD.print("Borrar", 85, 190);
    myGLCD.setBackColor(0, 0, 0);
    
    myGLCD.setColor(64, 64, 128);
    myGLCD.fillRoundRect(150, 180, 210, 220);
    myGLCD.setColor(255, 255, 255);
    myGLCD.drawRoundRect(150, 180, 210, 220);
    myGLCD.setBackColor(64, 64, 128);
    myGLCD.setFont(SmallFont);
    myGLCD.print("Log", 155, 190);
    myGLCD.setBackColor(0, 0, 0);
  
    myGLCD.setColor(64, 64, 128);
    myGLCD.fillRoundRect(220, 180, 280, 220);
    myGLCD.setColor(255, 255, 255);
    myGLCD.drawRoundRect(220, 180, 280, 220);
    myGLCD.setBackColor(64, 64, 128);
    myGLCD.setFont(SmallFont);
    myGLCD.print("Hist", 225, 190);
    myGLCD.setBackColor(0, 0, 0);

}

I made a library that can be used with the UTFT and UTouch libraries. Here is the link, TFT_Extension Newest update at reply #35. I included a lot of examples you can play around with.

Hi again

Your library is helping a lot with the lcd screen once i finish i'll post the code

i have one problem thought (not with the lcd but with the other code)

When the car passes the first and second sensor , the millis function is stored and boom the time and speed work great , but after my test on real cars, i had one problem , if the car passes through the first sensor and before it reaches the second one if there is a blink or something that changes the values of the first sensor from LOW to HIGH and LOW again the millis for the 1st sensor refreshes (all of this before the car passes trough the second sensor) , this refresh is making the total time to be incorrect and giving wrong speeds .

I tried to add some conditionals to fix this but its not working , the timer keep refreshing with the "blinks"
could you give me any tips to fix this? .

thanks

You can have a lockout condition in both timer functions.

Something like this.
void SensorOneState()
{
if( !S1_locked ) // S1_locked is originally set to false
{
SensorOne_timer = millis(); //record the time the car reaches the first gate
GotFirstSensor = true;
S1_locked = true; // this locks out the function from restoring a new time until S1_locked is set back to false.
}
}

void ClearStates()
{
GotFirstSensor= false;
GotSecondSensor = false;
S1_locked = false;
S2_locked = false;
}