ESP8266 C FLASH Saving of Variables

While I’m off in search of a C one-wire library, has anyone saved any variables to FLASH (hence non-volatile) – and if so would they care to give the simplest C code example of how to do that?  I (and no doubt others) would like to save some strings and integers permanently or semi-permanently – or even perhaps some log info – we have loads of Flash but personally I’ve not idea which bits I can safely use or how to do the read and write….  So kind of like the equivalent of Arduino EEPROM library.

Please note – this information has now been superseded by the info on the new blog at https://tech.scargill.net – please visit the new blog.

28 thoughts on “ESP8266 C FLASH Saving of Variables

    • Ah, very good – well done. Just doing some last tests on the LUA code to see if I can spot where the problem is and if I get no-where, I’m loading this code up and starting another learning curve 🙂

  1. THISJO… I wonder if you can help. I went off to find the code you used – and it looks simple enough to convert – but the PORT assignments are confusing me.

    In the init code – is reference to GPIO2 – but then the function is passing a port address – why would it do that if it has already hardcoded GPIO2 – and what number should one pass to that function for GPIO2? Ideas?

    void ds_init( int gpio )
    {
    //set gpio2 as gpio pin
    PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);

    //disable pulldown
    PIN_PULLDWN_DIS(PERIPHS_IO_MUX_GPIO2_U);

    //enable pull up R
    PIN_PULLUP_EN(PERIPHS_IO_MUX_GPIO2_U);

    // Configure the GPIO with internal pull-up
    // PIN_PULLUP_EN( gpio );

    GPIO_DIS_OUTPUT( gpio );

    gpioPin = gpio;

    reset_search();
    }

  2. Reading/writing to flash is quite easy. You need 3 functions:
    – spi_flash_erase_sector()
    – spi_flash_write()
    – spi_flash_read()

    You have to erase whole sector at once (which is 4kb large) before writing. Then you can read/write like this:

    int i;
    spi_flash_read(0x3D000, &i, sizeof(int));
    i++;
    spi_flash_erase_sector(0x3D);
    spi_flash_write(0x3D000, &i, sizeof(int));

    Now I have used first 4 bytes of sector 0x3D, why? If you’re using typical layout with only one application (not the one designed for OTA updates), you will have 248k of space (0x3E000) reserved for app.v6.flash.bin. Assuming your application is not that large, you can use last 4kb available (or more, if needed) for your own storage.

    So the only inconvenience is that you have to erase/write in 4kb blocks so you might want to create some wrapper for that. But that’s quite usual for flash, right?

    Hope this helps.

    • That is more than helpful. Do I take it the ready and write will take any size- so for example one could pass the address of and size of a struct rather than a simple int? Yes, it would apear that this is incredibly helpful.

      • You can read/write more than an int of data but I’m not sure what is the limit. I’ve seen someone using this for 1024 bytes. I think that one sector (4kb) may be the limit.

    • WELL that kept me going for a while until I realised that 0x3d000 was right in the middle of my program which started at 0x3c000 – actually it’s the configuration area. I finally changed the code above to 0x3a000 and it works a treat!! In the process we figured out how to add the blank512k.bin file into a project so now I have CLEAN, ALL, FLASH and BLANK512K buttons – needed that as the only way to get my program up and running again after that overwrite was to wipe the chip. I wonder how many people will fall fowl of that. Excellent

      • Sorry for that. This is why I explained where this address came from. But of course where your code is depends on the linker scripts you use so there is no place that will work for all people :/

      • It’s funny, it was all very murky and now it’s all quite clear.. the only thing I can’t get my head around is why people have code all over the place instead of starting from zero and working their way up 🙂

  3. Yeah, I saw that when I first looked at the code and thought it was strange and needed fixing. I think it works for me because I actually use GPIO2. I pass the integer 2 to the function and for me that works.

    But I think the code might be broken. I don’t think it will work passing anything else in.

    • Right thanks for that – yes I’m using GPIO2 – but clearly it would be good to get to the bottom of that so it can be attached to any GPIO… someone needs to write some macros so the ports are as easy to set up as Arduino. Right now they are un-necessary cryptic imho.

  4. Looking in ESP8266_SDK/include/eagle_soc.h I see:

    #define PERIPHS_IO_MUX_GPIO2_U (PERIPHS_IO_MUX + 0x38)

    and

    #define PERIPHS_IO_MUX 0x60000800

    So it looks like those functions at the start of ds_init want pin addresses. In that same file some other pins are defined too, so maybe just substitute those if you want another pin.

    #define PERIPHS_IO_MUX_GPIO0_U (PERIPHS_IO_MUX + 0x34)
    #define PERIPHS_IO_MUX_GPIO2_U (PERIPHS_IO_MUX + 0x38)
    #define PERIPHS_IO_MUX_GPIO4_U (PERIPHS_IO_MUX + 0x3C)
    #define PERIPHS_IO_MUX_GPIO5_U (PERIPHS_IO_MUX + 0x40)

  5. In my code the sequence is a bit like this:

    // perform the conversion
    reset();
    select(ds_addr);

    write(0x44, 1); // perform temperature conversion

    os_delay_us(1000000);

    reset();
    select(ds_addr);
    write(0xbe, 0); // read scratchpad

    uint8_t* p;
    p = data;
    for ( i = 0; i < 9; i++ ) {
    *p = read();
    p++;
    }

    After this the stuff I want is in 'data'…

    Seems to work pretty consistently. As you see, my sequence of writes is different (0x44, wait, then 0xbe). Don't know if the full second wait is needed, but I'm not in a hurry…

  6. The most imported thing you need the know is that you don’t have a flash controller here.
    This mean that you need to take care how you manage you erase/writing.
    1) You erase entire sector (4K), so you need to read and write all the data (not just the one you change).
    2) In this Flash unit you have (from the spec) around 100,000 erase/program cycles.
    A quick calculation, if you will write to the same place every 5 min , that will be the end of that sector (4K)!!! after one year. ( 12(write in hour) * 24 * 365 = 105,120 ).

  7. Useful info Ran. Yes, so clearly one wants to try and avoid writing more than once an hour. So clearly the routine will be to read your struct or whatever from the FLASH, make a change, erase, and write – no more than say once per hour. Of course this also means keeping your variables in precious RAM. A shame there isn’t a better way – EEPROM (as in Arduino) is so much nicer for non-volatile.

    • Where did you get this information. I thouht that those informations are stored on regular flash on 0x7e000 address. But I haven’t checked that myself yet.

    • I’m unaware of any EEPROM – I would have expected the details to be stored in FLASH as Krysztof suggests. I didn’t know the address but now I do… anyone care to write a little C routine to display this without using 4K of RAM which we don’t have?

    • There is only ROM and SRAM.
      As you must know the ROM is not for us,and can’t be changed.
      The SRAM is where the program runs.
      This is a short explanation:

      SRAM (static RAM) is random access memory (RAM) that retains data bits in its memory as long as power is being supplied. Unlike dynamic RAM (DRAM), which stores bits in cells consisting of a capacitor and a transistor, SRAM does not have to be periodically refreshed. Static RAM provides faster access to data and is more expensive than DRAM. SRAM is used for a computer’s cache memory and as part of the random access memory digital-to-analog converter on a video card.

      • Thanks for that – as it happens I know the full definition of RAM, but when you look at the memory map it does seem that it is very easy to run out of RAM despite their being plenty.. adding this to routines helps a lot.. ICACHE_FLASH_ATTR but doesn’t really explain why it is so easy to run out of RAM otherwise.

      • Thanks for that – as it happens I know the full definition of RAM, but when you look at the memory map it does seem that it is very easy to run out of RAM despite their being plenty.. adding this to routines helps a lot.. ICACHE_FLASH_ATTR but doesn’t really explain why it is so easy to run out of RAM otherwise.

        I had no time to really test that but as far as I understand this, ICACHE_FLASH_ATTR moves function code from .text to .irom0.text section. And I don’t see how this can influence now much memory is free. It may be needed as there is only 32KB of IRAM (which is separate from normal RAM (DRAM)) so you may run into situation where your code won’t fit there and you have to move it to flash.

        AFAIK DRAM is, however, used for .data, .rodata and .bss sections and ~40KB of those is used by SDK libs already.

        Or am I wrong here?

      • “adding this to routines helps a lot.. ICACHE_FLASH_ATTR ”

        In some CPU architecture there is section in the RAM that contain “impotent” function that will never be replaced. The second section is the place where the most recent function are placed and every time a new function is called it replaces the oldest (mostly).
        If they didn’t do a good job in the “swapping function” section (replacing function) then maybe that what causing the RAM to run out ?

        When you define a function as “ICACHE_FLASH_ATTR” I think it put the function in the “never remove this function from the RAM” section.
        (it has a lot of resemblance to my processor at work here we declare “RESIDENT”)

      • What you describe looks like IRAM vs ICACHE and we seem to have those obn esp8266 but the logic is reverse. Normal code goes to IRAM (of which we have 32KB). It’s loaded there by bootloader and stays forever. If you mark code with ICACHE_FLASH_ATTR it will go to flash and will be run thru ICACHE (thus it can be slower).

  8. Observant readers will note I’ve erased some of my comments in here regarding DS18B20 as I’ve just realised my stupid mistake. The relevant blog entry is updated accordingly.

  9. hello everyone..!! actually we are trying to store the persistent values/ranges of our sensor on esp8266….but can”nt get exact method to do so…it would be very much greatful if someone can help us to sort this issue…and we are using magnetometer as a sensor…:)

    • Hi there – wrong blog – this one is old and dead…. please go to http://tech.scargill.net – there is a function in the SDK for saving variables to FLASH reliably. Being flash of course it works a block at a time. For more on this please raise the subject in the above blog, not here. Thanks.

Leave a reply to Scargill Cancel reply