# measuring wheel speed using inductive proximity sensor

Hello. I am trying to read speed from both front and rear wheels of my motorcycle. I am using an Arduino uno with a 16x2 lcd display to view results and two fotek pl-05l npn inductive sensors. Sensors are reading wheel spinning from 5 screws that each one has for the disk brake. The main idea is to calculate time between each screw and assuming that its 1/5 of wheel to calculate the km/h. So far I have managed to read wheel speeds and comparing them to my original motorcycle speedometer they are pretty close. The problem is that often I get no logical km/h for example while riding at stable 90km/h suddenly shows 50km/h for a while and after that I have 90km/h again. My sensors are supposed to work at 800hz which from my calculations are enough for over 300 km/h. Can anyone help to find a solution. Thanks’ in advance.

``````int rearpin = 7;
int frontpin = 8;
int rearpinvalue;
int frontpinvalue;
int lastrearpinvalue;
int countboltr;
int countboltf;
float rearstarttime;
float timepassedrear;
float rearoldmillis;
int lastfrontpinvalue;
float frontstarttime;
float timepassedfront;
float frontoldmillis;
float kmhrear,kmr,kmhfront,kmf;

#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>

#define BACKLIGHT_PIN     3
#define En_pin  2
#define Rw_pin  1
#define Rs_pin  0
#define D4_pin  4
#define D5_pin  5
#define D6_pin  6
#define D7_pin  7

int n = 1;

void setup()
{

lcd.begin (16,2); //  <<----- My LCD was 16x2
// Switch on the backlight
lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);
lcd.setBacklight(HIGH);
lcd.home (); // go home

pinMode(rearpin,INPUT_PULLUP);
pinMode(frontpin,INPUT_PULLUP);
Serial.begin(9600);
lcd.setCursor (0,0);             // display welcome message on startup for 4 seconds and then clearing screen
lcd.print("HAVE A NICE RIDE");
lcd.setCursor (0,1);
lcd.print("   DRIVE SAFE   ");
delay(4000);
lcd.setCursor (0,0);
lcd.print("                ");
lcd.setCursor (0,1);
lcd.print("                ");

}

void loop()
{

if (rearpinvalue == HIGH && lastrearpinvalue == LOW)        // cheking if sensor has just left bolt limit which result changing from LOW to HIGH
{

rearstarttime = millis();                                  // if yes store milliseconds from start time of arduino boot
timepassedrear = (rearstarttime - rearoldmillis);          // and calculate time between previous valid sensor read

rearoldmillis = rearstarttime;                             // storing milliseconds for the next time code run the same funtion

kmhrear=(0,402*3600000)/timepassedrear;                    // calculating km/h for rear wheel depending wheel diameter,tire profile (17 inch wheel,55 profile in my case)
kmr=(kmhrear/1000)/1000;

countboltr = countboltr+1;                                 // every time running loop increasing a counter by one so every 20 bolt readings
if (countboltr == 20)                                      // we print km/h of rear wheel on display.
{
lcd.setCursor (0,1);
lcd.print("rear km ");
lcd.print(kmr);
countboltr = 0;                                            // after counter reach 20 we set it again to 0 to start counting again
}

}
lastrearpinvalue=rearpinvalue;                             // setting value so we can make compare at next loop run

// same for front wheel

if (frontpinvalue == HIGH && lastfrontpinvalue == LOW)           //cheking if sensor has just left bolt limit
{

frontstarttime = millis();                                       // if yes store milliseconds from start time of arduino boot
timepassedfront = (frontstarttime - frontoldmillis);             // and calculate time between previous valid sensor read

frontoldmillis = frontstarttime;                                 // storing milliseconds for the next time code run the same funtion

kmhfront=(0,376*3600000)/timepassedfront;                        // calculating km/h for front wheel depending wheel diameter,tire profile (17 inch wheel,70 profile in my case)
kmf=(kmhfront/1000)/1000;

countboltf = countboltf+1;                                       // every time running loop increasing a counter by one so every 20 bolt readings
if (countboltf == 20)                                            // we print km/h of rear wheel on display.
{
lcd.setCursor (0,0);
lcd.print("front km ");
lcd.print(kmf);
countboltf = 0;                                                  // after counter reach 20 we set it again to 0 to start counting again
}
}

lastfrontpinvalue=frontpinvalue;                                 // setting value so we can make compare at next loop run
}
``````

