Why would we care, though? The only thing that matters is how many pulses per kilometre, because it's kph we are trying to determine.
Yes, using this current approach - whereby we effectively wait for 250ms and then wait for two rising edges - could be done without the if() statement and just using a 250ms delay() inside the loop.
That's a good spot, @oldvetteguy, which I had missed. @enzom32: You could leave the code as it is, or else do what @oldvetteguy suggests and use a delay(() instead. They are functionally equivalent.
In my opinion there is a much better way to implement this using FSMs, because of the issues arising when the incoming pulses are more than 250ms apart, but I don't recommend that at the moment. The right thing to do is get this working first. Then think about improving it.
@enzom32: have you tried your code yet? What are the results?
This doesn't make sense to me, but I may be missing something.
But right now I think the best thing is for @enzom32 to implement the code as is and report how it works.
HI.
I made a speedo for my car using a HE sensor. The Arduino coding was by far the easiest part of the project. The hardest was the physical installation of the sensor and a magnet on a driveshaft.
I used the pulseln() function. This returns the duration of a pulse coming from the HE sensor. This function is well explained in the Arduino reference section.
A conversion factor (simply a specific numerical value) is used to convert the duration to kph. You can theoretically determine the CF using wheel diameter, but that will only give you a rough starting point. I just started with a number & compared the displayed kph from the Arduino with the car's speedo which I'm confident is pretty accurate, and adjusted the CF accordingly. Repeat until satisfied. Can also calibrate against radar speed displays on freeways.
Great thing with this speedo is that it is always available (unlike GPS) and it is consistent in accuracy from zero to 110 kph.
Use the timeout feature of pulseln() to deal with very low to stationary speeds. I know pulseln() is blocking code so others prefer the interrupt approach. If the sketch is not doing anything else then it doesn't matter if it is blocking code.
John.
@SteveThackery @oldvetteguy @HillmanImp Thanks for the help I would like to share an update with you guys. So I changed the code since this line (Speed =(11396.011396/DeltaTime)*3.6;) was wrong. Using math I figured out that when the car is traveling 1m/s the pulses are 87ms apart. And I did it! It works! However only at slow speed
. In fact, till 30 km/h the speedometer is very precise but as soon as I pass 30 km/h the speed goes crazy and the Arduino prints 200km/h, 70km/h, 40km/ 90km/h while I am going constant at 50 km/h with my car. I will attach the full code again. Perhaps, this code is not faster enough to calculate the speed of my car when the pulses are very near to each other. What do you guys think? Thanks
P. S The code still does not display 0 km/h because I first want to make sure it works fine and then I will finish it up and make the last adjustments
#define PIN_WHEEL_SENSOR 3
int Pulses = 0;
float TimeBefore;
float TimeFor0;
float TimeAfter;
float DeltaTime;
int Speed;
void setup() {
pinMode(PIN_WHEEL_SENSOR, INPUT_PULLUP);
Serial.begin(9600);
}
void loop() {
while (digitalRead(PIN_WHEEL_SENSOR) == LOW) {
} // Waits until the wheel sensor value is HIGH
TimeBefore = millis(); // Records the time when the sensor is HIGH
while (digitalRead(PIN_WHEEL_SENSOR) == HIGH) {
} // Waits until the wheel sensor changes again value form HIGH to LOW
while (digitalRead(PIN_WHEEL_SENSOR) == LOW) {} // Waits until the wheel sensor value is back to HIGH
TimeAfter = millis(); // Records the time when the sensor is HIGH again
DeltaTime = TimeAfter - TimeBefore; // Finds the difference in time between one pulse
Speed = round((87.4/DeltaTime)*3.6); // Finds the speed
Serial.print(Speed);
Serial.print(" KM/H");
Serial.println();
delay(500);
}
I was waiting for such results, read post #2 and #14.
I'm wondering what the outcome is if the pin is HIGH at the top of the void loop. The half second delay at the bottom means the wheel could be anywhere in its rotation at the top of the loop.
The pulseIn() function makes this easy.
Two comments. First, @HillmanImp is right: if the pin is HIGH you will drop straight through to TimeBefore = millis(), and thus record an invalid value. I'm embarrassed that I didn't spot that in your code.
If you are to continue with this approach, you need another while loop at the top to handle that situation:
void loop() {
while (digitalRead(PIN_WHEEL_SENSOR) == HIGH) {
} // Waits until the wheel sensor goes LOW
while (digitalRead(PIN_WHEEL_SENSOR) == LOW) {
} // Waits until the wheel sensor value goes HIGH
TimeBefore = millis(); // Timestamp the first LOW to HIGH transition
while (digitalRead(PIN_WHEEL_SENSOR) == HIGH) {
} // Waits until the wheel sensor changes again value form HIGH to LOW
while (digitalRead(PIN_WHEEL_SENSOR) == LOW) {} // Waits until the wheel sensor value is back to HIGH
TimeAfter = millis(); // Timestamp the second LOW to HIGH transition;
//etc...
Make that one change and see if it improves things, @enzom32, and report back to us?
Second point: much above 32 km/h and the milliseconds are already down in the single digits, and the faster you go the more the rounding will make your speed inaccurate. Although you've made TimeBefore and TimeAfter floats, millis() returns an integer, so you have a very coarse resolution. For example, anywhere between 49 kph and 56 kph will give a DeltaTime of 6 milliseconds, which will indicate 52 kph.
The way round this is to use micros() rather than millis(). However, micros() rolls over after about 70 minutes, so you need to make sure you code handles that correctly. If you do a Google search on 'Arduino micros() rollover' you will find plenty of explanations of how to avoid the problem.
As @HillmanImp says, PulseIn() makes this easier, and that works in microseconds anyway. Of course that only times the positive-going half of the complete pulse, so you would need to update your calculation accordingly.
I suggest you put that extra while() loop in first, then try it out and report back. Then we can discuss whether to use micros() or PulseIn().
Ok, thanks I will try to do all these things and I will update you guys. However, why do you think that for pulseIn(); I need to change the calculation? If I use
DeltaTime = pulseIn(PIN_WHEEL_SENSOR, LOW, 2000);
It will read the time in milliseconds between a pulse (HIGH) and the next one right?
enzom,
DeltaTime = pulseIn(PIN_WHEEL_SENSOR, LOW, 2000);
It will read the time in milliseconds between a pulse (HIGH) and the next one right?
No, that's not what it will deliver. The reference material explains.
What you are after is the duration of a HI or LO pulse (it doesn't matter which). You need to detect the transitions from HI to LO & LO to HI. The pulseIn() function does this. You don't need to measure the duration of a complete revoluton.
I started along the lines you have (and you've made a good effort there), but when I switched to using pulseln() it became trivial.
I have not found that the speed of the wheel "outpaces" the speed of the Arduino (up to 110 kph).
@enzom32: if you have access to an oscilloscope, now might be a good time to look at the waveform you are getting from the wheel sensor. If you measure the 'up' time compared with the total time, you'll get a pretty good idea of how to adjust your calculation to use PulseIn().
Yeah. I saw someone eating a orange and they told me that you have to have 100000 points in order to cover the hall sensor.
@SteveThackery @HillmanImp I will share an update regarding what you guys suggest to me. Each day this is getting better. I used the pulseIn() and it works fine. I decided to get 3 data and average them to have a more precise speed. I will attach my new code to this post. In fact, I need to say the speed is very precise. However, we have a last problem. At higher speeds (Over 40 km/h) there is a clear gap between the output. For instance, if I go at 45 km/h the output will show 45 km/h but if I go 48 or 49 km/h the output is still 45km/h. If I accelerate more then output suddenly passes to 54 km/h skipping all the speeds in between.
This is an example of the output at these speeds:
45 KM/H
54 KM/H
45 KM/H
45 KM/H
45 KM/H
45 KM/H
54 KM/H
54 KM/H
This is my new code:
#define PIN_WHEEL_SENSOR 3
int Pulses = 0;
unsigned long DeltaTime1 = 0;
unsigned long DeltaTime2 = 0;
unsigned long DeltaTime3 = 0;
unsigned long DeltaTimeAVG = 0;
unsigned long PULSEIN_TIMEOUT = 1000000;
int Speed;
void setup() {
pinMode(PIN_WHEEL_SENSOR, INPUT_PULLUP);
Serial.begin(9600);
}
void loop() {
DeltaTime1 = pulseIn(PIN_WHEEL_SENSOR, LOW, PULSEIN_TIMEOUT) * 2;
delay(150);
DeltaTime2 = pulseIn(PIN_WHEEL_SENSOR, LOW, PULSEIN_TIMEOUT) * 2;
delay(150);
DeltaTime3 = pulseIn(PIN_WHEEL_SENSOR, LOW, PULSEIN_TIMEOUT) * 2;
DeltaTimeAVG = (DeltaTime1 + DeltaTime2 + DeltaTime3) / 3;
if (DeltaTimeAVG == 0){
Speed = 0;
}
else {
Speed = round((75.5/(DeltaTimeAVG/1000))*3.6);
}
Serial.print(Speed);
Serial.print(" KM/H");
Serial.println();
delay(200);
}
Thanks again to everyone for the help
Why are you multiplying the pulesIn() return values by 2?
Do you need the delay(150)?
Hard to imagine why the computed speed misbehaves above 45 kph.
I would get the LOW pulse duration and the HIGH pulse duration and display both, together with the computed speed. I think the better pulse to use for the speed computation is the shorter one but not sure.
You want to see the ratio of the two pulses to be constant for all speeds. Maybe something odd will show up above 45 kph.
Thanks for keeping us posted.
Too narrow pulses
It would be an enormous help if you could source a signal generator so you can do some proper bench testing and debugging. It will allow you to see if the problem occurs with a clean square wave or only with the wheel sensor. It will also allow you to watch some print() outputs as you vary the input frequency and mark-to-space ratio.
Any chance of that?
Did you read my earlier post, where I pointed out that using millis() instead of micros() will cause exactly the symptoms you describe? I know you aren't using either, now, but perhaps there is a clue in there somewhere. You seem to be suffering a loss of precision, and it's probably somewhere in your integer maths.
The last code that you posted averages the duration of 3 LOW areas between pulses. Perhaps I am missing something, but aren't you looking for the time interval between pulses? pulseIn() returns the length of the pulse in microseconds, not the time interval between pulses.
Not necessarily. All you need is the duration of some event - any event - within a single rotation of the wheel. Such a duration will be inversely proportional to the speed. Double the speed and the duration will halve.
PulseIn() returns the duration of a HI or LO pulse. I used this & it worked perfectly. No debouncing needed; accurate speed computation from 0 to 110 kph.
Perhaps there's something different about the OP's sensor.
Hope we can resolve this.
Well, one pulse will be short & the other long. One might be over 20 degrees of rotation & the other 340 degrees. So, if one is too short, the other can be used.