So I want to build a sort of speedometer to measure the speed of my RC cars in which I would use 2 hall effect sensors a foot apart on the ground, connected to my Duemilanove. I would then tape a magnet to the bottom of my RC car so that as it passes over the sensors, the Arduino counts the time in between the sensors tripping, then does some simple math to calculate speed and write that speed to the serial monitor in the Arduino IDE. Essentially how I think it will work (and how I'm trying to set it up now in my code) is this:
When the first sensor is HIGH, the Arduino stores the milliseconds for later use
When the second sensor is HIGH, the Arduino stores the milliseconds for that as well
Then it does this math: (3600000/((elapsedTime - startTime)*5280)) to come up with the mph
Then it writes that mph to serial.
This is about the most complicated thing I've tried to code on the Arduino, even in its relative simplicity, so I'm a bit lost. It does print numbers to the serial, but most of the time, it says "1-1-1-1-1-1-1..." with the occasional other number. I bet there's some silly thing I did and it will probably be very obvious to a lot of people. Here's my current code, any help would be appreciated!
const int startPin = 9;
const int stopPin = 10;
long startTime;
long elapsedTime;
int mph;
int startState = 0;
int stopState = 1;
void setup () {
Serial.begin(9600);
pinMode(startPin, INPUT);
pinMode(stopPin, INPUT);
}
void loop () {
startState = digitalRead(startPin);
if (startState == HIGH) {
startTime = millis();
stopState = digitalRead(stopPin);
if (stopState == HIGH) {
elapsedTime = millis();
mph = (3600000/((elapsedTime - startTime)*5280));
Serial.print(mph);
}
}
}
I understand that you only print when both StartState and StopState are high at the same time.
elapsedTime should be the same as startTime then, and your speed calculation should fail due to div by 0
Put your curly braces different, to have the chance to catch the two events in different cycles.
Once the start trigger was detected, I'd not read startPin again, to keep startState high until measurement is done. ( Or some other state housekeeping )
3600000 should be 3600000UL to force long int arithmetics ...
millis() and micros() return unsigned long for good reason, to get around overflow, so use unsigned!
Go with micros() instead of millis(). You get 1000x precision (except least micros is 4).
People who want the closest set up a long = 0 then run that through a while loop until some exit condition is reached, perhaps a change in the state of a digital pin. At least that's what reading in the archives turned up one day (I didn't think it up myself, but anyone can use it 8)).
I'm no genius programmer, I do it for fun. I don't see how you'll ever get an accurate reading of the second sensor. You only read the second sensor if the first one is HIGH. If the first one is HIGH you go on to read the second. If the second isn't HIGH the loop function runs again. If the first sensor is now LOW (and it likely is) you won't read the second as it only gets read if the first one is HIGH.
I would read the first sensor in the loop function and when it goes HIGH grab the start time and then use a while() loop to wait for the next sensor to go HIGH. When it does grab the end time. Then do the math. I would also put a counter in that loop to break it if the second sensor never reads or you'll be pushing reset.
Sort of like this (this is pseudo code)
if (firstsensor == HIGH)
{
get start time
c = 0
while(1)
{
if (secondsensor == HIGH)
{
get end time
do the math
output result
c = 1000 // since we're done this will break the loop
}
c++
if (c > 1000) break // break if no reading occurs, may need to be a bigger number
}
}
Also a I'm a bit pendantic so I noticed the misuse of elapsed time. Elapsed time would be the difference between the start and end times.
Someone will probably be along with a better idea but I hope this helps.
Thanks for the replies, guys. I actually didn't mean that the second sensor would only read when the first is high, but I guess that's how I inadvertently programmed it :~. Most likely the first sensor would no longer be high when the second one is. Thanks again
Here is my code now. I think I'm getting close, but I'm still not getting the sorts of values I'm expecting out of the serial monitor. I think there may be a problem with how I'm doing my math but I'm not sure. Right now as I don't have hall effect sensors at the moment, I'm using tact switches with a pulldown resistor to ground for when it's LOW.
Here's an example of the serial output I'm getting when I click the first switch, followed rapidly by the second switch:
1969119691-8718-8718196911969119691-8718196911969119691196911969119691196911969119691-229231969119691196911969119691196911318165544640363229272523212019181716151414
const int startPin = 9;
const int stopPin = 10;
long startTime;
long stopTime;
int mph;
int startState = 0;
int stopState = 1;
void setup () {
Serial.begin(9600);
pinMode(startPin, INPUT);
pinMode(stopPin, INPUT);
}
void loop () {
startState = digitalRead(startPin);
if (startState == HIGH)
{
startTime = micros();
}
stopState = digitalRead(stopPin);
if (stopState == HIGH)
{
stopTime = micros();
mph = (3600000000/((stopTime - startTime)*5280));
Serial.print(mph);
}
}
I think your math is OK, but there are a several problems with the code: floating inputs, division with ints, wrong variable type for micros(), etc.
Try the sketch with my modifications below. It stays in the first while loop until the first switch closes. Then it stays in the second loop until the second switch closes. Then it calculates the answer and prints it. The delay is to prevent a lot of junk from printing. 1 second may be longer than necessary, but I assume your lap times are more than 1 second!
I used 2 wires connected to pins 9 and 10. A 3rd wire was connected to ground. To simulate the 2 switches closing, I held the first 2 wires close together and dragged the ground wire over them so that each was grounded in rapid succession (first pin 9, then pin 10). The numbers I got in the serial monitor were believeable mph values.
JavaMan:
I think your math is OK, but there are a several problems with the code: floating inputs, division with ints, wrong variable type for micros(), etc.
Try the sketch with my modifications below. It stays in the first while loop until the first switch closes. Then it stays in the second loop until the second switch closes. Then it calculates the answer and prints it. The delay is to prevent a lot of junk from printing. 1 second may be longer than necessary, but I assume your lap times are more than 1 second!
I used 2 wires connected to pins 9 and 10. A 3rd wire was connected to ground. To simulate the 2 switches closing, I held the first 2 wires close together and dragged the ground wire over them so that each was grounded in rapid succession (first pin 9, then pin 10). The numbers I got in the serial monitor were believeable mph values.
const int startPin = 9;
const int stopPin = 10;
unsigned long startTime=0;
unsigned long stopTime=0;
float mph;
That's awesome, thank you so much! I'm pleased with myself to know that I wasn't too far off. As it turns out I actually switched to unsigned long after posting my code, so I was heading in the right direction I suppose. I've decided to keep my setup as-is for now, so the inputs are pulled down to gnd and they wait for a high signal, which is working out just fine for me at the moment. I noticed that the serial port is giving me whole numbers (11.00, 4.00, etc.).. is there a way to get it to give me the decimals as well for a more precise measurement? Thanks again
Nevermind, I've got it with help from my friend
I changed the 3600000000 to a float as well so it now reads 3600000000.0 and now it gives me 2 decimal places populated with real digits! I've also had it write " mph" to serial as well so it writes now, for example, "37.62 mph" and then goes to the next line.
Glad you got it to work! I was going to suggest mph = 681818.2/(stopTime - startTime); which would accomplish the same thing.
Pulldown resistors are fine. The reason I used the internal pullup resistors is because I cheated and just used bare wires. No breadboard, switches or resistors.
JavaMan:
Glad you got it to work! I was going to suggest mph = 681818.2/(stopTime - startTime); which would accomplish the same thing.
Pulldown resistors are fine. The reason I used the internal pullup resistors is because I cheated and just used bare wires. No breadboard, switches or resistors.
Ah yes, I suppose you're right about the 681818.2, huh? That would take care of the float as well as get rid of some unnecessary arithmetic. Sort of a silly oversight, glad one of us thought of it! Thanks for your help
I just added a km/h conversion in it as well, so it stores the kph (converted from mph) as a float as well and prints it along side the mph readout! This is exciting for me as I haven't really used the serial monitor much before besides for the example sketches, haha. Code followed by a screenshot of my serial monitor readout