Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 9 of 9

Thread: Storing strings in EEPROM and data changing after power cycle

  1. #1
    Junior Member
    Join Date
    Aug 2020
    Posts
    6

    Storing strings in EEPROM and data changing after power cycle

    Hello,

    As the title says, I have been using EEPROM.put/get to store a string in the EEPROM of a 3.2. When later power cycling, the string completely changes value. Any ideas? I'm speculating that maybe all the values get left/right shifted?

    Code:
    #include <StreamMessageHandler.h>
    #include <EEPROM.h>
    
    StreamMessageHandler USBSERIAL;
    char testWordread[10];
    String testWord;
    
    void setup() {
      // put your setup code here, to run once:
    Serial.begin(115200);
    USBSERIAL.registerState(ping,"ping");
    USBSERIAL.registerState(readName,"readname");
    
    
    }
    
    void loop() {
      // put your main code here, to run repeatedly:
      USBSERIAL.run();
    }
    
    state ping() {
      Serial.println("pong");
      testWord = USBSERIAL.parameter(0);
      EEPROM.put(0,testWord.c_str());
    }
    
    state readName() {
      EEPROM.get(0,testWordread);
      testWord = String(testWordread);
      Serial.println(testWord);
    }
    Serial:
    Code:
    ping::aaaaaaaa
    pong
    readname //before power cycle
    aaaaaaaa
    readname // after power cycle
    YzTb
    ping::aaaaaaa
    pong
    readname //before
    aaaaaaa
    readname
    YzyT

  2. #2
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    13,204
    Not sure the String.c_str() is giving proper bytes to write info? :: The number of bytes written is related to the datatype or custom structure of the variable to be written >> www.arduino.cc/en/Tutorial/EEPROMPut

    The example there shows a 'c' null term string in a struct that works - but the compiler knows the size of the struct. Any null term string of 'c' style or from .c_str() may not provide the needed sizeof() info for the write if done at compile time.

    @mjs513 has written code for FRAM's that may show it working for 'c' style - - but the .c_str() won't have that.

    On Read/.get() it is read to 'c' string memory then converted. Try doing that the other way [ strcpy (cstr, str.c_str()); ] on the .put() may allow it to resolve the length to .put()

  3. #3
    Junior Member
    Join Date
    Aug 2020
    Posts
    6
    I will give it a try with strcpy, but also felt it's worth adding that I tried to get this to work after having originally done it with String type:
    Code:
    #include <StreamMessageHandler.h>
    #include <EEPROM.h>
    
    StreamMessageHandler USBSERIAL;
    
    String testWord;
    
    void setup() {
      // put your setup code here, to run once:
    Serial.begin(115200);
    USBSERIAL.registerState(ping,"ping");
    USBSERIAL.registerState(readName,"readname");
    
    
    }
    
    void loop() {
      // put your main code here, to run repeatedly:
      USBSERIAL.run();
    }
    
    state ping() {
      Serial.println("pong");
      testWord = USBSERIAL.parameter(0);
      EEPROM.put(0,testWord);
    }
    
    state readName() {
      EEPROM.get(0,testWord);
      Serial.println(testWord);
    }
    Serial:
    Code:
    ping::aaaaaaa
    pong
    readname
    aaaaaaa
    readname //after power cycle
    Yz€t‚

  4. #4
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    8,218
    If it were me, I would also try changing the put as well.
    Code:
    state ping() {
      Serial.println("pong");
      testWord = USBSERIAL.parameter(0);
      EEPROM.put(0,testWord);
    }
    That is the underlying system will see you passed in a pointer to string and maybe decide it is 4 bytes long...

    When I was debugging some of the EEPROM stuff on T4 and T4.1 beta, I did a sketch that dumped out the EEPROM data, but the values and actual underlying data...

    Also I would probably add in a little debug code to get an idea of what was really written... What I think was written was the address of your variable...

    So again if it were me, I would first try simple test to see what was really output to EEPROM. Like:

    Code:
    state ping() {
      Serial.println("pong");
      testWord = USBSERIAL.parameter(0);
      EEPROM.put(0,testWord);
    
      // Output debug information.
      Serial.println(testWord);
      Serial.println((uint32_t)&testWord, HEX);
    #define eeprom_size 64  // not the actual real size, but limit to only 64 bytes to start.
      for (uint16_t i = 0; i < eeprom_size; i++) {
        Serial.printf("%x:%x ", i, EEPROM.read(i));
    
        if ((i & 0xf) == 0xf) Serial.println();
      }
    }
    And if desired you could also output the ascii with the above prints, something like:
    Code:
      for (uint16_t i = 0; i < eeprom_size; i++) {
        uint8_t ch = EEPROM.read(i);
        Serial.printf("%x:%x ", i, (ch >= ' ' && ch <= '~')? ch : '.', ch);
    
        if ((i & 0xf) == 0xf) Serial.println();
      }
    Again typed in on fly but should be reasonably close...

    And again I think it will show the actual data written is not what you think it is...

  5. #5
    Junior Member
    Join Date
    Aug 2020
    Posts
    6
    Thank you! Now I fully understand what you mean. Using strcpy as defragster suggested and knowing what data was actually stored in a 'String', I've now got it working. Thank you both again!

    (Working code if anyone else stumbles upon this issue:
    Code:
    #include <StreamMessageHandler.h>
    #include <EEPROM.h>
    
    StreamMessageHandler USBSERIAL;
    
    char nameArray[15];
    String usrString;
    
    void setup() {
      // put your setup code here, to run once:
    Serial.begin(115200);
    USBSERIAL.registerState(ping,"ping");
    USBSERIAL.registerState(readName,"readname");
    
    
    }
    
    void loop() {
      // put your main code here, to run repeatedly:
      USBSERIAL.run();
    }
    
    state ping() {
      Serial.println("pong");
      usrString = USBSERIAL.parameter(0);
      strcpy(nameArray,usrString.c_str());
      Serial.println(String(nameArray));
      EEPROM.put(0,nameArray);
    
    }
    
    state readName() {
      EEPROM.get(0,nameArray);
      Serial.println(String(nameArray));
    }

  6. #6
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    8,218
    You are welcome,

    Glad it is working

  7. #7
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,486
    I wonder if there is C++ template magic which could allow the EEPROM library to treat String objects in a smart/special manner? Would be really nice if this sort of code could "just work". Or alternately, maybe give an error or warning if it can't work?

  8. #8
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    13,204
    Glad the suggestion was usable! My time was short to investigate or try. As noted I know @mjs513 did some variety of writes to FRAM memory and allowed some things to work. I did not look to see if null term 'c' strings were one of the viable options.

    Just looked at :: github.com/mjs513/FRAM_MB85RC_I2C/blob/master/examples/FRAM_I2C_store_anythingv2/FRAM_I2C_store_anythingv2.ino

    Those writes are using a STRUCT with a 'c string' embedded like the linked Arduino.cc example in p#2.

  9. #9
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,328
    Quote Originally Posted by PaulStoffregen View Post
    I wonder if there is C++ template magic which could allow the EEPROM library to treat String objects in a smart/special manner? Would be really nice if this sort of code could "just work". Or alternately, maybe give an error or warning if it can't work?
    Actually there is not much magic needed. One simply has to specialize the templates for Arduino strings. Here my try. To test it, copy the code after the EEPromClass in EEPROM.h.

    Code:
    // Specialization for Arduino Strings
    template <>
    String &EEPROMClass::get(int idx, String &s)
    {
        s = ""; // just in case...
        EEPtr e = idx;
    
        char c = *e; // read in bytes until we find the terminating \0
        while (c != '\0')
        {
            s.append(c);
            c = *(++e);
        }
        return s;
    }
    
    // Specialization for Arduino Strings
    template <>
    const String &EEPROMClass::put(int idx, const String &s)
    {
        const uint8_t *ptr = (uint8_t *)s.c_str();
    
    #ifdef __arm__
        eeprom_write_block(ptr, (void *)idx, s.length() + 1); // length() doesn't account for the trailing \0
    #else
        EEPtr e = idx;
        for (int count = s.length() + 1; count; --count, ++e)
            (*e).update(*ptr++);
    #endif
        return s;
    }

    Then you can do things like:
    Code:
    #include "EEPROM.h"
    
    void setup()
    { 
      while (!Serial)  {  }
    
      // uncomment to write
      /*
      String s1 = "Hello string";    
      EEPROM.put(0, s1);  
      */
      
      String s2;
      EEPROM.get(0,s2);  
      Serial.println(s2);  
    }
    
    void loop()
    { 
    }
    which prints:
    Code:
    Hello string
    The reading into the string can probably be done more efficiently but I doubt if it is worth the effort... If you want, I can do a PR tomorrow but I have no chance to test the AVR part.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •