Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d7044ec

Browse files
sticilfaceigrr
authored andcommitted
Add PROGMEM docs
1 parent 283eb97 commit d7044ec

File tree

2 files changed

+255
-1
lines changed

2 files changed

+255
-1
lines changed

doc/PROGMEM.rst

+253
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
Guide to PROGMEM on ESP8266 and Arduino IDE
2+
===========================================
3+
4+
Intro
5+
-----
6+
7+
PROGMEM is a Arduino AVR feature that has been ported to ESP8266 to
8+
ensure compatability with existing Arduino libraries, as well as, saving
9+
RAM. On the esp8266 declaring a string such as ``const char * xyz =
10+
"this is a string"`` will place this string in RAM, not flash. It is
11+
possible to place a String into flash, and then load it into RAM when
12+
it is needed. On an 8bit AVR this process is very simple. On the 32bit
13+
ESP8266 there are conditions that must be met to read back from flash.
14+
15+
On the ESP8266 PROGMEM is a macro:
16+
17+
.. code:: cpp
18+
19+
#define PROGMEM ICACHE_RODATA_ATTR
20+
21+
``ICACHE_RODATA_ATTR`` is defined by:
22+
23+
.. code:: cpp
24+
25+
#define ICACHE_RODATA_ATTR __attribute__((section(".irom.text")))
26+
27+
Which places the variable in the .irom.text section ie flash. Placing strings in
28+
flash requires using any of the methods above.
29+
30+
| ### Declare a global string to be stored in flash.
31+
32+
.. code:: cpp
33+
34+
static const char xyz[] PROGMEM = "This is a string stored in flash";
35+
36+
Declare a flash string within code block.
37+
-----------------------------------------
38+
39+
For this you can use the PSTR macro. Which are all defined in
40+
`pgmspace.h <https://github.com/esp8266/Arduino/blob/master/cores/esp8266/pgmspace.h>`__
41+
42+
.. code:: cpp
43+
44+
#define PGM_P const char *
45+
#define PGM_VOID_P const void *
46+
#define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];}))
47+
48+
In practice:
49+
50+
.. code:: cpp
51+
52+
void myfunction(void) {
53+
PGM_P xyz = PSTR("Store this string in flash");
54+
const char * abc = PSTR("Also Store this string in flash");
55+
}
56+
57+
The two examples above will store these strings in flash. To retrieve
58+
and manipulate flash strings they must be read from flash in 4byte words.
59+
In the Arduino IDE for esp8266 there are several functions that can help
60+
retrieve strings from flash that have been stored using PROGMEM. Both of
61+
the examples above return ``const char *``. However use of these pointers,
62+
without correct 32bit alignment you will cause a segmentation fault and
63+
the ESP8266 will crash. You must read from the flash 32 bit aligned.
64+
65+
Functions to read back from PROGMEM
66+
-----------------------------------
67+
68+
Which are all defined in
69+
`pgmspace.h <https://github.com/esp8266/Arduino/blob/master/cores/esp8266/pgmspace.h>`__
70+
71+
.. code:: cpp
72+
73+
int memcmp_P(const void* buf1, PGM_VOID_P buf2P, size_t size);
74+
void* memccpy_P(void* dest, PGM_VOID_P src, int c, size_t count);
75+
void* memmem_P(const void* buf, size_t bufSize, PGM_VOID_P findP, size_t findPSize);
76+
void* memcpy_P(void* dest, PGM_VOID_P src, size_t count);
77+
char* strncpy_P(char* dest, PGM_P src, size_t size);
78+
char* strcpy_P(dest, src)
79+
char* strncat_P(char* dest, PGM_P src, size_t size);
80+
char* strcat_P(dest, src)
81+
int strncmp_P(const char* str1, PGM_P str2P, size_t size);
82+
int strcmp_P(str1, str2P)
83+
int strncasecmp_P(const char* str1, PGM_P str2P, size_t size);
84+
int strcasecmp_P(str1, str2P)
85+
size_t strnlen_P(PGM_P s, size_t size);
86+
size_t strlen_P(strP)
87+
char* strstr_P(const char* haystack, PGM_P needle);
88+
int printf_P(PGM_P formatP, ...);
89+
int sprintf_P(char *str, PGM_P formatP, ...);
90+
int snprintf_P(char *str, size_t strSize, PGM_P formatP, ...);
91+
int vsnprintf_P(char *str, size_t strSize, PGM_P formatP, va_list ap);
92+
93+
There are a lot of functions there but in reality they are ``_P``
94+
versions of standard c functions that are adapted to read from the
95+
esp8266 32bit aligned flash. All of them take a ``PGM_P`` which is
96+
essentially a ``const char *``. Under the hood these functions all use, a
97+
process to ensure that 4 bytes are read, and the request byte is returned.
98+
99+
This works well when you have designed a function as above that is
100+
specialised for dealing with PROGMEM pointers but there is no type
101+
checking except against ``const char *``. This means that it is totally
102+
legitimate, as far as the compiler is concerned, for you to pass it any
103+
``const char *`` string, which is obviously not true and will lead to
104+
undefined behaviour. This makes it impossible to create any overloaded
105+
functions that can use flash strings when they are defined as ``PGM_P``.
106+
If you try you will get an ambiguous overload error as ``PGM_P`` ==
107+
``const char *``.
108+
109+
Enter the \_\_FlashStringHelper... This is a wrapper class that allows flash
110+
strings to be used as a class, this means that type checking and function
111+
overloading can be used with flash strings. Most people will be familiar with
112+
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>`__:
113+
114+
.. code:: cpp
115+
116+
#define FPSTR(pstr_pointer) (reinterpret_cast<const __FlashStringHelper *>(pstr_pointer))
117+
#define F(string_literal) (FPSTR(PSTR(string_literal)))
118+
119+
So ``FSPTR()`` takes a PROGMEM pointer to a string and casts it to this
120+
``__FlashStringHelper`` class. Thus if you have defined a string as
121+
above ``xyz`` you can use ``FPSTR()`` to convert it to
122+
``__FlashStringHelper`` for passing into functions that take it.
123+
124+
.. code:: cpp
125+
126+
static const char xyz[] PROGMEM = "This is a string stored in flash";
127+
Serial.println(FPSTR(xyz));
128+
129+
The ``F()`` combines both of these methods to create an easy and quick
130+
way to store an inline string in flash, and return the type
131+
``__FlashStringHelper``. For example:
132+
133+
.. code:: cpp
134+
135+
Serial.println(F("This is a string stored in flash"));
136+
137+
Although these two functions provide a similar function, they serve
138+
different roles. ``FPSTR()`` allows you to define a global flash string
139+
and then use it in any function that takes ``__FlashStringHelper``.
140+
``F()`` allows you to define these flash strings in place, but you can't
141+
use them anywhere else. The consequence of this is sharing common
142+
strings is possible using ``FPSTR()`` but not ``F()``.
143+
``__FlashStringHelper`` is what the String class uses to overload its
144+
constructor:
145+
146+
.. code:: cpp
147+
148+
String(const char *cstr = ""); // constructor from const char *
149+
String(const String &str); // copy constructor
150+
String(const __FlashStringHelper *str); // constructor for flash strings
151+
152+
This allows you to write:
153+
154+
.. code:: cpp
155+
156+
String mystring(F("This string is stored in flash"));
157+
158+
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 for the concat function.
159+
160+
.. code:: cpp
161+
162+
unsigned char String::concat(const __FlashStringHelper * str) {
163+
if (!str) return 0; // return if the pointer is void
164+
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.
165+
if (length == 0) return 1;
166+
unsigned int newlen = len + length;
167+
if (!reserve(newlen)) return 0; // create a buffer of the correct length
168+
strcpy_P(buffer + len, (PGM_P)str); //copy the string in using strcpy_P
169+
len = newlen;
170+
return 1;
171+
}
172+
173+
How do I declare a global flash string and use it?
174+
--------------------------------------------------
175+
176+
.. code:: cpp
177+
178+
static const char xyz[] PROGMEM = "This is a string stored in flash. Len = %u";
179+
180+
void setup() {
181+
Serial.begin(115200); Serial.println();
182+
Serial.println( FPSTR(xyz) ); // just prints the string, must convert it to FlashStringHelper first using FPSTR().
183+
Serial.printf_P( xyz, strlen_P(xyz)); // use printf with PROGMEM string
184+
}
185+
186+
How do I use inline flash strings?
187+
----------------------------------
188+
189+
.. code:: cpp
190+
191+
void setup() {
192+
Serial.begin(115200); Serial.println();
193+
Serial.println( F("This is an inline string")); //
194+
Serial.printf_P( PSTR("This is an inline string using printf %s"), "hello");
195+
}
196+
197+
How do I declare and use data in PROGMEM?
198+
-----------------------------------------
199+
200+
.. code:: cpp
201+
202+
const size_t len_xyz = 30;
203+
const uint8_t xyz[] PROGMEM = {
204+
0x53, 0x61, 0x79, 0x20, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20,
205+
0x74, 0x6f, 0x20, 0x4d, 0x79, 0x20, 0x4c, 0x69, 0x74, 0x74,
206+
0x6c, 0x65, 0x20, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x00};
207+
208+
void setup() {
209+
Serial.begin(115200); Serial.println();
210+
uint8_t * buf = new uint8_t[len_xyz];
211+
if (buf) {
212+
memcpy_P(buf, xyz, len_xyz);
213+
Serial.write(buf, len_xyz); // output the buffer.
214+
}
215+
}
216+
217+
How do I declare some data in PROGMEM, and retrieve one byte from it.
218+
---------------------------------------------------------------------
219+
220+
Declare the data as done previously, then use ``pgm_read_byte`` to get
221+
the value back.
222+
223+
.. code:: cpp
224+
225+
const size_t len_xyz = 30;
226+
const uint8_t xyz[] PROGMEM = {
227+
0x53, 0x61, 0x79, 0x20, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20,
228+
0x74, 0x6f, 0x20, 0x4d, 0x79, 0x20, 0x4c, 0x69, 0x74, 0x74,
229+
0x6c, 0x65, 0x20, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x00
230+
};
231+
232+
void setup() {
233+
Serial.begin(115200); Serial.println();
234+
for (int i = 0; i < len_xyz; i++) {
235+
uint8_t byteval = pgm_read_byte(xyz + i);
236+
Serial.write(byteval); // output the buffer.
237+
}
238+
}
239+
240+
In summary
241+
----------
242+
243+
It is easy to store strings in flash using ``PROGMEM`` and ``PSTR`` but
244+
you have to create functions that specifically use the pointers they
245+
generate as they are basically ``const char *``. On the other hand
246+
``FPSTR`` and ``F()`` give you a class that you can do implicit
247+
conversions from, very useful when overloading functions, and doing
248+
implicit type conversions. It is worth adding that if you wish to store
249+
an ``int``, ``float`` or pointer these can be stored and read back
250+
directly as they are 4 bytes in size and therefor will be always
251+
aligned!
252+
253+
Hope this helps.

doc/index.rst

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ Welcome to ESP8266 Arduino Core's documentation!
1111
File system <filesystem>
1212
ESP8266WiFi <esp8266wifi/readme>
1313
OTA Updates <ota_updates/readme>
14-
14+
PROGMEM <PROGMEM>
15+
1516
Boards <boards>
1617
FAQ <faq/readme>
1718

0 commit comments

Comments
 (0)