Updated 4/10/16: Changed read_StringEE function with improved code
I have been working on a project, the same project from my Using an Arduino with C# post. Besides needing to send commands and settings to my Arduino I also needed to save them. For this, I decided to use the EEPROM with is built into the Arduino. Even though the EEPROM has a limited amount of times it can be written too, 100,000 to be precise. The amount of rights will never get that high.
As for the type of data I want to save, I’m mostly focused on Strings. In order to write Strings to EEPROM I went looking for code that could help. I found code from ediy.com.my which is based on the code the Arduino playground project EEPROM utility. The only issue I had with the code was the way it handled Strings.
I was not able to simply pass a Sting to a function with a starting address and then have it saved, nor could I read in a String from the EEPROM and store it in a Sting variable. So I wrote two additional function to be used with the EEPROM String functions.
The first function is called write_StringEE and all you need to do is pass it a String and the starting address you want to use to save it.
1 2 3 4 5 6 7 8 |
//Takes in a String and converts it to be used with the EEPROM Functions // bool write_StringEE(int Addr, String input) { char cbuff[input.length()+1];//Finds length of string to make a buffer input.toCharArray(cbuff,input.length()+1);//Converts String into character array return eeprom_write_string(Addr,cbuff);//Saves String } |
The second function is called read_StringEE and as the name implies it will return a String when you give it a starting address and length. You might ask how would you know the length of a string you are reading, for this I would save the length of a string also to the EEPROM in a set location then read that location first before reading the string its self.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
//Updated 4/10/16 cast type replaces concat //Given the starting address, and the length, this function will return //a String and not a Char array String read_StringEE(int Addr, int length) { char cbuff[length+1]; eeprom_read_string(Addr, cbuff, length+1); String stemp(cbuff); return stemp; } //Below shows old slower function String read_StringEE(int Addr,int length) { String stemp=""; char cbuff[length]; eeprom_read_string(Addr,cbuff,length); for(int i=0;i<length-1;i++) { stemp.concat(cbuff[i]);//combines characters into a String delay(100); } return stemp; } |
An example of how I would store all the data would be as follows, at memory location 0 I would store the length of the sting I want to save plus one which would be used for the string terminator. Starting at memory location 1 I would start saving my String. This way I always know how long the String I want is for when I need to read it back. If I wanted to store more the one string, I would follow the same idea. If I had two strings, then the first two memory locations would hold the lengths of each string plus one. Then to read the strings back I would start at the third memory location plus the length, for the second string I would find its memory location by taking the length of the first string plus one.
An example would be, if I had a string of length 3 called A and a string of length 4 called B, I would store 3 into memory location 0 and 4 into memory location 1. When I go to save these Strings to the EEPROM I would store A in memory location 2 and I would store B in memory location 6. The way I arrived at using memory location 6 was by taking the length of String A plus its string terminator and adding it to the starting address of String A, so 2+4=6. When reading the information back you do the same. You start by reading in the lengths from the EEPROM then start at memory location 2. (You need to make some assumptions when reading back the data unless you used a memory location to specify the start of the saved strings but that’s outside of this example.) Once you have the lengths you can read in the first String using the String read function by giving it the memory location and length. To read the second string give the String read function the length of the string and for the address, add the start location of the first string plus the length (2+4=6, four includes the string terminator). Now you have have different Strings of different lengths saved to the EEPROM and they can be read back dynamically.
Here is a diagram to help explain:
This is all the other functions from ediy.com.my
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
//http://ediy.com.my/index.php/tutorials/item/68-arduino-reading-and-writing-string-to-eeprom // Returns true if the address is between the // minimum and maximum allowed values, false otherwise. // // This function is used by the other, higher-level functions // to prevent bugs and runtime errors due to invalid addresses. boolean eeprom_is_addr_ok(int addr) { return ((addr >= EEPROM_MIN_ADDR) && (addr <= EEPROM_MAX_ADDR)); } // Writes a sequence of bytes to eeprom starting at the specified address. // Returns true if the whole array is successfully written. // Returns false if the start or end addresses aren't between // the minimum and maximum allowed values. // When returning false, nothing gets written to eeprom. boolean eeprom_write_bytes(int startAddr, const byte* array, int numBytes) { // counter int i; // both first byte and last byte addresses must fall within // the allowed range if (!eeprom_is_addr_ok(startAddr) || !eeprom_is_addr_ok(startAddr + numBytes)) { return false; } for (i = 0; i < numBytes; i++) { EEPROM.write(startAddr + i, array[i]); } return true; } // Writes a string starting at the specified address. // Returns true if the whole string is successfully written. // Returns false if the address of one or more bytes fall outside the allowed range. // If false is returned, nothing gets written to the eeprom. boolean eeprom_write_string(int addr, const char* string) { int numBytes; // actual number of bytes to be written //write the string contents plus the string terminator byte (0x00) numBytes = strlen(string) + 1; return eeprom_write_bytes(addr, (const byte*)string, numBytes); } // Reads a string starting from the specified address. // Returns true if at least one byte (even only the string terminator one) is read. // Returns false if the start address falls outside the allowed range or declare buffer size is zero. // // The reading might stop for several reasons: // - no more space in the provided buffer // - last eeprom address reached // - string terminator byte (0x00) encountered. boolean eeprom_read_string(int addr, char* buffer, int bufSize) { byte ch; // byte read from eeprom int bytesRead; // number of bytes read so far if (!eeprom_is_addr_ok(addr)) { // check start address return false; } if (bufSize == 0) { // how can we store bytes in an empty buffer ? return false; } // is there is room for the string terminator only, no reason to go further if (bufSize == 1) { buffer[0] = 0; return true; } bytesRead = 0; // initialize byte counter ch = EEPROM.read(addr + bytesRead); // read next byte from eeprom buffer[bytesRead] = ch; // store it into the user buffer bytesRead++; // increment byte counter // stop conditions: // - the character just read is the string terminator one (0x00) // - we have filled the user buffer // - we have reached the last eeprom address while ( (ch != 0x00) && (bytesRead < bufSize) && ((addr + bytesRead) <= EEPROM_MAX_ADDR) ) { // if no stop condition is met, read the next byte from eeprom ch = EEPROM.read(addr + bytesRead); buffer[bytesRead] = ch; // store it into the user buffer bytesRead++; // increment byte counter } // make sure the user buffer has a string terminator, (0x00) as its last byte if ((ch != 0x00) && (bytesRead >= 1)) { buffer[bytesRead - 1] = 0; } return true; } |