openobject.org

Storing Data

From Open Source Urbanism

If the project is to meet it's goal of recording the total distance travelled over an extended period of time then this data needs to be stored in a non-volatile location (were information persists after the power is turned off). The Arduino has three pools of memory as detailed on this page. The area of interest to us is the 512 byte EEPROM. This space can be used to store long-term information.

Contents

Adding a Save Button

The EEPROM has a limit life span of 100000 write/erase cycles, so accessing the memory should be kept to a minimum. It would be preferable if data was only written to the memory when requested by the user. To facilitate this I decided to add a save button to the Arduino shield.

Initially I installed the button on pin 0 however it was interfering with the USB connection so I shifted it to pin 7 and shuffled the LEDs down a pin. It turned out that an LED on pin 0 also disturbs the USB connection so that move didn't actually achieve anything, but the upshot is the pins are now defined as follows:

pins 0-6: LED via 100 Ohm resistor to ground.
pin 7: push switch connected to 5V with pull-down resistor to ground.

The shield now looks like this:

Pressing the button while the Arduino is running will save your distance measurement to the EEPROM. When the Arduino is turned on at some latter time it will read the EEPROM and retrieve your distance measurement.

Writing to the EEPROM

To access the EEPROM you need to use the EEPROM library. The library is activated by including the following at the top of the sketch:

#include <EEPROM.h>

The library consists of two function calls: EEPROM.write(address, value) and EEPROM.read(address). The address specifies the memory address you will be writing to, it must be in the range of 0 - 512. The value is the byte of data you want to save. You write data to the EEPROM one byte at a time. A byte is capable of storing 256 values. If we are going to record the number of metres then we need to use more than one byte. Two bytes will allow us to store an Arduino integer (any number up to 32,767), four bytes are required to store a long integer. A long integer will let us count to 2,147,483,647 - that should be plenty of metres (the Arduino site has a good introduction to binary maths here).

Following Marc's example I used this code to separate a long int into 4 bytes and save then to the EEPROM.

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);
}

Getting the bytes back together was a little more difficult. If you shift the byte more than 8 bits to the left then you start to truncate the data (as explained here). The solution I found was to work with two bytes at a time and then combine the results.

{
  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);

  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);
}

Zeroing the Data

The distance measurement is read from the EEPROM during the setup() function when the Arduino is powered up however, the code has no way of knowing if this is the first time it has been run. If it is the first time then it is reading data that we didn't store. This wouldn't be such a problem if the EEPROM memory came zeroed by default but I've found it can contain random bits of data that give wierd starting distances. To compensate for this I created a reset option; if the save switch is held down for 3 seconds while the Arduino is starting up then the distance value is zeroed. This code, housed within the setup() function, handles the distance reset.

 int j=0;
 while(digitalRead(saveSwitchPin) == HIGH){  // is the save switch being pressed?
   cycleLEDs(HIGH, 20); // give some visual feedback via the LEDs
   cycleLEDs(LOW, 20); 
   j++;
   if(j>5){ // if the button is held for more than 4 cycles
     EEPROMWriteInt(memLoc, 0); // write 0 to the memory location
     allLEDs(HIGH); // indicate a clear has taken place
     delay(1000);
     allLEDs(LOW);
     break;  // exit the while loop
   }

Visual Feedback

Visual feedback is given to the user via the POV LEDs. The cycleLEDs() and allLEDs() functions are used to provide this. They are defined as follows:

void allLEDs(int newState)
{
  // turn all the LEDs ON or OFF
  for (int i=0; i<charHeight; i++) {
    digitalWrite(LEDpins[i], newState);
  }
}
void cycleLEDs(int LEDdirection, int LEDdelay)
{
  int i;
  if(LEDdirection == HIGH){
    for (i=0; i<charHeight; i++) {
      digitalWrite(LEDpins[i], HIGH);
      delay(LEDdelay);
      digitalWrite(LEDpins[i], LOW);
    } 
  } 
  else {
    for (i=charHeight-1; i>=0; i--) {
      digitalWrite(LEDpins[i], HIGH);
      delay(LEDdelay);
      digitalWrite(LEDpins[i], LOW);
    } 
  }
}

These functions are called when clearing and saving the data. When data is being cleared the LEDs cycle on and off in a knight rider fashion; when data is being saved they pulse in one direction only. A sustained pulse with all LEDs on lets you know the process has finished (if the distance value is identical to the one already stored in memory then the data is not re-saved and there is no sustained pulse).

The new code is available here: Bike_POV_Beta_6


< Counting Revolutions | Conserving Power >