Counting Revolutions
From Open Source Urbanism
In order to get the Arduino to display the bike's speed and distance travelled we need to collect data about the wheel revolutions.
Contents |
Calculating Distance and Speed
To calculate distance travelled we need the wheel circumference and the number of revolutions. The circumference can be set in a constant at the start of the sketch. The number of revolutions can be tracked by adding 1 to a variable each time the magnetic sensor is triggered.
To calculate speed we need the wheel circumference and rotation time. We get the circumference from a constant (as before), the rotation time we can calculate by comparing millis() readings when the sensor is triggered.
All variables are unsigned long. By avoiding floats we can reduce the load on the Arduino's processor. When integer values are calculated the decimal places are truncated but with careful handling we can use this fact to obtain a number of decimal places. The code for this is as follows:
// calculate distance unsigned long distancem = numWheelRotations * wheelCurcum / 1000; // distance in metres unsigned long distancekm = distancem / 1000; // distance in km (this truncates the number) unsigned long distanceRem = distancem - (distancekm * 1000); // the bit after the decimal point // calculate speed unsigned long speed100m = wheelCurcum * 36000 / duration; // 100m per hour unsigned long speedkm = speed100m / 10; // km per hour unsigned long speedRem = speed100m - (speedkm * 10); // the bit after the decimal point
Making a Text String
Turning the calculated distance and speed into a text string for display is not so straight forward; the Arduino needs a little help when it comes to handling text strings. The simplest way to get help is to use the stdio.h library (Standard Input and Output Library). Libraries add functionality to the Arduino programming environment, this one adds read and write functionality. We will use the write functionality to write data to a string. To get access to the library use:
#undef int() #include <stdio.h>
The #undef int() line is needed because version 0011 of the Arduino software breaks the stdio libraries functionality (see this forum thread for details).
The text string (which in Arduino land is just an array of characters) can then be constructed using the sprintf() function. The line of code looks like this:
n = sprintf(textString, "%ld.%ld km %ld.%ld km/hr \n", distancekm, distanceRem, speedkm, speedRem);
The only thing to be careful of is to make the textString array large enough to hold all the characters (otherwise your data will overrun into other memory space).
It may be possible to use the Arduino TextString library instead of stdio.h or the itoa() function with no additional libraries but these paths seem more complex (if we start to run out of memory space then the itoa() option may be worth exploring).
A Better Timer
Although the text is timed to remain static on the wheel I noticed an irregularity when the frame duration (the time allocated for each column of a letter) moved past 1000 microseconds. The problem turned out to be related to the millis() function I was using to calculate elapsed time. millis() returns the time in milliseconds (1 millis = 1000 microsec) and this isn't accurate enough to keep the display stable. The solution was to get the elapsed time in microseconds, this forum post showed me how. It requires access to the timer's overflow_count. The relevant code is as follows:
extern volatile unsigned long timer0_overflow_count;
unsigned long microSeconds(void)
{
return ((timer0_overflow_count << 8) + TCNT0)*4;
}
I replaced all the millis() calls in my code with microSeconds() and the text display is now consistently static at all speeds.
Positioning the Text
All versions of the code, to this point, start (and restart) the text display when the magnetic sensor is triggered. This means that the texts location on the wheel is determined by where the magnet on the bikes frame is located. This is not ideal because the text can become obstructed by the frame. To prevent this I decided to introduce a text offset value. This will offset the start of the the text from the magnets location by a given amount. The offset variable replaces the sensor flag boolean.
The new code with distance and speed display, improved timing, and text offset is available here: Bike_POV_Beta_5
Dead Ends (and the meaning of life)
I though it would be a good idea to re-write the code so that the display text is stored in an array of bytes. This would simplify the text display to a matter of stepping forward or backward through the array ('backward' is important because it will let us print to both sides of the wheel). However, I ran into problems; it appears that I've run out of memory. It is a lot more memory efficient to convert the characters 'on the fly' then to store the conversion in an array.
The Arduino behaves quite erratically when you overload it's memory; sometimes it just goes dead, other times data seems to overflow into other areas of memory giving unusual results. The first time I ran the code I got this:
The Answer to Life, the Universe, and Everything.
This code fork is available at Bike_POV_Bytes_Beta_1. I may return to it some time.
< Adding a Sensor | Storing Data >