To remove spurious readings, you could average the readings over a number of readings and reject those that are not near the current average.

Weedpharma

nixtas:
Hello. I am trying to read speed from both front and rear wheels of my motorcycle. I am using an Arduino uno with a 16x2 lcd display to view results and two fotek pl-05l npn inductive sensors. Sensors are reading wheel spinning from 5 screws that each one has for the disk brake. The main idea is to calculate time between each screw and assuming that its 1/5 of wheel to calculate the km/h. So far I have managed to read wheel speeds and comparing them to my original motorcycle speedometer they are pretty close. The problem is that often I get no logical km/h for example while riding at stable 90km/h suddenly shows 50km/h for a while and after that I have 90km/h again. My sensors are supposed to work at 800hz which from my calculations are enough for over 300 km/h. Can anyone help to find a solution. Thanks’ in advance.

I would recommend you change how your program is receiving the sensor data. You expect the loop() to catch the HIGH/LOW readings. It only looks for a microsecond during the digitalRead().

Think about using interrupts, with some sanity checks.

connect your signal to pin 2 and pin 3:

``````#define REARPIN 2
#define FRONTPIN 3

void setup(){
pinMode(REARPIN,INPUT_PULLUP);
pinMode(FRONTPIN,INPUT_PULLUP);

attachInterrupt(digitalPinToInterrupt(REARPIN),rearFunction,FALLING);
attachInterrupt(digitalPinToInterrupt(FRONTPIN),frontFunction,FALLING);
}

volatile unsigned long lastRearMillis; // millisecond counter lasttime rear sensor activated
volatile unsigned long rearDelta;      // current millisecond between activations
volatile unsigned long lastFrontMillis;
volatile unsigned long frontDelta;

void rearFunction(){
unsigned long tick=millis(); // current millisecond
rearDelta = tick - lastRearMillis;
lastRearMillis = tick;
}

void frontFunction(){
unsigned long tick=millis(); // current millisecond
frontDelta = tick - lastFrontMillis;
lastFrontMillis = tick;
}

