Spoke-o-dometer Beta 8
From Open Source Urbanism
Code for the Arduino spoke-o-dometer project
Status: working
Environment: Arduino 0011 (not working on version 12)
Download Arduino sketch: spoke-o-dometer Beta 8.zip
Or copy the following code into the Arduino programming environment (use the font.h code from Bike_POV_Beta_4).
//============================================================ // spoke_o_dometer // // by Scott Mitchell // www.openobject.org // Open Source Urbanism // // Copyright (C) 2008 Scott Mitchell 12-10-2008 // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // A copy of the GNU General Public License // can be found at <http://www.gnu.org/licenses/>. // // B8.0 // Last Modified: November 4, 2008 //============================================================ #undef int() // fixes a bug with the stdio.h #include <stdio.h> // gives access to function sprintf #include <EEPROM.h> //Needed to access the eeprom read write functions // defining the alphabet #include "font.h" // get access to better timekeeping. ref: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1193623343 extern volatile unsigned long timer0_overflow_count; // define the Arduino LED pins in use. const byte LEDpinsRHS[] = { 2,3,4,5,6,7,8}; const byte LEDpinsLHS[] = { 15,14,13,12,11,10,9}; // use #define were posible to reduce memory load // sensor setup #define sensorPIN 5 // define the Arduino sensor pin #define mean 508 // sensor at rest (no magnet) #define sensitivity 30 // sensor sensitivity // EEPROM setup #define saveSwitchPin 16 // digital pin for save button #define memLoc 168 // EEPROM memory address - any number between 0 and (512 - 4) unsigned long storedMetres; // the distance in metres as stored in memory // number of LEDs #define charHeight 7 #define charWidth 5 // set up display parameters #define numChars 30 // number of characters in the display (increasing this number will condense char width) #define letterSpace 4 // number of frames between characters const int numFrames = (numChars + 5) * (charWidth + letterSpace); // number of frames per rotation with buffer of 5 characters to prevent truncation #define RtextOffset 200 // right hand text start distance from magnet in frames #define LtextOffset 120 // left hand text start distance from magnet in frames int RtextPoss = 0; // the possition in the text string - right side int LtextPoss = numChars - 1; // the possition in the text string - left side int RcharIndex = 0; // the font index number for the current caracter - right side int LcharIndex = 0; // the font index number for the current caracter - left side int RbyteIndex = 0; // the byte index number for the current caracter byte - right side int LbyteIndex = charWidth - 1; // the byte index number for the current caracter byte - left side unsigned long frameDuration = 1000; // time for each frame in microseconds - updated from rotation speed unsigned long startTime = microSeconds(); // start time for wheel rotation in microseconds unsigned long RresetTime = 5000000; // reset time for text in microseconds (5 sec) unsigned long LresetTime = 5000000; // reset time for text in microseconds (5 sec) // distance and speed variables (floats are avoided because they slow the Arduino down) unsigned long numWheelRotations; // the base from which all other data is calculated #define wheelCircum 1100 // bike wheel circumference in mm (adjust this to match your bike) // text string char textString[numChars] = "HELLO"; // declared large enough to hold the full string void setup() { int i; for (i = 0; i<charHeight; i++){ pinMode(LEDpinsRHS[i], OUTPUT); // set each pin as an output digitalWrite(LEDpinsRHS[i], HIGH); // turns the LEDs off pinMode(LEDpinsLHS[i], OUTPUT); // set each pin as an output digitalWrite(LEDpinsLHS[i], HIGH); // turns the LEDs off } pinMode(saveSwitchPin, INPUT); // set the save button as an input digitalWrite(saveSwitchPin, HIGH); // sets the pull-up resistor // should we clear the EEPROM memory? i=0; while(digitalRead(saveSwitchPin) == LOW){ // is the save switch being pressed? cycleLEDs(HIGH, 20); // indicate the button press and take care of debounce cycleLEDs(LOW, 20); i++; if(i>5){ // if the button is held for longer than 4 cycles EEPROMWriteInt(memLoc, 0); // write 0 to the memory location allLEDs(LOW); // indicate a clear has taken place delay(1000); allLEDs(HIGH); break; // exit the while loop } } // set the number of wheel rotations from data stored in EEPROM memory storedMetres = EEPROMReadInt(memLoc); // EEPROM value is stored in metres numWheelRotations = (storedMetres * 1000)/ wheelCircum; // metres converted to number of wheel rotations } void loop() { byte Rbyte, Lbyte; unsigned long currentTime = microSeconds(); // check the reset time if (currentTime > RresetTime){ RtextPoss = 0; RcharIndex = charToFontIndex(textString[RtextPoss]); RbyteIndex = 0; RresetTime = currentTime + 50000000; // set to reset in 5 seconds. } if (currentTime > LresetTime){ LtextPoss = numChars - 1; LcharIndex = charToFontIndex(textString[LtextPoss]); LbyteIndex = charWidth - 1; LresetTime = currentTime + 50000000; // set to reset in 5 seconds. } // get the RHS character byte for printing if (RbyteIndex < charWidth){ Rbyte = font[RcharIndex][RbyteIndex]; } else if (RbyteIndex < (charWidth + letterSpace)){ Rbyte = font[0][0]; // blank space } else { // move to next character in text string (if there is one) if (RtextPoss < numChars -1){ RtextPoss++; RcharIndex = charToFontIndex(textString[RtextPoss]); RbyteIndex = 0; // reset the byte index Rbyte = font[RcharIndex][RbyteIndex]; } else { Rbyte = font[0][0]; } } // get the LHS character byte for printing if (LbyteIndex >= 0){ Lbyte = font[LcharIndex][LbyteIndex]; } else if (LbyteIndex >= (0 - letterSpace)){ Lbyte = font[0][0]; // blank space } else { // move to previous character in text string (if there is one) if (LtextPoss > 0){ LtextPoss--; LcharIndex = charToFontIndex(textString[LtextPoss]); LbyteIndex = charWidth - 1; // reset the byte index Lbyte = font[LcharIndex][LbyteIndex]; } else { Lbyte = font[0][0]; // blank space } } // print the current byte printByte(Rbyte, Lbyte); // pause for frame duration pauseFrame(1); // clear LEDs allLEDs(HIGH); // increment/decrement the byte count RbyteIndex++; LbyteIndex--; // check for save button press int i=0; while(digitalRead(saveSwitchPin) == LOW){ // is the save switch being pressed? cycleLEDs(HIGH, 20); // indicate the button press and take care of debounce i++; if(i>3){ // if the button is held for longer than 3 cycles unsigned long distancem = numWheelRotations * wheelCircum / 1000; // calculate the distance in metres if(storedMetres != distancem){ // if it hasn't already been stored EEPROMWriteInt(memLoc, distancem); // write the metres to memory storedMetres = distancem; allLEDs(LOW); // indicate a save has taken place delay(500); allLEDs(HIGH); } break; // exit the while loop } } } void printByte(byte Rb, byte Lb) { // bit shift through the byte and output it to the pin for (int i=0; i<charHeight; i++) { digitalWrite(LEDpinsRHS[i], !(Rb & (1 << i))); digitalWrite(LEDpinsLHS[i], !(Lb & (1 << i))); } } void pauseFrame(int frames){ unsigned long currentTime; int sensVal; // variable to store the value coming from the sensor unsigned long endTime = microSeconds() + (frames * frameDuration); // calculate the end time while(microSeconds() < endTime){ // check the sensor sensVal = analogRead(sensorPIN); // read the Hall Effect Sensor if (sensVal > (mean + sensitivity) || sensVal < (mean - sensitivity)) { currentTime = microSeconds(); // in microseconds if(currentTime > (startTime + (20 * frameDuration))){ // de-bounce the sensor reading // recalculate frameDuration frameDuration = (currentTime - startTime) / numFrames; // recalculate frameDuration if(frameDuration > 10000) frameDuration = 10000; // limit the frame duration to a reasonable time // set the reset time RresetTime = currentTime + (RtextOffset * frameDuration); LresetTime = currentTime + (LtextOffset * frameDuration); numWheelRotations++; // add 1 to the number of wheel rotations setTextString(currentTime - startTime); // reset the display text } startTime = currentTime; // reset the start time } } } int charToFontIndex(char ch) { // make sure the character is within the alphabet bounds (defined by the font.h file) // if it's not, make it a blank character if (ch < 32 || ch > 126) ch = 32; // converts the ASCII number to the font index number return (ch - 32); } void setTextString(unsigned long duration){ int n; // number of char written to the string (not used) // calculate distance unsigned long distancem = numWheelRotations * wheelCircum / 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 // mm / microsecond x 1000 is mm / millisecond // mm / millisecond is equivalent to m / sec // m / sec x 60 x 60 is m / hr // m / hr / 1000 is km / hr unsigned long speed100m = wheelCircum * 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 // print a new text string into the variable n = sprintf(textString, "%ld.%.3ld km %ld.%.ld km/hr \n", distancekm, distanceRem, speedkm, speedRem); } // get an accurate time reading. ref: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1193623343 unsigned long microSeconds(void) { return ((timer0_overflow_count << 8) + TCNT0)*4; } //This function will write a 4 byte long integer to the eeprom from the specified address void EEPROMWriteInt(int p_address, long p_value) { byte Byte1 = ((p_value >> 0) & 0xFF); byte Byte2 = ((p_value >> 8) & 0xFF); byte Byte3 = ((p_value >> 16) & 0xFF); byte Byte4 = ((p_value >> 24) & 0xFF); EEPROM.write(p_address, Byte1); EEPROM.write(p_address + 1, Byte2); EEPROM.write(p_address + 2, Byte3); EEPROM.write(p_address + 3, Byte4); } //This function will read a 4 byte long integer from the eeprom from the specified address unsigned long EEPROMReadInt(int p_address) { byte Byte1 = EEPROM.read(p_address); byte Byte2 = EEPROM.read(p_address + 1); byte Byte3 = EEPROM.read(p_address + 2); byte Byte4 = EEPROM.read(p_address + 3); // bit shifting left more than 8 places wont work because the result gets truncated // so do this in two stages long firstTwoBytes = ((Byte1 << 0) & 0xFF) + ((Byte2 << 8) & 0xFF00); long secondTwoBytes = (((Byte3 << 0) & 0xFF) + ((Byte4 << 8) & 0xFF00)); secondTwoBytes *= 65536; // multiply by 2 to power 16 - bit shift 24 to the left return (firstTwoBytes + secondTwoBytes); } void allLEDs(int newState) { // turn all the LEDs ON or OFF for (int i=0; i<charHeight; i++) { digitalWrite(LEDpinsRHS[i], newState); digitalWrite(LEDpinsLHS[i], newState); } } void cycleLEDs(int LEDdirection, int LEDdelay) { int i; // turn all the LEDs ON and OFF in a cycle if(LEDdirection == HIGH){ for (i=0; i<charHeight; i++) { digitalWrite(LEDpinsRHS[i], LOW); // turn LED on digitalWrite(LEDpinsLHS[i], LOW); // turn LED on delay(LEDdelay); digitalWrite(LEDpinsRHS[i], HIGH); // turn LED off digitalWrite(LEDpinsLHS[i], HIGH); // turn LED off } } else { for (i=charHeight-1; i>=0; i--) { digitalWrite(LEDpinsRHS[i], LOW); // turn LED on digitalWrite(LEDpinsLHS[i], LOW); // turn LED on delay(LEDdelay); digitalWrite(LEDpinsRHS[i], HIGH); // turn LED off digitalWrite(LEDpinsLHS[i], HIGH); // turn LED off } } }

