Reed sensor to calculate speed

Hello all

I am trying to make speedometer for my bike. I've got a reed sensor and a hc05 bluetooth module.

I want to calculate the speed im going.

The problem i'm having is that 'timer' stays 0 at all times. It's the first time im working with these interrupts so i might be doing something wrong.

Can anyone help? I have added prints for all variables and commented their output next to it

int reedSwitch =7;
int circumference;   
int totaltime;      
int Speed;           
int timer;          
int radius = 311;   // radius in mm
int reed_status;

void isr()           
 {   
      timer++;                    
      delayMicroseconds(9600);  
 }
 
void setup(){
  Serial.begin(9600);                                        
  attachInterrupt(digitalPinToInterrupt(reedSwitch), isr, RISING);   
   pinMode(reedSwitch, INPUT_PULLUP);                        
   timer = 0;                                                
   Speed= 0;                                                 
   totaltime = 0;                                             
   circumference = 6.28 * radius;                             
}

void loop() {
    //reed_status = digitalRead(reedSwitch);  // this code prints 0 when magnet is close and 1 when no magnet is close so the reed sensor is working 
    //Serial.println(reed_status);                                                  
    delay(5);
   if (millis() - totaltime >= 1000) {                                      
                                                                            
        detachInterrupt(digitalPinToInterrupt(reedSwitch));                          

        Speed= ((0.0036*circumference)/(millis() - totaltime)*timer);      // Calculates the speed of the bike.  mm/s -> km/u


        Serial.print("Speed ");                                               
        Serial.println(Speed); //prints 0

        Serial.print("circum ");                                               
        Serial.println(circumference); // prints 1953

        Serial.print("totaltime ");                                               
        Serial.println(totaltime); // this prints 1000 + 1000 every loop until about 32000. Then it prints -32000 and the prints start coming way faster
        
        Serial.print("timer ");                                               
        Serial.println(timer); //prints 0

        timer = 0;                                                          
        totaltime = millis();                                               
        attachInterrupt(digitalPinToInterrupt(reedSwitch), isr, RISING);             
   }
}

'Speed' is an int but your are calculating a floating point value. try changing any variables that you are using that ARE not integer values to float

also for your 'timer' variable, I would suggest you use volatile int timer

hope that helps....

Thank you for your reply and help.

I changed Speed to float but it did not change anything. Just to be sure i also changed totaltime to float, timer to volatile float timer and circumference to float as a second test.

This also did not change anything.

I think the main problem is that timer is always 0 (that causes the equation to always be 0)

Still trying to decipher your code. 'totaltime' needs to be an unsigned long integer, by the way.

I've never seen anyone put a delay() in an ISR. It doesn't work, does it?

if that is the case then it seems that the ISR is never triggered then...

please know a schematic of your circuit connections of the reed relay to the arduino. hopefully that may shed some light on this issue...

btw why the delay within the ISR?? sort of a bad practice to put one within an ISR...

So what is that delayMicroseconds(9600) in the ISR for anyway?

By the way, you don't need to detach and attach the interrupt every time. Use interrupts() and noInterrupts() instead.

I have tried with and without the delay. I have deleted it now.

How do i use interrupts() and noInterrupts()?

Do i just replace detach with noInterrupts();
and attach with interrupts()?

Working on a DIY scheme of my setup btw

int reedSwitch =7;
float circumference;   
float totaltime;      
float speedt;           
volatile float timer;          
int radius = 311;   // radius in mm
int reed_status;

void isr()           
 {   
      timer++;                        // Begin the timer and start counting
 }
 
void setup(){
  Serial.begin(9600);                                        
  //attachInterrupt(digitalPinToInterrupt(reedSwitch), isr, RISING);   
   pinMode(reedSwitch, INPUT_PULLUP);                        
   timer = 0;                                                
   speedt = 0;                                                 
   totaltime = 0;                                             
   circumference = 6.28 * radius;                             
}

void loop() {
    //reed_status = digitalRead(reedSwitch); 
    //Serial.println(reed_status); // prints 0 when magnet is close and 1 when no magnet is close
    delay(5);
   if (millis() - totaltime >= 1000) {                                      
                                                                            
        //detachInterrupt(digitalPinToInterrupt(reedSwitch));                          
        noInterrupts();
        speedt = ((0.0036*circumference)/(millis() - totaltime)*timer);      // Calculates the speed of the bike.  mm/s -> km/u

        Serial.print("Speed ");                                               
        Serial.println(speedt);

        Serial.print("circum ");                                               
        Serial.println(circumference);

        Serial.print("totaltime ");                                               
        Serial.println(totaltime);
        
        Serial.print("timer ");                                               
        Serial.println(timer);

        timer = 0;                                                          
        totaltime = millis();     
        interrupts();                                          
       // attachInterrupt(digitalPinToInterrupt(reedSwitch), isr, RISING);             

        
   }
}

It's better for you to refer to the official documentation:

Look in the Functions section.

This is probably the worst schematic you have ever seen, but i think it shows everything necessary and i dont have software for making schemes...

the reed module has a 10k pullup resistor (VMA308: ARDUINO® COMPATIBLE MINI MAGNETIC REED MODULE (2 pcs) – Velleman – Wholesaler and developer of electronics)

I have tried with and without a 10k ohm resistor between + and - of the reed switch

Picture: the hc05 module is disconnect because you cant upload sketches when the 0 and 1 ports are connected.

I have changed my code to millis() - totaltime is in milliseconds, my formula was for seconds.

I have divided millis() - totaltime by 1000. I think this should work now. I havent testen on a wheel but by playing with a magnet, the values are kinda believable.