void loop(){
static unsigned long rearSpeed=0, frontSpeed=0; // initialize once at startup to zero
static unsigned long lastRearUpdate=0;
static unsigned long lastFrontUpdate=0;

// what is minimum speed?
if((rearDelta<MIMIMUMSPEED)&&(rearDelta>MAXIMUMSPEED)){ // valid speed
//convert milliseconds between bolts to velocity

if(millis()-lastRearUpdate>500){// update the screen every 1/2 second
lastRearUpdate = millis();

// code to update screen
}

if((frontDelta<MIMIMUMSPEED)&&(frontDelta>MAXIMUMSPEED)){ // valid speed
//convert milliseconds between bolts to velocity

if(millis()-lastFrontUpdate>500){// update the screen every 1/2 second
lastFrontUpdate = millis();

// code to update screen
}
}
``````

Chuck.

weedpharma:
To remove spurious readings, you could average the readings over a number of readings and reject those that are not near the current average.

Weedpharma

the next step after reading correct wheel speeds is to compare them so if rear wheel loose traction and spins faster than front to cut ignition until regain traction.i think that rejecting out of range values will not help me in the future code.thank's

chucktodd:
I would recommend you change how your program is receiving the sensor data. You expect the loop() to catch the HIGH/LOW readings. It only looks for a microsecond during the digitalRead().

Think about using interrupts, with some sanity checks.

connect your signal to pin 2 and pin 3:

``````#define REARPIN 2
``````

#define FRONTPIN 3

void setup(){
pinMode(REARPIN,INPUT_PULLUP);
pinMode(FRONTPIN,INPUT_PULLUP);

attachInterrupt(digitalPinToInterrupt(REARPIN),rearFunction,FALLING);
attachInterrupt(digitalPinToInterrupt(FRONTPIN),frontFunction,FALLING);
}

volatile unsigned long lastRearMillis; // millisecond counter lasttime rear sensor activated
volatile unsigned long rearDelta;      // current millisecond between activations
volatile unsigned long lastFrontMillis;
volatile unsigned long frontDelta;

void rearFunction(){
unsigned long tick=millis(); // current millisecond
rearDelta = tick - lastRearMillis;
lastRearMillis = tick;
}

void frontFunction(){
unsigned long tick=millis(); // current millisecond
frontDelta = tick - lastFrontMillis;
lastFrontMillis = tick;
}

void loop(){
static unsigned long rearSpeed=0, frontSpeed=0; // initialize once at startup to zero
static unsigned long lastRearUpdate=0;
static unsigned long lastFrontUpdate=0;

// what is minimum speed?
if((rearDelta<MIMIMUMSPEED)&&(rearDelta>MAXIMUMSPEED)){ // valid speed
//convert milliseconds between bolts to velocity

if(millis()-lastRearUpdate>500){// update the screen every 1/2 second
lastRearUpdate = millis();

// code to update screen
}

if((frontDelta<MIMIMUMSPEED)&&(frontDelta>MAXIMUMSPEED)){ // valid speed
//convert milliseconds between bolts to velocity

if(millis()-lastFrontUpdate>500){// update the screen every 1/2 second
lastFrontUpdate = millis();

// code to update screen
}
}

``````

Chuck.
``````

thank’s for your reply.the screw head is about 1cm width and the gap between each screw is about 5 cm.so i believe that even a screw reading fails at one loop it will be at about same position for next code reading loop.is that right?do you believe that this values are not enough to get a correct high low reading.i want to understand why this is not working before i proceed to interrupt.

thank's for your reply.the screw head is about 1cm width and the gap between each screw is about 5 cm.so i believe that even a screw reading fails at one loop it will be at about same position for next code reading loop.is that right?do you believe that this values are not enough to get a correct high low reading.i want to understand why this is not working before i proceed to interrupt.

I agree with Chuck that you are probably missing pulses and that interrupts are the better way to go. If you miss a pulse, the reported speed will be 1/2.

Have you calculated the time each bolt head will be in front of the sensor? Have you benchmarked your loop speed with micros() time markers placed in the code?

You can speed up the loop by placing the lcd prints of the unchanging characters "front km" and "rear km" in setup so they are only printed once.

In your code, you measure the period for every bolt to bolt period, but report the data every 20. You might as well get in some averaging by timing the period for several bolt-bolt transitions.

Here is some example code which uses interrupts, and times the interval between multiple interrupts to determine rpm.

``````volatile byte  count = 0;
byte numCount = 8; //number of pulse intervals to measure

volatile unsigned long startTime;
volatile unsigned long endTime;
unsigned long copy_startTime;
unsigned long copy_endTime;

volatile boolean finishCount = false;
float period;

unsigned int rpm = 0;

void setup()
{
Serial.begin(115200);
Serial.println("start...");

attachInterrupt(digitalPinToInterrupt(3), isrCount, RISING);//interrupt on pin3
}

void loop()
{
if (finishCount == true)
{
finishCount = false;//reset flag
// disable interrupts, make protected copy of time values
noInterrupts();
copy_startTime = startTime;
copy_endTime = endTime;
count = 0;
interrupts();

period = (copy_endTime - copy_startTime) / 1000.0; //micros to millis
//debug prints
Serial.print(period); //total time for numCount
Serial.print('\t');
Serial.println(period/numCount);//time between individual pulses

rpm = numCount * 12.0 * (1000.0/period);//five counts per revolution

//rpm = numCount * 60.0 * (1000.0 / period);//one counts per revolution
//rpm = numCount * 30.0 * (1000.0 / period);//two counts per revolution

Serial.print("RPM = ");
Serial.println(rpm);
}
}

void isrCount()
{
if (count == 0)//first entry to isr
{
startTime = micros();
}

if (count == numCount)
{
endTime = micros();
finishCount = true;
}
count++; //increment after test for numCount
}
``````

The symptoms you describe sound like two different cycles ‘syncronizing’ (so that you miss some pulses), then going out of sync (and get them all!). The ‘loop()’ is the first cycle, the cycle of the wheel turning is the other.

What kind of pulse shaping, if any, do you do on the output of the sensor?

Depending on how short the pulse is, it could be that the loop randomly synchronizes its sampling of one of the pulses (on each turn of the wheel) outside of the time when your pulse is active. All of which is a complicated way of suggesting you are dropping events, as both cattledog and chuck previously suggested

I second (or third…) the suggestion of using interrupts. Also, on the Arduino, timer 1 can do a ‘capture input’ on an external event - perfect for this application.

Here’s how it might work:
Start the timer 1 ticking off of a system clock (or whatever…), with the ‘capture input’ feature of the timer enabled to detect the leading edge of the incoming pulse. When the pulse occurs, the capture-input feature will record the current clock value in the capture input register, and (optionally) trigger an interrupt. This is all done by hardware - the likelyhood of missing a pulse at your suggested speeds is close to zero.

The detection is is edge triggered, which should be more accurate than the sampling in the middle of the pulse you are doing now. On interrupt, the interrupt routine reads the capture input register, and by simple subtraction determines the number of clock pulses (interval) since the previous one.

To keep from missing an event, use an interrupt to determine when to take the reading from the capture register.

Hope this helps.

nixtas, interesting project. I am a bit concerned that you will not have enough resolution with using just the 5 bolts. Measure the circumference of your tire and divide it by 5. You will have a resolution of 10" plus or minus a few. You would have to get an error of two pulses minimum to react. The tire has already spun 20". On another note you may check your distance of you prox. from the bolt, it may be on the edge of its reading range. and using interrupts while counting is always a good idea.

1/ I take it you're getting clean pulses from your sensors - can you observe them with a scope?

2/ I've used pulseIn HIGH and LOW in a revcounter and obtained rock solid results...

regards

Allan.

Hi,

Just looking at your code, if you just write code for one wheel only, does the problem occur?
Also these inductive sensors are industrial type, the pull_ups in the arduino are between 20K and 50K, these sensors are more used to 24V and 4K7 pullups.

So it would be worth putting a 4K7 resistor from the output of the sensor to 5V, rather than rely on the 20K or higher internal arduino pullup.

I would first only read and calculate for one wheel, get it right then see if the other will work in the same configuration.

Unfortunately FOTEK happen to be the brand of SSR that are sold on ebay where some have proven pretty shonky build quality.

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?
Especially how you power the sensor as its minimum suppy is 10V.

Thanks… Tom…

TomGeorge:
Hi,

Just looking at your code, if you just write code for one wheel only, does the problem occur?
Also these inductive sensors are industrial type, the pull_ups in the arduino are between 20K and 50K, these sensors are more used to 24V and 4K7 pullups.

So it would be worth putting a 4K7 resistor from the output of the sensor to 5V, rather than rely on the 20K or higher internal arduino pullup.

I would first only read and calculate for one wheel, get it right then see if the other will work in the same configuration.

Unfortunately FOTEK happen to be the brand of SSR that are sold on ebay where some have proven pretty shonky build quality.

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?
Especially how you power the sensor as its minimum suppy is 10V.

Thanks… Tom…

The power supply I use is 12 volt from my motorcycle battery,i tried to disable INPUT_PULLUP and use 10K external resistors but I had the same result.

Ok most of you suggested to go with interrupts so I tried today.Upload code and rolled my bike just to see if I get a count on display. It looked ok,....ok until I turned on my motorcycle then everything messed up, i was getting measurements without rolling, the more I throttling the more random km/h values appeared, seems like noise from electric parts affect my sensor inputs, with my previous code without interrupts there wasn't these effect. I guess there is a lot of work to do until I reach my goal.Can someone tell me at what voltage does falling or rising of interrupts takes place,if I have a 1volt for example noise will at falling back to 0 cause a FALLING trigger?

allanhurst:
1/ I take it you're getting clean pulses from your sensors - can you observe them with a scope?

2/ I've used pulseIn HIGH and LOW in a revcounter and obtained rock solid results...

regards

Allan.

can you give same more details of your project to give it a try.thanks

Hi Nixtas…

it sounds as if your getting electrical noise on your signal - you’ll have to sort that out first. If you have access to a scope this will be useful for identifying this. No software tricks help you here…

The relevant bit of my code is :

``````    pulselow = pulseIn( pulsepin, LOW,20000);  // measure pulsewidth
pulsehigh = pulseIn( pulsepin, HIGH,20000);  // measure pulsewidth
pulselen = pulselow + pulsehigh;    // total pulsewidth

freq = 4000000 / pulselen ;          // gives freq in Hz * 4
//and scale noting that typically 500hz = 2000rpm
``````

and note it takes a signal from the activation pin of a conventional car alternator - actually on my daughter’s boat - and uses the attached signal conditioning - this has a high dynamic range - about 100mV p-p up to several volts - and isolates the arduino input from overvoltage.

regards

Allan

ps I then used the value to drive an aircored meter movement using sine and cosine lookup tables - which won’t be relevant to you…

edit… sorry - the input capacitor should be 1uF, not 1n as shown…

revsig.pdf (18.1 KB)