Skip to content

Instantly share code, notes, and snippets.

@joernassimian
Forked from sticilface/PROGMEM.md
Created April 25, 2017 04:31
Show Gist options
  • Select an option

  • Save joernassimian/ec4097f07fc09d8a250df88b503671d8 to your computer and use it in GitHub Desktop.

Select an option

Save joernassimian/ec4097f07fc09d8a250df88b503671d8 to your computer and use it in GitHub Desktop.

Revisions

  1. @sticilface sticilface revised this gist Mar 5, 2017. 1 changed file with 9 additions and 10 deletions.
    19 changes: 9 additions & 10 deletions PROGMEM.md
    Original file line number Diff line number Diff line change
    @@ -132,21 +132,20 @@ unsigned char String::concat(const __FlashStringHelper * str) {
    ### How do I declare a global flash string and use it?
    ```cpp
    static const char xyz[] PROGMEM = "This is a string stored in flash. Len = %u";
    static const char xyz[] PROGMEM = "This is a string stored in flash. Len = %u";
    void setup() {
    Serial.begin(115200);
    Serial.println();
    Serial.println( FPSTR(xyz) ); // just prints the string, must convert it to FlashStringHelper first using FPSTR().
    Serial.printf_P( xyz, strlen_P(xyz)); // use printf with PROGMEM string
    }
    void setup() {
    Serial.begin(115200); Serial.println();
    Serial.println( FPSTR(xyz) ); // just prints the string, must convert it to FlashStringHelper first using FPSTR().
    Serial.printf_P( xyz, strlen_P(xyz)); // use printf with PROGMEM string
    }
    ```

    ### How do I use inline flash strings?

    ```cpp
    void setup() {
    Serial.begin(115200);
    Serial.begin(115200); Serial.println();
    Serial.println( F("This is an inline string")); //
    Serial.printf_P( PSTR("This is an inline string using printf %s"), "hello");
    }
    @@ -162,7 +161,7 @@ const uint8_t xyz[] PROGMEM = {
    0x6c, 0x65, 0x20, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x00};

    void setup() {
    Serial.begin(115200);
    Serial.begin(115200); Serial.println();
    uint8_t * buf = new uint8_t[len_xyz];
    if (buf) {
    memcpy_P(buf, xyz, len_xyz);
    @@ -182,7 +181,7 @@ const uint8_t xyz[] PROGMEM = {
    0x6c, 0x65, 0x20, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x00};
    void setup() {
    Serial.begin(115200);
    Serial.begin(115200); Serial.println();
    uint8_t * buf = new uint8_t[len_xyz];
    if (buf) {
    memcpy_P(buf, xyz, len_xyz);
  2. @sticilface sticilface revised this gist Mar 5, 2017. 1 changed file with 28 additions and 26 deletions.
    54 changes: 28 additions & 26 deletions PROGMEM.md
    Original file line number Diff line number Diff line change
    @@ -136,6 +136,7 @@ unsigned char String::concat(const __FlashStringHelper * str) {
    void setup() {
    Serial.begin(115200);
    Serial.println();
    Serial.println( FPSTR(xyz) ); // just prints the string, must convert it to FlashStringHelper first using FPSTR().
    Serial.printf_P( xyz, strlen_P(xyz)); // use printf with PROGMEM string
    }
    @@ -147,46 +148,47 @@ unsigned char String::concat(const __FlashStringHelper * str) {
    void setup() {
    Serial.begin(115200);
    Serial.println( F("This is an inline string")); //
    Serial.printf_P( PSTR("This is an inline string using printf %s), "hello");
    Serial.printf_P( PSTR("This is an inline string using printf %s"), "hello");
    }
    ```

    ### How do I declare and use data in PROGMEM?

    ```cpp
    const size_t len_xyz = 32;
    const uint8_t xyz[] PROGMEM = {
    0x1F, 0x8B, 0x08, 0x08, 0xEC, 0x73, 0xBF, 0x57, 0x00, 0x03, 0x65, 0x64, 0x69, 0x74, 0x2E, 0x68,
    0x74, 0x6D, 0x00, 0xC5, 0x1B, 0xE9, 0x7A, 0xD3, 0x48, 0xF2, 0x7F, 0x9E, 0xA2, 0x47, 0xEC, 0x10}

    void setup() {
    Serial.begin(115200);
    uint8_t * buf = new uint8_t[len_xyz];
    if (buf) {
    memcpy_P(buf, xyz, len_xyz);
    Serial.write(buf, xyz_len); // output the buffer.
    }
    }
    const size_t len_xyz = 30;
    const uint8_t xyz[] PROGMEM = {
    0x53, 0x61, 0x79, 0x20, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20,
    0x74, 0x6f, 0x20, 0x4d, 0x79, 0x20, 0x4c, 0x69, 0x74, 0x74,
    0x6c, 0x65, 0x20, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x00};

    void setup() {
    Serial.begin(115200);
    uint8_t * buf = new uint8_t[len_xyz];
    if (buf) {
    memcpy_P(buf, xyz, len_xyz);
    Serial.write(buf, len_xyz); // output the buffer.
    }
    }
    ```
    ### How do I declare some data in PROGMEM, and retrieve one byte from it.
    Declare the data as done previously, then use `pgm_read_byte` to get the value back.
    ```cpp
    const size_t len_xyz = 32;
    const uint8_t xyz[] PROGMEM = {
    0x1F, 0x8B, 0x08, 0x08, 0xEC, 0x73, 0xBF, 0x57, 0x00, 0x03, 0x65, 0x64, 0x69, 0x74, 0x2E, 0x68,
    0x74, 0x6D, 0x00, 0xC5, 0x1B, 0xE9, 0x7A, 0xD3, 0x48, 0xF2, 0x7F, 0x9E, 0xA2, 0x47, 0xEC, 0x10}
    void setup() {
    Serial.begin(115200);
    for (uint8_t i = 0; i < len_xyz; i++) {
    uint8_t val = pgm_read_byte(xyz + i);
    Serial.print(val);
    }
    }
    const size_t len_xyz = 30;
    const uint8_t xyz[] PROGMEM = {
    0x53, 0x61, 0x79, 0x20, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20,
    0x74, 0x6f, 0x20, 0x4d, 0x79, 0x20, 0x4c, 0x69, 0x74, 0x74,
    0x6c, 0x65, 0x20, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x00};
    void setup() {
    Serial.begin(115200);
    uint8_t * buf = new uint8_t[len_xyz];
    if (buf) {
    memcpy_P(buf, xyz, len_xyz);
    Serial.write(buf, len_xyz); // output the buffer.
    }
    }
    ```

    ### In summary
  3. @sticilface sticilface revised this gist Mar 5, 2017. 1 changed file with 9 additions and 9 deletions.
    18 changes: 9 additions & 9 deletions PROGMEM.md
    Original file line number Diff line number Diff line change
    @@ -17,7 +17,7 @@ Which places the variable in the .irom.text section ie flash.

    The key to understanding PROGMEM is to understand how the strings are stored and then how they are retrieved from flash.
    ### Declare a global string to be stored in flash.
    ```
    ```cpp
    static const char xyz[] PROGMEM = "This is a string stored in flash";
    ```

    @@ -38,7 +38,7 @@ const char * abc = PSTR("Also Store this string in flash");
    }
    ```

    The above examples will store these strings in flash. To retrieve and manipulate flash strings is not straight forward as the esp8266 must read from flash in 4byte words. In the Arduino IDE for esp8266 there are several functions that can help retrieve strings from flash that have been stored using PROGMEM. Both of the examples above will give you a `const char *` back, how ever if you try to do anything with these pointers, without correct 32bit alignment you will get a seg fault and the esp will crash. You must read from the flash 32 bit aligned.
    The two examples above will store these strings in flash. To retrieve and manipulate flash strings is not straight forward as the esp8266 must read from flash in 4byte words. In the Arduino IDE for esp8266 there are several functions that can help retrieve strings from flash that have been stored using PROGMEM. Both of the examples above will give you a `const char *` back, however if you try to do anything with these pointers, without correct 32bit alignment you will get a seg fault and the esp will crash. You must read from the flash 32 bit aligned.

    ### Functions to read back from PROGMEM
    Which are all defined in [pgmspace.h](https://github.com/esp8266/Arduino/blob/master/cores/esp8266/pgmspace.h)
    @@ -65,7 +65,7 @@ int snprintf_P(char *str, size_t strSize, PGM_P formatP, ...);
    int vsnprintf_P(char *str, size_t strSize, PGM_P formatP, va_list ap);
    ```
    There are a lot of functions there but in reality they are `_P` versions of standard c functions that are adapted to read from the esp8266 32bit aligned flash. All of them take a `PGM_P` which is essentially a `const char *`. Under the hood these functions all use
    There are a lot of functions there but in reality they are `_P` versions of standard c functions that are adapted to read from the esp8266 32bit aligned flash. All of them take a `PGM_P` which is essentially a `const char *`. Under the hood these functions all use:
    ```cpp
    #define pgm_read_byte(addr) \
    @@ -79,17 +79,17 @@ There are a lot of functions there but in reality they are `_P` versions of stan
    ```
    which reads backs the bytes without causing a seg fault.

    This works well when you have designed a function as above that is specialised for dealing with PROGMEM pointers but there is no type checking except against `const char *`. This means that it is totally legitimate, as far as the compiler is concerned, for you to pass it any `const char *` string, which is obviously not true and may lead to undefined behaviour. This makes it impossible to create any overloaded functions that can use flash strings when defined as `PGM_P`. If you try you will get an ambiguous overload error as `PGM_P` ==`const char *`.
    This works well when you have designed a function as above that is specialised for dealing with PROGMEM pointers but there is no type checking except against `const char *`. This means that it is totally legitimate, as far as the compiler is concerned, for you to pass it any `const char *` string, which is obviously not true and will lead to undefined behaviour. This makes it impossible to create any overloaded functions that can use flash strings when they are defined as `PGM_P`. If you try you will get an ambiguous overload error as `PGM_P` == `const char *`.

    ### Enter the __FlashStringHelper
    This is a wrapper class that allows flash strings to be used as a class, this means that type checking and function overloading can be used with flash strings. Most people will be familiar with the `F()` macro and possibly the FPSTR() macro. These are defined:
    ### Enter the __FlashStringHelper...
    This is a wrapper class that allows flash strings to be used as a class, this means that type checking and function overloading can be used with flash strings. Most people will be familiar with the `F()` macro and possibly the FPSTR() macro. These are defined in [WString.h](https://github.com/esp8266/Arduino/blob/master/cores/esp8266/WString.h#L37):

    ```cpp
    #define FPSTR(pstr_pointer) (reinterpret_cast<const __FlashStringHelper *>(pstr_pointer))
    #define F(string_literal) (FPSTR(PSTR(string_literal)))
    ```
    So `FSPTR()` takes a PROGMEM pointer to a string and casts it to this FlashStringHelper class. Thus if you have defined a string as above `xyz` you can use `FPSTR()` to convert it to __FlashStringHelper for passing into functions that take it.
    So `FSPTR()` takes a PROGMEM pointer to a string and casts it to this `__FlashStringHelper` class. Thus if you have defined a string as above `xyz` you can use `FPSTR()` to convert it to `__FlashStringHelper` for passing into functions that take it.
    ```cpp
    static const char xyz[] PROGMEM = "This is a string stored in flash";
    @@ -100,7 +100,7 @@ The `F()` combines both of these methods to create an easy and quick way to stor
    ```cpp
    Serial.println(F("This is a string stored in flash"));
    ```
    Although these two functions provide a similar function, they serve different roles. `FPSTR()` allows you to define a global flash string and then use it in any function that takes __FlashStringHelper. `F()` allows you to define these flash strings in place, but you can't use them anywhere else. The consequence of this is sharing common strings is possible using `FPSTR()` but not `F()`. `__FlashStringHelper` is what the String class uses to overload its constructor:
    Although these two functions provide a similar function, they serve different roles. `FPSTR()` allows you to define a global flash string and then use it in any function that takes `__FlashStringHelper`. `F()` allows you to define these flash strings in place, but you can't use them anywhere else. The consequence of this is sharing common strings is possible using `FPSTR()` but not `F()`. `__FlashStringHelper` is what the String class uses to overload its constructor:

    ```cpp
    String(const char *cstr = ""); // constructor from const char *
    @@ -114,7 +114,7 @@ String mystring(F("This string is stored in flash"));
    ```

    ### How do I write a function to use __FlashStringHelper?
    Simples: cast the pointer back to a PGM_P and use the `_P` functions shown above. This an example implementation for String, the concat function.
    Simples: cast the pointer back to a PGM_P and use the `_P` functions shown above. This an example implementation for String for the concat function.

    ```cpp
    unsigned char String::concat(const __FlashStringHelper * str) {
  4. @sticilface sticilface revised this gist Mar 5, 2017. 1 changed file with 0 additions and 2 deletions.
    2 changes: 0 additions & 2 deletions PROGMEM.md
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,5 @@
    # Guide to PROGMEM on ESP8266 and Arduino IDE

    [TOC]

    ### Intro
    On low memory devices like the arduino and esp8266 you do not want strings to be stored in RAM. This occurs by default on these systems. Declare a string `const char * xyz = "this is a string"` and it will use up RAM.

  5. @sticilface sticilface created this gist Mar 5, 2017.
    197 changes: 197 additions & 0 deletions PROGMEM.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,197 @@
    # Guide to PROGMEM on ESP8266 and Arduino IDE

    [TOC]

    ### Intro
    On low memory devices like the arduino and esp8266 you do not want strings to be stored in RAM. This occurs by default on these systems. Declare a string `const char * xyz = "this is a string"` and it will use up RAM.

    The solution on these devices is to allow strings to be stored in read only memory, in Arduino this is the PROGMEM macro. Most of my experience is with the ESP8266 which is a 32bit micros controller. This device stores PROGMEM data in flash. The macro PROGMEM on ESP8266 is simply

    ```cpp
    #define PROGMEM ICACHE_RODATA_ATTR
    ```
    Which in turn is defined by:
    ```cpp
    #define ICACHE_RODATA_ATTR __attribute__((section(".irom.text")))
    ```
    Which places the variable in the .irom.text section ie flash.

    The key to understanding PROGMEM is to understand how the strings are stored and then how they are retrieved from flash.
    ### Declare a global string to be stored in flash.
    ```
    static const char xyz[] PROGMEM = "This is a string stored in flash";
    ```
    ### Declare a flash string within code block.
    For this you can use the PSTR macro. Which are all defined in [pgmspace.h](https://github.com/esp8266/Arduino/blob/master/cores/esp8266/pgmspace.h)
    ```cpp
    #define PGM_P const char *
    #define PGM_VOID_P const void *
    #define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];}))
    ```
    In practice:

    ```cpp
    void myfunction(void) {
    PGM_P xyz = PSTR("Store this string in flash");
    const char * abc = PSTR("Also Store this string in flash");
    }
    ```
    The above examples will store these strings in flash. To retrieve and manipulate flash strings is not straight forward as the esp8266 must read from flash in 4byte words. In the Arduino IDE for esp8266 there are several functions that can help retrieve strings from flash that have been stored using PROGMEM. Both of the examples above will give you a `const char *` back, how ever if you try to do anything with these pointers, without correct 32bit alignment you will get a seg fault and the esp will crash. You must read from the flash 32 bit aligned.
    ### Functions to read back from PROGMEM
    Which are all defined in [pgmspace.h](https://github.com/esp8266/Arduino/blob/master/cores/esp8266/pgmspace.h)
    ```cpp
    int memcmp_P(const void* buf1, PGM_VOID_P buf2P, size_t size);
    void* memccpy_P(void* dest, PGM_VOID_P src, int c, size_t count);
    void* memmem_P(const void* buf, size_t bufSize, PGM_VOID_P findP, size_t findPSize);
    void* memcpy_P(void* dest, PGM_VOID_P src, size_t count);
    char* strncpy_P(char* dest, PGM_P src, size_t size);
    #define strcpy_P(dest, src) strncpy_P((dest), (src), SIZE_IRRELEVANT)
    char* strncat_P(char* dest, PGM_P src, size_t size);
    #define strcat_P(dest, src) strncat_P((dest), (src), SIZE_IRRELEVANT)
    int strncmp_P(const char* str1, PGM_P str2P, size_t size);
    #define strcmp_P(str1, str2P) strncmp_P((str1), (str2P), SIZE_IRRELEVANT)s
    int strncasecmp_P(const char* str1, PGM_P str2P, size_t size);
    #define strcasecmp_P(str1, str2P) strncasecmp_P((str1), (str2P), SIZE_IRRELEVANT)
    size_t strnlen_P(PGM_P s, size_t size);
    #define strlen_P(strP) strnlen_P((strP), SIZE_IRRELEVANT)
    char* strstr_P(const char* haystack, PGM_P needle);
    int printf_P(PGM_P formatP, ...);
    int sprintf_P(char *str, PGM_P formatP, ...);
    int snprintf_P(char *str, size_t strSize, PGM_P formatP, ...);
    int vsnprintf_P(char *str, size_t strSize, PGM_P formatP, va_list ap);
    ```

    There are a lot of functions there but in reality they are `_P` versions of standard c functions that are adapted to read from the esp8266 32bit aligned flash. All of them take a `PGM_P` which is essentially a `const char *`. Under the hood these functions all use

    ```cpp
    #define pgm_read_byte(addr) \
    (__extension__({ \
    PGM_P __local = (PGM_P)(addr); /* isolate varible for macro expansion */ \
    ptrdiff_t __offset = ((uint32_t)__local & 0x00000003); /* byte aligned mask */ \
    const uint32_t* __addr32 = (const uint32_t*)((const uint8_t*)(__local)-__offset); \
    uint8_t __result = ((*__addr32) >> (__offset * 8)); \
    __result; \
    }))
    ```
    which reads backs the bytes without causing a seg fault.
    This works well when you have designed a function as above that is specialised for dealing with PROGMEM pointers but there is no type checking except against `const char *`. This means that it is totally legitimate, as far as the compiler is concerned, for you to pass it any `const char *` string, which is obviously not true and may lead to undefined behaviour. This makes it impossible to create any overloaded functions that can use flash strings when defined as `PGM_P`. If you try you will get an ambiguous overload error as `PGM_P` ==`const char *`.
    ### Enter the __FlashStringHelper
    This is a wrapper class that allows flash strings to be used as a class, this means that type checking and function overloading can be used with flash strings. Most people will be familiar with the `F()` macro and possibly the FPSTR() macro. These are defined:
    ```cpp
    #define FPSTR(pstr_pointer) (reinterpret_cast<const __FlashStringHelper *>(pstr_pointer))
    #define F(string_literal) (FPSTR(PSTR(string_literal)))
    ```

    So `FSPTR()` takes a PROGMEM pointer to a string and casts it to this FlashStringHelper class. Thus if you have defined a string as above `xyz` you can use `FPSTR()` to convert it to __FlashStringHelper for passing into functions that take it.

    ```cpp
    static const char xyz[] PROGMEM = "This is a string stored in flash";
    Serial.println(FPSTR(xyz));
    ```
    The `F()` combines both of these methods to create an easy and quick way to store an inline string in flash, and return the type `__FlashStringHelper`. For example:

    ```cpp
    Serial.println(F("This is a string stored in flash"));
    ```
    Although these two functions provide a similar function, they serve different roles. `FPSTR()` allows you to define a global flash string and then use it in any function that takes __FlashStringHelper. `F()` allows you to define these flash strings in place, but you can't use them anywhere else. The consequence of this is sharing common strings is possible using `FPSTR()` but not `F()`. `__FlashStringHelper` is what the String class uses to overload its constructor:

    ```cpp
    String(const char *cstr = ""); // constructor from const char *
    String(const String &str); // copy constructor
    String(const __FlashStringHelper *str); // constructor for flash strings
    ```
    This allows you to write:
    ```cpp
    String mystring(F("This string is stored in flash"));
    ```

    ### How do I write a function to use __FlashStringHelper?
    Simples: cast the pointer back to a PGM_P and use the `_P` functions shown above. This an example implementation for String, the concat function.

    ```cpp
    unsigned char String::concat(const __FlashStringHelper * str) {
    if (!str) return 0; // return if the pointer is void
    int length = strlen_P((PGM_P)str); // cast it to PGM_P, which is basically const char *, and measure it using the _P version of strlen.
    if (length == 0) return 1;
    unsigned int newlen = len + length;
    if (!reserve(newlen)) return 0; // create a buffer of the correct length
    strcpy_P(buffer + len, (PGM_P)str); //copy the string in using strcpy_P
    len = newlen;
    return 1;
    }
    ```
    ### How do I declare a global flash string and use it?
    ```cpp
    static const char xyz[] PROGMEM = "This is a string stored in flash. Len = %u";
    void setup() {
    Serial.begin(115200);
    Serial.println( FPSTR(xyz) ); // just prints the string, must convert it to FlashStringHelper first using FPSTR().
    Serial.printf_P( xyz, strlen_P(xyz)); // use printf with PROGMEM string
    }
    ```

    ### How do I use inline flash strings?

    ```cpp
    void setup() {
    Serial.begin(115200);
    Serial.println( F("This is an inline string")); //
    Serial.printf_P( PSTR("This is an inline string using printf %s), "hello");
    }
    ```

    ### How do I declare and use data in PROGMEM?

    ```cpp
    const size_t len_xyz = 32;
    const uint8_t xyz[] PROGMEM = {
    0x1F, 0x8B, 0x08, 0x08, 0xEC, 0x73, 0xBF, 0x57, 0x00, 0x03, 0x65, 0x64, 0x69, 0x74, 0x2E, 0x68,
    0x74, 0x6D, 0x00, 0xC5, 0x1B, 0xE9, 0x7A, 0xD3, 0x48, 0xF2, 0x7F, 0x9E, 0xA2, 0x47, 0xEC, 0x10}

    void setup() {
    Serial.begin(115200);
    uint8_t * buf = new uint8_t[len_xyz];
    if (buf) {
    memcpy_P(buf, xyz, len_xyz);
    Serial.write(buf, xyz_len); // output the buffer.
    }
    }

    ```
    ### How do I declare some data in PROGMEM, and retrieve one byte from it.
    Declare the data as done previously, then use `pgm_read_byte` to get the value back.
    ```cpp
    const size_t len_xyz = 32;
    const uint8_t xyz[] PROGMEM = {
    0x1F, 0x8B, 0x08, 0x08, 0xEC, 0x73, 0xBF, 0x57, 0x00, 0x03, 0x65, 0x64, 0x69, 0x74, 0x2E, 0x68,
    0x74, 0x6D, 0x00, 0xC5, 0x1B, 0xE9, 0x7A, 0xD3, 0x48, 0xF2, 0x7F, 0x9E, 0xA2, 0x47, 0xEC, 0x10}
    void setup() {
    Serial.begin(115200);
    for (uint8_t i = 0; i < len_xyz; i++) {
    uint8_t val = pgm_read_byte(xyz + i);
    Serial.print(val);
    }
    }
    }
    ```

    ### In summary
    It is easy to store strings in flash using `PROGMEM` and `PSTR` but you have to create functions that specifically use the pointers they generate as they are basically `const char *`. On the other hand `FPSTR` and `F()` give you a class that you can do implicit conversions from, very useful when overloading functions, and doing implicit type conversions. It is worth adding that if you wish to store an `int`, `float` or pointer these can be stored and read back directly as they are 4 bytes in size and therefor will be always aligned!

    Hope this helps.