3

I want to log GPS positions to an external EEPROM over I2C.

For example: Latitude: 51.0000 Longitude: 4.0000

If I remove the decimal place then each of these addresses would consume up to two bytes each.

My question is: How can I log each new set of GPS data to a new memory address on the EEPROM each time?

Bearing in mind that the Arduino might be switched off between entries.

4 Answers 4

6

The trick you are looking to implement here is the ability to look through the EEPROM and say "This is the last place I wrote to - I will write to the next place now."

There's two ways of doing it, the easy way and the right way.

The easy way is to maintain a single address in the EEPROM that says where you wrote to last. If you are trying to implement some form of wear levelling to extend the life of your EEPROM then this would basically negate it, since that one cell in the EEPROM will wear out before the rest of it and become useless. So it's really not a good idea although it may sound like it at first.

The right way to do it is to store more information in the EEPROM every time. Not only do you need to store the data, but also some kind of identifying sequence number.

Say you have 4 bytes of data to write each time. Add a fifth byte to it. That fifth byte increases by one each time you write. Before you write you scan through the EEPROM looking at every fifth byte looking for the "end" of the sequence.

For instance, you may end up with (xx is your data bytes) in hexadecimal:

00: 00 xx xx xx xx
05: 01 xx xx xx xx
0A: 02 xx xx xx xx
0F: 03 xx xx xx xx
14: FF FF FF FF FF

You see that at address 0x14 the nice sequence of numbers breaks. Therefore your next write must be at that point. Now, if you then loop through the EEPROM many times after a while, you may end up with something that looks like this:

00: FE xx xx xx xx
05: FF xx xx xx xx
0A: 00 xx xx xx xx
0F: 01 xx xx xx xx
14: 58 xx xx xx xx

You can see a clear sequence here of 0xFE 0xFF 0x01 where they increase by 1 each time (0xFF + 1 = 0x00) so you can see that you have looped through your sequence numbers. You don't care though because it's still just plus one. Only when you get the jump from 0x01 to 0x58 does the sequence break, so you know where the last write would have been.

There are only a couple of small gotchas here:

  1. You can only use 5*n cells in the EEPROM since the last 5-byte packet of data may not fit within the last bit of EEPROM (say it's a 1024 byte EEPROM you would have room for 204.8 packets - so 204 packets and 4 bytes left over).
  2. You must ensure that you don't end up with exactly 256 packets fitting in the EEPROM before you loop the address - otherwise the sequence won't "break" so you will never know where the last write was after the first loop.
7

You can divide EEPROM space into entries and provide each entry with a single "used" bit. You could use a separate byte to store it, or reuse a bit from a byte which is not completely used by your data.

Suppose you start with EEPROM willed with zeroes, and you decide that the first byte of each entry holds the "used" bit in the LSb. When you log coordinates the first time, you'll have:

 /---"used"
01 51 00 00 04 00 00
00 00 00 00 00 00 00
 \---"free"
...
00 00 00 00 00 00 00

(of course, you don't have to store GPS coordinates as BCD, it's just an illustration).

Note that as you add entries, it's trivial to keep track of the last one: just check the entries in sequence until you find a "free" one and go back one step. Also note that the first entry is always "used".

This will go on until all your entries are "used":

01 51 00 00 04 00 00
01 52 00 00 05 00 00
...
01 53 00 00 06 00 00

At this point (when you have to write new data but there are no "free" entries), you start writing from the first entry again, inverting the value of the "used" bit:

 /---"used"
00 54 00 00 07 00 00
01 02 00 00 05 00 00
 \---"free"
...
01 53 00 00 06 00 00

(remember that the first entry is always "used"? Thanks to this, it's possible to simply swap the values representing "used" and "free" entries without actually erasing anything).

To sum up, the algorithm is:

  • read the "used" bit from the first entry
  • scan entries until the value of the "used" bit changes, or until you reach the end.
  • to read the last logged data, use the last "used" entry
  • to write new data, use the first "free" entry. If there are no "free" entries, start from the beginning, inverting the "used" bit.
2
  • 1
    This seems like the best answer here. "Dirty" might not be the best word for the currently valid data, though.
    – endolith
    Commented Feb 2, 2023 at 19:49
  • 2
    @endolith Thanks, I replaced "dirty" with "used" and "clean" with "free". Commented Feb 6, 2023 at 12:12
1

You could assign the first byte(s) of the EEPROM as the address of the next free location in which to store GPS coordinates, and update it before the Arduino sleeps. At wake-up, retrieve the address.

You could keep the pointer in RAM, too; RAM keeps its contents across a sleep. But using EEPROM would even let you power off the Arduino, and still pick up where you left off after the power-up reset. (Of course, other parts of your program would have to be restart-tolerant as well; I'm just looking at the coordinate storage here).

As an aside, two bytes isn't sufficient to store a GPS coordinate to four decimals unless their range is limited such that you can scale them into [0 - 65535].

1
  • 2
    Using the same bytes and writing into the same location often will render those bytes soon broken. They have a limited write amount.
    – Avamander
    Commented Oct 11, 2015 at 20:53
0

The question asks specifically about EEPROM (E2) and many of the answers point out or attempt to address E2's main limitation: limited # of write operations. Not mentioned is E2's slow speed. Its is best suited for storing infrequently modified data such as-built- or setup parameters such device calibration data and features of a device's external operating environment. Its only real advantage over other solutions is that it may already exist on your MCU - Atmega 328, for instance.

The new(er) kid on the block for non-volatile data storage is Ferroelectric RAM - FRAM. Write times are on the order of 30 times faster, at 150ns, and endurance is claimed to be 10^12 writes, or 10 million TIMES the endurance of E2. I've built a data logger that uses a ring-buffer in FRAM to store periodic data samples and write them on request.

The same techniques proposed in the answers for E2 will work equally well in FRAM with the additional advantage that you can ignore the complications meant to work around E2's limited write-endurance.

And by way of disclaimer: I don't sell this this stuff or have any other interest in it. But I do use it and it works like it says on the box.

Adafruit makes these on a breakout board in a couple of variations - size and serial-bus type - that are sold by them and other retailers. The link is for an 8K-byte, SPI-bus board.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.