Notes, Misc - mhightower83/Arduino-ESP8266-misc GitHub Wiki
ESP8266 Board Summary
here TODO: Need to verify internal pull-up and pull-down options against Expressive documentation.
CPU Speed Changes
From bbs.espressif.com post by Don Kinzer
I agree that the defines in the include file are misleading. I've done some additional testing and this is what I've found:
- It appears not to be necessary to use REG_SET_BIT() or REG_CLR_BIT() to change the speed. Simply calling system_update_cpu_freq() with a parameter of 80 or 160 effects the desired speed. Alternatively, one can set/clear the register bit instead of calling system_update_cpu_freq(). In other words, using either alone appears to have the same effect and using both together does not appear to have any additional effect.
- Specifying a CPU speed of 160 does, in fact, change the execution speed to 160MHz. I confirmed this using an app that toggled an output several times and observed with a logic analyzer that the period was about one-half of that measured when operating at 80MHz.
- The UART baud rate is not affected by the CPU speed setting. A given divisor produces the same baud rate with both speed settings.
- The timers of the RTC are not affected by the CPU speed setting. A given timer divisor produces the same rate of change of the RTC timer's COUNT register with both speed settings.
Don Kinzer Beaverton, OR, USA
Flash Access Issue
You cannot reliably access data in flash from an ISR. The problem is not that interrupts are needed to handle cache-miss; it is that the SPI bus may be busy. You could have interrupted a foreground SPI bus operation that is not finished. And, that can happen with an ISR, not in IRAM or an ISR making a call to a non-IRAM function that is not cached. Cache-miss creates demand for a busy SPI bus, thus crash.
No additional interrupts needed for processing a cache-miss, just SPI bus access. Thus we are okay when we start an INTLEVEL 15 from the foreground, there is no SPI bus activity and none can start outside of our execution path. From this context, we can safely access flash.
Atomic Operators on ESP8266
I tried to use an std::atomic_exchange(&lock, 1)
function. The sketch compiles; however, it cannot link because of an "undefined reference to __atomic_exchange_4". Apparently, there are no test-and-set instructions on the ESP8266. So far it looks like, for memory exchanges that need to be atomic, you will need to disable and restore interrupts around the sensitive area.
- For multi-architecture Arduino platforms, this repo looks good. He creates an ATOMIC() macro similar to the ATOMIC_BLOCK() that is available for Arduino AVRs.
- In the Arduino ESP8266 core the
interrupts.h
file has two class-based options for doing this:InterruptsLock
- Disables all interrupts through level 15.{ esp8266::InterruptLock lock; // Do sensitive stuff with interrupts locked out. } // Back to normal interrupt operations in background.
- A macro
AutoInterruptLock(level)
that defines a class inline, then uses it to start the process.{ esp8266::AutoInterruptLock(3); // Allows interrupt levels above 3 // Do sensitive stuff with interrupts up to 3 locked out. } // Back to normal interrupt operations in background.
- Something like this also seems to compile down nicely:
int interlocked_exchange_int(volatile int *target, int value) { uint32_t ps = xt_rsil(15); int oldValue = *target; *target = value; xt_wsr_ps(ps); return oldValue; }
String Class - Use Considerations
"In Libraries String use is not recommended by inexperienced programmers."
Based on Develo's comments::
The String class manages its own dynamic allocs internally, and the user must be aware of how it behaves in a tiny system like the ESP. Usage such as pass-by-value (Creates a copy, use pass by reference.) or long sequences of concats (Many small allocs causes heap fragmentation, use .reserve
.) are bad. Using when receiving chars and not knowing when the sequence ends is preferable to C-style manual handling of the dynamic allocs, so it's good.
As a user, it can ease a whole lot of use cases, but you must be a responsible adult and use them correctly.
- Use
.reserve
when many smallconcat
operations may occur. - Pass by reference, not by value and use
const
when possible. - Starting with Core 2.5.0, Small String Optimization, SSO, was added. A String object can hold up to an 11 character string (+ '/0', 12 chars total) before an
alloc
is done.
WiFi Init Notes
Start by extracting some interesting details from Arduino ESP8266 Core Issue #1054 There are a lot more interesting points in this issue discussion.
- Correct order Set
mode
before callingsoftAP
. ref(https://github.com/esp8266/Arduino/issues/1054#issuecomment-158611389)
WiFi.mode(WIFI_AP_STA);
WiFi.softAP(ap_ssid, SecuritySettings.WifiAPKey);
- Only call these when there is a change in WiFi settings.
WiFi.mode(WIFI_STA);
WiFi.begin(id, pass);
Each time these are done, the Espressif SDK writes these settings to flash memory. If these are done at every boot, depending on your frequency, this might result in some wear to these sectors of flash memory. ref(https://github.com/esp8266/Arduino/issues/1054#issuecomment-158404894)
-
If I am reading this correctly, using
WiFi.persistent(false);
will result in using the SDK calls that do not save the WiFi credential information in flash. Thus a sketch that always calls WiFi.mode/WiFi.begin should be able to avoid wearing out the flash. ref(https://github.com/esp8266/Arduino/issues/1054#issuecomment-158663415) -
As it relates to the use of
WiFi.persistent(false);
, there is an interesting code summary ofESP8266WiFiGeneric.cpp
. However, I miss the point he makes in his final summary. It implies a need to do aWiFi.persistent(true);
. Maybe this is in the context of using DeepSleep. TODO: reread description. -
Concerns of FLASH wear - Suggestion to use SPIFFS which has wear leveling built in. ref(https://github.com/esp8266/Arduino/issues/1054#issuecomment-158611718)
-
Polymorphism -What is it? It would appear that C++ overloaded function calls would qualify as a simple form; however, there is much more to it than that. Needs more googling. Hmm, this description of Polymorphism sounds good.The point to note, it cannot be used from an ISR, when vtables are in flash.
Scheduled Functions
There are two classes of Scheduled Functions: (Extraced from d-a-v's comment)
- One time
- Scheduled functions are not called from
yield()
, because they are allowed to callyield()
. - They are only called at next
loop()
.
- Scheduled functions are not called from
- Reoccurring
- Reoccurring scheduled functions, must not call
yield()
, because they are called fromyield()
.
- Reoccurring scheduled functions, must not call
WiFi Debug Logging - Connect Semantics Issue
I think there is a semantics issue created by looking at the SDK debug logging. It looks to me that the SDK concept of "connected" is based on 802.11 of being connected to an AP. The Core.'s concept of "connected" is based on being able to use the Network, eg. connected to AP, and having an IP address.
- SDK
wifi evt: 0
(EVENT_STAMODE_CONNECTED
) - which I take to mean finished 802.11 negotiations with the AP were successful. - SDK
wifi evt: 3
(EVENT_STAMODE_GOT_IP
) indicates got an IP address, either DHCP has succeeded or used a static IP address. - The Arduino ESP8266 core returns connected,
WiFi.status() == WL_CONNECTED
, when the SDK APIwifi_station_get_connect_status()
returnsSTATION_GOT_IP
. Note, SDK status and events have a different set of enums.
So the SDK can be connected to an AP w/o an IP Address, and the Arduino ESP8266 Core will continue to report != WL_CONNECTED
because the connection is not complete from a user's perspective.