Skip to content

Commit 67c027c

Browse files
egnorpedrominatel
andauthored
Add documentation on threading issues with WiFi.onEvent() to examples (espressif#8081)
* Compile error if CONFIG_FREERTOS_HZ != 1000 * add a check at the CMake level, per feedback * fix a punctuation glitch * Remove `_Static_assert` per feedback * add documentation on threading issues with WiFi.onEvent() * more comments * thin out comments, add docs * Update WiFiProv.ino merge conflict issue fixed * Added the CLK type and MAC from eFuse to Ethernet begin * Fixed the order and arguments on the Ethernet begin function --------- Co-authored-by: Pedro Minatel <pedro.minatel@espressif.com> Co-authored-by: Pedro Minatel <pminatel@gmail.com>
1 parent 7ecde87 commit 67c027c

File tree

15 files changed

+148
-33
lines changed

15 files changed

+148
-33
lines changed

Diff for: docs/source/api/wifi.rst

+105-2
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,102 @@ Common API
5050

5151
Here are the common APIs that are used for both modes, AP and STA.
5252

53+
onEvent (and removeEvent)
54+
*************************
55+
56+
Registers a caller-supplied function to be called when WiFi events
57+
occur. Several forms are available.
58+
59+
Function pointer callback taking the event ID:
60+
61+
.. code-block:: arduino
62+
63+
typedef void (*WiFiEventCb)(arduino_event_id_t);
64+
wifi_event_id_t onEvent(WiFiEventCb, arduino_event_id_t = ARDUINO_EVENT_MAX);
65+
66+
Function pointer callback taking an event-ID-and-info struct:
67+
68+
.. code-block:: arduino
69+
70+
typedef struct{
71+
arduino_event_id_t event_id;
72+
arduino_event_info_t event_info;
73+
} arduino_event_t;
74+
75+
typedef void (*WiFiEventSysCb)(arduino_event_t *);
76+
wifi_event_id_t onEvent(WiFiEventSysCb, arduino_event_id_t = ARDUINO_EVENT_MAX);
77+
78+
Callback using ``std::function`` taking event ID and info separately:
79+
80+
.. code-block:: arduino
81+
82+
typedef std::function<void(arduino_event_id_t, arduino_event_info_t)> WiFiEventFuncCb;
83+
wifi_event_id_t onEvent(WiFiEventFuncCb, arduino_event_id_t = ARDUINO_EVENT_MAX);
84+
85+
A similar set of functions are available to remove callbacks:
86+
87+
.. code-block:: arduino
88+
89+
void removeEvent(WiFiEventCb, arduino_event_id_t = ARDUINO_EVENT_MAX);
90+
void removeEvent(WiFiEventSysCb, arduino_event_id_t = ARDUINO_EVENT_MAX);
91+
void removeEvent(wifi_event_id_t = ARDUINO_EVENT_MAX);
92+
93+
In all cases, the subscribing function accepts an optional event type to
94+
invoke the callback only for that specific event; with the default
95+
``ARDUINO_EVENT_MAX``, the callback will be invoked for all WiFi events.
96+
97+
Any callback function is given the event type in a parameter.
98+
Some of the possible callback function formats also take an
99+
``arduino_event_info_t`` (or use ``arduino_event_t`` which includes both
100+
ID and info) which is a union of structs with additional information
101+
about different event types.
102+
103+
See
104+
`WiFiGeneric.h <https://github.com/espressif/arduino-esp32/blob/master/libraries/WiFi/src/WiFiGeneric.h>`_
105+
for the list of event types and "info" substructures, and also see a full
106+
example of event handling: `events example`_.
107+
108+
.. warning::
109+
110+
Event callback functions are invoked on a separate
111+
`thread <https://en.wikipedia.org/wiki/Thread_(computing)>`_
112+
(`FreeRTOS task <https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/freertos_idf.html#tasks>`_)
113+
independent of the main application thread that runs ``setup()`` and
114+
``loop()``. Callback functions must therefore be
115+
`thread-safe <https://en.wikipedia.org/wiki/Thread_safety>`_;
116+
they must not access shared/global variables directly without locking,
117+
and must only call similarly thread-safe functions.
118+
119+
Some core operations like ``Serial.print()`` are thread-safe but many
120+
functions are not. Notably, ``WiFi.onEvent()`` and ``WiFi.removeEvent()``
121+
are not thread-safe and should never be invoked from a callback thread.
122+
123+
setHostname (and getHostname)
124+
*****************************
125+
126+
Sets the name the DHCP client uses to identify itself. In a typical network
127+
setup this will be the name that shows up in the Wi-Fi router's device list.
128+
The hostname must be no longer than 32 characters.
129+
130+
.. code-block:: arduino
131+
132+
setHostname(const char *hostname);
133+
134+
If the hostname is never specified, a default one will be assigned based
135+
on the chip type and MAC address. The current hostname (default or custom)
136+
may be retrieved:
137+
138+
.. code-block:: arduino
139+
140+
const char *getHostname();
141+
142+
.. warning::
143+
144+
The ``setHostname()`` function must be called BEFORE WiFi is started with
145+
``WiFi.begin()``, ``WiFi.softAP()``, ``WiFi.mode()``, or ``WiFi.run()``.
146+
To change the name, reset WiFi with ``WiFi.mode(WIFI_MODE_NULL)``,
147+
then proceed with ``WiFi.setHostname(...)`` and restart WiFi from scratch.
148+
53149
useStaticBuffers
54150
****************
55151

@@ -552,6 +648,8 @@ To see how to use the ``WiFiScan``, take a look at the ``WiFiScan.ino`` example
552648
Examples
553649
--------
554650

651+
`Complete list of WiFi examples <https://github.com/espressif/arduino-esp32/tree/master/libraries/WiFi/examples>`_.
652+
555653
.. _ap example:
556654

557655
Wi-Fi AP Example
@@ -568,5 +666,10 @@ Wi-Fi STA Example
568666
.. literalinclude:: ../../../libraries/WiFi/examples/WiFiClient/WiFiClient.ino
569667
:language: arduino
570668

571-
References
572-
----------
669+
.. _events example:
670+
671+
Wi-Fi Events Example
672+
********************
673+
674+
.. literalinclude:: ../../../libraries/WiFi/examples/WiFiClientEvents/WiFiClientEvents.ino
675+
:language: arduino

Diff for: libraries/Ethernet/examples/ETH_LAN8720/ETH_LAN8720.ino

+5-4
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@
1616

1717
static bool eth_connected = false;
1818

19-
void onEvent(arduino_event_id_t event, arduino_event_info_t info)
19+
// WARNING: WiFiEvent is called from a separate FreeRTOS task (thread)!
20+
void WiFiEvent(WiFiEvent_t event)
2021
{
2122
switch (event) {
2223
case ARDUINO_EVENT_ETH_START:
2324
Serial.println("ETH Started");
24-
//set eth hostname here
25+
// The hostname must be set after the interface is started, but needs
26+
// to be set before DHCP, so set it from the event handler thread.
2527
ETH.setHostname("esp32-ethernet");
2628
break;
2729
case ARDUINO_EVENT_ETH_CONNECTED:
@@ -72,11 +74,10 @@ void testClient(const char * host, uint16_t port)
7274
void setup()
7375
{
7476
Serial.begin(115200);
75-
WiFi.onEvent(onEvent);
77+
WiFi.onEvent(WiFiEvent); // Will call WiFiEvent() from another thread.
7678
ETH.begin();
7779
}
7880

79-
8081
void loop()
8182
{
8283
if (eth_connected) {

Diff for: libraries/Ethernet/examples/ETH_TLK110/ETH_TLK110.ino

+11-9
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,23 @@
55

66
#include <ETH.h>
77

8-
#define ETH_TYPE ETH_PHY_TLK110
9-
#define ETH_ADDR 31
10-
#define ETH_MDC_PIN 23
11-
#define ETH_MDIO_PIN 18
12-
#define ETH_POWER_PIN 17
13-
#define ETH_CLK_MODE ETH_CLOCK_GPIO0_IN
8+
#define ETH_TYPE ETH_PHY_TLK110
9+
#define ETH_ADDR 31
10+
#define ETH_MDC_PIN 23
11+
#define ETH_MDIO_PIN 18
12+
#define ETH_POWER_PIN 17
13+
#define ETH_CLK_MODE ETH_CLOCK_GPIO0_IN
1414

1515
static bool eth_connected = false;
1616

17-
void onEvent(arduino_event_id_t event, arduino_event_info_t info)
17+
// WARNING: WiFiEvent is called from a separate FreeRTOS task (thread)!
18+
void WiFiEvent(WiFiEvent_t event)
1819
{
1920
switch (event) {
2021
case ARDUINO_EVENT_ETH_START:
2122
Serial.println("ETH Started");
22-
//set eth hostname here
23+
// The hostname must be set after the interface is started, but needs
24+
// to be set before DHCP, so set it from the event handler thread.
2325
ETH.setHostname("esp32-ethernet");
2426
break;
2527
case ARDUINO_EVENT_ETH_CONNECTED:
@@ -70,7 +72,7 @@ void testClient(const char * host, uint16_t port)
7072
void setup()
7173
{
7274
Serial.begin(115200);
73-
WiFi.onEvent(onEvent);
75+
WiFi.onEvent(WiFiEvent); // Will call WiFiEvent() from another thread.
7476
ETH.begin(ETH_TYPE, ETH_ADDR, ETH_MDC_PIN, ETH_MDIO_PIN, ETH_POWER_PIN, ETH_CLK_MODE);
7577
}
7678

Diff for: libraries/RainMaker/examples/RMakerCustom/RMakerCustom.ino

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ bool dimmer_state = true;
2424
// But, you can also define custom devices using the 'Device' base class object, as shown here
2525
static Device *my_device = NULL;
2626

27+
// WARNING: sysProvEvent is called from a separate FreeRTOS task (thread)!
2728
void sysProvEvent(arduino_event_t *sys_event)
2829
{
2930
switch (sys_event->event_id) {
@@ -105,7 +106,7 @@ void setup()
105106

106107
RMaker.start();
107108

108-
WiFi.onEvent(sysProvEvent);
109+
WiFi.onEvent(sysProvEvent); // Will call sysProvEvent() from another thread.
109110
#if CONFIG_IDF_TARGET_ESP32S2
110111
WiFiProv.beginProvision(WIFI_PROV_SCHEME_SOFTAP, WIFI_PROV_SCHEME_HANDLER_NONE, WIFI_PROV_SECURITY_1, pop, service_name);
111112
#else

Diff for: libraries/RainMaker/examples/RMakerCustomAirCooler/RMakerCustomAirCooler.ino

+2-1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ bool power_state = true;
4040
// But, you can also define custom devices using the 'Device' base class object, as shown here
4141
static Device *my_device = NULL;
4242

43+
// WARNING: sysProvEvent is called from a separate FreeRTOS task (thread)!
4344
void sysProvEvent(arduino_event_t *sys_event)
4445
{
4546
switch (sys_event->event_id) {
@@ -170,7 +171,7 @@ void setup()
170171

171172
RMaker.start();
172173

173-
WiFi.onEvent(sysProvEvent);
174+
WiFi.onEvent(sysProvEvent); // Will call sysProvEvent() from another thread.
174175
#if CONFIG_IDF_TARGET_ESP32S2
175176
WiFiProv.beginProvision(WIFI_PROV_SCHEME_SOFTAP, WIFI_PROV_SCHEME_HANDLER_NONE, WIFI_PROV_SECURITY_1, pop, service_name);
176177
#else

Diff for: libraries/RainMaker/examples/RMakerSonoffDualR3/RMakerSonoffDualR3.ino

+2-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ LightSwitch switch_ch2 = {gpio_switch2, false};
3434
static Switch *my_switch1 = NULL;
3535
static Switch *my_switch2 = NULL;
3636

37+
// WARNING: sysProvEvent is called from a separate FreeRTOS task (thread)!
3738
void sysProvEvent(arduino_event_t *sys_event)
3839
{
3940
switch (sys_event->event_id) {
@@ -160,7 +161,7 @@ void setup()
160161
Serial.printf("\nStarting ESP-RainMaker\n");
161162
RMaker.start();
162163

163-
WiFi.onEvent(sysProvEvent);
164+
WiFi.onEvent(sysProvEvent); // Will call sysProvEvent() from another thread.
164165
#if CONFIG_IDF_TARGET_ESP32
165166
WiFiProv.beginProvision(WIFI_PROV_SCHEME_BLE, WIFI_PROV_SCHEME_HANDLER_FREE_BTDM, WIFI_PROV_SECURITY_1, pop, service_name);
166167
#else

Diff for: libraries/RainMaker/examples/RMakerSwitch/RMakerSwitch.ino

+2-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ bool switch_state = true;
2525
// fan, temperaturesensor.
2626
static Switch *my_switch = NULL;
2727

28+
// WARNING: sysProvEvent is called from a separate FreeRTOS task (thread)!
2829
void sysProvEvent(arduino_event_t *sys_event)
2930
{
3031
switch (sys_event->event_id) {
@@ -107,7 +108,7 @@ void setup()
107108

108109
RMaker.start();
109110

110-
WiFi.onEvent(sysProvEvent);
111+
WiFi.onEvent(sysProvEvent); // Will call sysProvEvent() from another thread.
111112
#if CONFIG_IDF_TARGET_ESP32S2
112113
WiFiProv.beginProvision(WIFI_PROV_SCHEME_SOFTAP, WIFI_PROV_SCHEME_HANDLER_NONE,
113114
WIFI_PROV_SECURITY_1, pop, service_name);

Diff for: libraries/WiFi/examples/FTM/FTM_Initiator/FTM_Initiator.ino

+2-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ SemaphoreHandle_t ftmSemaphore;
2828
bool ftmSuccess = true;
2929

3030
// FTM report handler with the calculated data from the round trip
31+
// WARNING: This function is called from a separate FreeRTOS task (thread)!
3132
void onFtmReport(arduino_event_t *event) {
3233
const char * status_str[5] = {"SUCCESS", "UNSUPPORTED", "CONF_REJECTED", "NO_RESPONSE", "FAIL"};
3334
wifi_event_ftm_report_t * report = &event->event_info.wifi_ftm_report;
@@ -62,7 +63,7 @@ void setup() {
6263
// Create binary semaphore (initialized taken and can be taken/given from any thread/ISR)
6364
ftmSemaphore = xSemaphoreCreateBinary();
6465

65-
// Listen for FTM Report events
66+
// Will call onFtmReport() from another thread with FTM Report events.
6667
WiFi.onEvent(onFtmReport, ARDUINO_EVENT_WIFI_FTM_REPORT);
6768

6869
// Connect to AP that has FTM Enabled

Diff for: libraries/WiFi/examples/WPS/WPS.ino

+2-1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ String wpspin2string(uint8_t a[]){
6161
return (String)wps_pin;
6262
}
6363

64+
// WARNING: WiFiEvent is called from a separate FreeRTOS task (thread)!
6465
void WiFiEvent(WiFiEvent_t event, arduino_event_info_t info){
6566
switch(event){
6667
case ARDUINO_EVENT_WIFI_STA_START:
@@ -103,7 +104,7 @@ void setup(){
103104
Serial.begin(115200);
104105
delay(10);
105106
Serial.println();
106-
WiFi.onEvent(WiFiEvent);
107+
WiFi.onEvent(WiFiEvent); // Will call WiFiEvent() from another thread.
107108
WiFi.mode(WIFI_MODE_STA);
108109
Serial.println("Starting WPS");
109110
wpsInitConfig();

Diff for: libraries/WiFi/examples/WiFiBlueToothSwitch/WiFiBlueToothSwitch.ino

+2-1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ void onButton(){
7373
delay(100);
7474
}
7575

76+
// WARNING: WiFiEvent is called from a separate FreeRTOS task (thread)!
7677
void WiFiEvent(WiFiEvent_t event){
7778
switch(event) {
7879
case ARDUINO_EVENT_WIFI_AP_START:
@@ -112,7 +113,7 @@ void WiFiEvent(WiFiEvent_t event){
112113
void setup() {
113114
Serial.begin(115200);
114115
pinMode(0, INPUT_PULLUP);
115-
WiFi.onEvent(WiFiEvent);
116+
WiFi.onEvent(WiFiEvent); // Will call WiFiEvent() from another thread.
116117
Serial.print("ESP32 SDK: ");
117118
Serial.println(ESP.getSdkVersion());
118119
Serial.println("Press the button to select the next mode");

Diff for: libraries/WiFi/examples/WiFiClientEvents/WiFiClientEvents.ino

+4-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
const char* ssid = "your-ssid";
4242
const char* password = "your-password";
4343

44-
44+
// WARNING: This function is called from a separate FreeRTOS task (thread)!
4545
void WiFiEvent(WiFiEvent_t event)
4646
{
4747
Serial.printf("[WiFi-event] event: %d\n", event);
@@ -132,6 +132,7 @@ void WiFiEvent(WiFiEvent_t event)
132132
default: break;
133133
}}
134134

135+
// WARNING: This function is called from a separate FreeRTOS task (thread)!
135136
void WiFiGotIP(WiFiEvent_t event, WiFiEventInfo_t info)
136137
{
137138
Serial.println("WiFi connected");
@@ -148,7 +149,8 @@ void setup()
148149

149150
delay(1000);
150151

151-
// Examples of different ways to register wifi events
152+
// Examples of different ways to register wifi events;
153+
// these handlers will be called from another thread.
152154
WiFi.onEvent(WiFiEvent);
153155
WiFi.onEvent(WiFiGotIP, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_GOT_IP);
154156
WiFiEventId_t eventID = WiFi.onEvent([](WiFiEvent_t event, WiFiEventInfo_t info){

Diff for: libraries/WiFi/examples/WiFiIPv6/WiFiIPv6.ino

+2-3
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,15 @@ void wifiConnectedLoop(){
6464
delay(9000);
6565
}
6666

67+
// WARNING: WiFiEvent is called from a separate FreeRTOS task (thread)!
6768
void WiFiEvent(WiFiEvent_t event){
6869
switch(event) {
69-
7070
case ARDUINO_EVENT_WIFI_AP_START:
7171
//can set ap hostname here
7272
WiFi.softAPsetHostname(AP_SSID);
7373
//enable ap ipv6 here
7474
WiFi.softAPenableIpV6();
7575
break;
76-
7776
case ARDUINO_EVENT_WIFI_STA_START:
7877
//set sta hostname here
7978
WiFi.setHostname(AP_SSID);
@@ -106,7 +105,7 @@ void WiFiEvent(WiFiEvent_t event){
106105
void setup(){
107106
Serial.begin(115200);
108107
WiFi.disconnect(true);
109-
WiFi.onEvent(WiFiEvent);
108+
WiFi.onEvent(WiFiEvent); // Will call WiFiEvent() from another thread.
110109
WiFi.mode(WIFI_MODE_APSTA);
111110
WiFi.softAP(AP_SSID);
112111
WiFi.begin(STA_SSID, STA_PASS);

Diff for: libraries/WiFi/examples/WiFiUDPClient/WiFiUDPClient.ino

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,15 @@ void connectToWiFi(const char * ssid, const char * pwd){
4747
// delete old config
4848
WiFi.disconnect(true);
4949
//register event handler
50-
WiFi.onEvent(WiFiEvent);
50+
WiFi.onEvent(WiFiEvent); // Will call WiFiEvent() from another thread.
5151

5252
//Initiate connection
5353
WiFi.begin(ssid, pwd);
5454

5555
Serial.println("Waiting for WIFI connection...");
5656
}
5757

58-
//wifi event handler
58+
// WARNING: WiFiEvent is called from a separate FreeRTOS task (thread)!
5959
void WiFiEvent(WiFiEvent_t event){
6060
switch(event) {
6161
case ARDUINO_EVENT_WIFI_STA_GOT_IP:

Diff for: libraries/WiFiProv/examples/WiFiProv/README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ This example allows Arduino users to choose either BLE or SOFTAP as the mode of
1010

1111
### WiFi.onEvent()
1212

13-
Using this API, users can register to receive WiFi Events and Provisioning Events.
13+
This API can be used to register a function to be called from another
14+
thread for WiFi Events and Provisioning Events.
1415

1516
### WiFi.beginProvision()
1617

0 commit comments

Comments
 (0)