int reedSwitch =2;
float circumference;   
float totaltime;      
float speedt;           
volatile float timer;          
int radius = 311;   // radius in mm
int reed_status;

void isr()           
 {   
      timer++;                        // Begin the timer and start counting
 }
 
void setup(){
  Serial.begin(9600);                                        
  attachInterrupt(digitalPinToInterrupt(reedSwitch), isr, RISING);   
   pinMode(reedSwitch, INPUT_PULLUP);                        
   timer = 0;                                                
   speedt = 0;                                                 
   totaltime = 0;                                             
   circumference = 6.28 * radius;                             
}

void loop() {
   if (millis() - totaltime >= 1000) {                                      
                                                                      
        detachInterrupt(digitalPinToInterrupt(reedSwitch));                          

        speedt = ((0.0036*circumference)/((millis() - totaltime) / 1000 )*timer);      // Calculates the speed of the bike.  mm/ms -> km/u
       
        timer = 0;                                                          
        totaltime = millis();     
                                                 
       attachInterrupt(digitalPinToInterrupt(reedSwitch), isr, RISING);             

        
   }
}

Perhaps the major problem is you are using a module with an active IC device that just happens to have a reed switch. But your program is written as if it is actually directly connected to a real reed switch. Nothing in the link you gave for the module has any information about the module or how to use it. I see a pot on the board, so that is likely a sensitivity control, but perhaps some other use. Who knows. But until you get that actually defined, there is no hope for you program.

There is no reason to use interrupts for an application like this. They introduce more problems (for a beginner) than they solve.

Do the timing in loop().

I know that if i hold a magnet next to it the digitalRead value is 0 and when no magnet is close, the digitalRead value is 1.

But my program definitly doesnt work as intended yet... The values for Speed in the program above are something like:

speed49.22
speed7.03
speed0.00
speed0.00
speed21.09
speed14.06
speed14.06
speed7.03
speed49.22
speed28.12
speed21.09
speed49.22
speed35.16
speed63.28
speed49.22
speed28.12
speed0.00
speed7.03
speed0.00
speed14.06

Yea, i reworked my sketch to this. It is definitly not perfectly accurate but an estimate is good enough for the time being.

unsigned long day = 86400000;
unsigned long hour = 3600000; // 3600000 milliseconds in an hour
unsigned long minute = 60000; // 60000 milliseconds in a minute
unsigned long second =  1000; // 1000 milliseconds in a second

unsigned long startMillis;  
unsigned long elapsedTimeCircumference;
unsigned long previousTimeCircumference;
const unsigned long period = 250; 

unsigned long Speed;

int reed_sensor = 2;
int reed_status;

float elapsedDistance;
//in mm
unsigned int radius = 311;
float circumference;  

void setup()
{
  Serial.begin(9600);
  pinMode(reed_sensor, INPUT_PULLUP);   
  startMillis = millis();
  elapsedTimeCircumference = millis();
  // in mm
  circumference = 6.28 * radius; 
}

void loop()
{
   reed_status = digitalRead(reed_sensor);
   startMillis = millis();
  
  if (reed_status == 0)
  {
      readValues();
  }
}

void readValues() {
        // tijd sinds vorige rotatie in ms
        elapsedTimeCircumference = startMillis - previousTimeCircumference;
        
        // snelheid in km/uur
        Speed = (( 3.6 * circumference)/(elapsedTimeCircumference)); 

        // afgelegde afstand in mm
        elapsedDistance += circumference ; 
        
        previousTimeCircumference = millis();
        Serial.println("speed: ");
        Serial.println(Speed);
        distance(elapsedDistance);
        Serial.println("Totale tijd: ");
        time(millis());
        delay(100);
 }

 void distance(float distancemm) {
  if (distancemm <= 500000) {
      float meter = distancemm / 1000;
      Serial.print("afgelegde afstand in meter:");
      Serial.println(meter);
    } else {
      float km = distancemm / 1000000;
      Serial.print("afgelegde afstand in km:");
      Serial.println(km);
      }
  }

void time(long timeNow){
 int days = timeNow / day ;                                
 int hours = (timeNow % day) / hour;                      
 int minutes = ((timeNow % day) % hour) / minute ;       
 int seconds = (((timeNow % day) % hour) % minute) / second;
 
  Serial.print(days,DEC);  
  printDigits(hours);  
  printDigits(minutes);
  printDigits(seconds);
  Serial.println();  
}

void printDigits(byte digits){
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits,DEC);   
}

The way I solve timing problems like this is:

  1. wait until the sensor first detects the magnet and closes,
  2. wait for the sensor to open, then record start_milliseconds.
  3. wait for sensor to close
  4. wait for sensor to open, calculate total time and report speed.

For more stable reports, keep a running average of speeds (say, the last three values, and report the average).

Would that be with a do while? Something like this:

if(read_status == 0) {
do {
  delay(50);          
  read_status  = digitalRead(reed_sensor);
} while (read_status == 0);
//continu code
}

Assuming 0 or LOW means magnet detected, here is what I have done.

void loop() {
while (digitalRead(sensor) == HIGH) ;  //wait until magnet detected.
while (digitalRead(sensor) == LOW); //wait until it passes
unsigned long start = millis();
while (digitalRead(sensor) == HIGH); //wait until detected again
while (digitalRead(sensor) == LOW); //wait until it passes again
unsigned long total_time = millis() - start;
report_speed(total_time);
}

NOTE: This skips one revolution of the wheel and starts over, which with a little more work, can be fixed. And, of course, the code is blocking. The Arduino can't do anything else.

While troubleshooting I would print out the raw timer reading. When that works the way you want the worry about the conversion to Speed.

And to get a feel for how fast the pulses are coming you should calculate the time between pulses at your max desired sensing speed.