Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add documentation on threading issues with WiFi.onEvent() to examples #8081

Merged
merged 11 commits into from
Dec 5, 2023
107 changes: 105 additions & 2 deletions docs/source/api/wifi.rst
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,102 @@ Common API

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

onEvent (and removeEvent)
*************************

Registers a caller-supplied function to be called when WiFi events
occur. Several forms are available.

Function pointer callback taking the event ID:

.. code-block:: arduino

typedef void (*WiFiEventCb)(arduino_event_id_t);
wifi_event_id_t onEvent(WiFiEventCb, arduino_event_id_t = ARDUINO_EVENT_MAX);

Function pointer callback taking an event-ID-and-info struct:

.. code-block:: arduino

typedef struct{
arduino_event_id_t event_id;
arduino_event_info_t event_info;
} arduino_event_t;

typedef void (*WiFiEventSysCb)(arduino_event_t *);
wifi_event_id_t onEvent(WiFiEventSysCb, arduino_event_id_t = ARDUINO_EVENT_MAX);

Callback using ``std::function`` taking event ID and info separately:

.. code-block:: arduino

typedef std::function<void(arduino_event_id_t, arduino_event_info_t)> WiFiEventFuncCb;
wifi_event_id_t onEvent(WiFiEventFuncCb, arduino_event_id_t = ARDUINO_EVENT_MAX);

A similar set of functions are available to remove callbacks:

.. code-block:: arduino

void removeEvent(WiFiEventCb, arduino_event_id_t = ARDUINO_EVENT_MAX);
void removeEvent(WiFiEventSysCb, arduino_event_id_t = ARDUINO_EVENT_MAX);
void removeEvent(wifi_event_id_t = ARDUINO_EVENT_MAX);

In all cases, the subscribing function accepts an optional event type to
invoke the callback only for that specific event; with the default
``ARDUINO_EVENT_MAX``, the callback will be invoked for all WiFi events.

Any callback function is given the event type in a parameter.
Some of the possible callback function formats also take an
``arduino_event_info_t`` (or use ``arduino_event_t`` which includes both
ID and info) which is a union of structs with additional information
about different event types.

See
`WiFiGeneric.h <https://github.com/espressif/arduino-esp32/blob/master/libraries/WiFi/src/WiFiGeneric.h>`_
for the list of event types and "info" substructures, and also see a full
example of event handling: `events example`_.

.. warning::

Event callback functions are invoked on a separate
`thread <https://en.wikipedia.org/wiki/Thread_(computing)>`_
(`FreeRTOS task <https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/freertos_idf.html#tasks>`_)
independent of the main application thread that runs ``setup()`` and
``loop()``. Callback functions must therefore be
`thread-safe <https://en.wikipedia.org/wiki/Thread_safety>`_;
they must not access shared/global variables directly without locking,
and must only call similarly thread-safe functions.

Some core operations like ``Serial.print()`` are thread-safe but many
functions are not. Notably, ``WiFi.onEvent()`` and ``WiFi.removeEvent()``
are not thread-safe and should never be invoked from a callback thread.

setHostname (and getHostname)
*****************************

Sets the name the DHCP client uses to identify itself. In a typical network
setup this will be the name that shows up in the Wi-Fi router's device list.
The hostname must be no longer than 32 characters.

.. code-block:: arduino

setHostname(const char *hostname);

If the hostname is never specified, a default one will be assigned based
on the chip type and MAC address. The current hostname (default or custom)
may be retrieved:

.. code-block:: arduino

const char *getHostname();

.. warning::

The ``setHostname()`` function must be called BEFORE WiFi is started with
``WiFi.begin()``, ``WiFi.softAP()``, ``WiFi.mode()``, or ``WiFi.run()``.
To change the name, reset WiFi with ``WiFi.mode(WIFI_MODE_NULL)``,
then proceed with ``WiFi.setHostname(...)`` and restart WiFi from scratch.

useStaticBuffers
****************

Expand Down Expand Up @@ -543,6 +639,8 @@ To see how to use the ``WiFiScan``, take a look at the ``WiFiScan.ino`` example
Examples
--------

`Complete list of WiFi examples <https://github.com/espressif/arduino-esp32/tree/master/libraries/WiFi/examples>`_.

.. _ap example:

Wi-Fi AP Example
Expand All @@ -559,5 +657,10 @@ Wi-Fi STA Example
.. literalinclude:: ../../../libraries/WiFi/examples/WiFiClient/WiFiClient.ino
:language: arduino

References
----------
.. _events example:

Wi-Fi Events Example
********************

.. literalinclude:: ../../../libraries/WiFi/examples/WiFiClientEvents/WiFiClientEvents.ino
:language: arduino
6 changes: 4 additions & 2 deletions libraries/Ethernet/examples/ETH_LAN8720/ETH_LAN8720.ino
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@

static bool eth_connected = false;

// WARNING: WiFiEvent is called from a separate FreeRTOS task (thread)!
void WiFiEvent(WiFiEvent_t event)
{
switch (event) {
case ARDUINO_EVENT_ETH_START:
Serial.println("ETH Started");
//set eth hostname here
// The hostname must be set after the interface is started, but needs
// to be set before DHCP, so set it from the event handler thread.
ETH.setHostname("esp32-ethernet");
break;
case ARDUINO_EVENT_ETH_CONNECTED:
Expand Down Expand Up @@ -67,7 +69,7 @@ void testClient(const char * host, uint16_t port)
void setup()
{
Serial.begin(115200);
WiFi.onEvent(WiFiEvent);
WiFi.onEvent(WiFiEvent); // Will call WiFiEvent() from another thread.
ETH.begin();
}

Expand Down
6 changes: 4 additions & 2 deletions libraries/Ethernet/examples/ETH_TLK110/ETH_TLK110.ino
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@

static bool eth_connected = false;

// WARNING: WiFiEvent is called from a separate FreeRTOS task (thread)!
void WiFiEvent(WiFiEvent_t event)
{
switch (event) {
case ARDUINO_EVENT_ETH_START:
Serial.println("ETH Started");
//set eth hostname here
// The hostname must be set after the interface is started, but needs
// to be set before DHCP, so set it from the event handler thread.
ETH.setHostname("esp32-ethernet");
break;
case ARDUINO_EVENT_ETH_CONNECTED:
Expand Down Expand Up @@ -73,7 +75,7 @@ void testClient(const char * host, uint16_t port)
void setup()
{
Serial.begin(115200);
WiFi.onEvent(WiFiEvent);
WiFi.onEvent(WiFiEvent); // Will call WiFiEvent() from another thread.
ETH.begin(ETH_ADDR, ETH_POWER_PIN, ETH_MDC_PIN, ETH_MDIO_PIN, ETH_TYPE);
}

Expand Down
3 changes: 2 additions & 1 deletion libraries/RainMaker/examples/RMakerCustom/RMakerCustom.ino
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ bool dimmer_state = true;
// But, you can also define custom devices using the 'Device' base class object, as shown here
static Device *my_device = NULL;

// WARNING: sysProvEvent is called from a separate FreeRTOS task (thread)!
void sysProvEvent(arduino_event_t *sys_event)
{
switch (sys_event->event_id) {
Expand Down Expand Up @@ -105,7 +106,7 @@ void setup()

RMaker.start();

WiFi.onEvent(sysProvEvent);
WiFi.onEvent(sysProvEvent); // Will call sysProvEvent() from another thread.
#if CONFIG_IDF_TARGET_ESP32S2
WiFiProv.beginProvision(WIFI_PROV_SCHEME_SOFTAP, WIFI_PROV_SCHEME_HANDLER_NONE, WIFI_PROV_SECURITY_1, pop, service_name);
#else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ bool power_state = true;
// But, you can also define custom devices using the 'Device' base class object, as shown here
static Device *my_device = NULL;

// WARNING: sysProvEvent is called from a separate FreeRTOS task (thread)!
void sysProvEvent(arduino_event_t *sys_event)
{
switch (sys_event->event_id) {
Expand Down Expand Up @@ -171,7 +172,7 @@ void setup()

RMaker.start();

WiFi.onEvent(sysProvEvent);
WiFi.onEvent(sysProvEvent); // Will call sysProvEvent() from another thread.
#if CONFIG_IDF_TARGET_ESP32S2
WiFiProv.beginProvision(WIFI_PROV_SCHEME_SOFTAP, WIFI_PROV_SCHEME_HANDLER_NONE, WIFI_PROV_SECURITY_1, pop, service_name);
#else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ LightSwitch switch_ch2 = {gpio_switch2, false};
static Switch *my_switch1 = NULL;
static Switch *my_switch2 = NULL;

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

WiFi.onEvent(sysProvEvent);
WiFi.onEvent(sysProvEvent); // Will call sysProvEvent() from another thread.
#if CONFIG_IDF_TARGET_ESP32
WiFiProv.beginProvision(WIFI_PROV_SCHEME_BLE, WIFI_PROV_SCHEME_HANDLER_FREE_BTDM, WIFI_PROV_SECURITY_1, pop, service_name);
#else
Expand Down
3 changes: 2 additions & 1 deletion libraries/RainMaker/examples/RMakerSwitch/RMakerSwitch.ino
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ bool switch_state = true;
// fan, temperaturesensor.
static Switch *my_switch = NULL;

// WARNING: sysProvEvent is called from a separate FreeRTOS task (thread)!
void sysProvEvent(arduino_event_t *sys_event)
{
switch (sys_event->event_id) {
Expand Down Expand Up @@ -107,7 +108,7 @@ void setup()

RMaker.start();

WiFi.onEvent(sysProvEvent);
WiFi.onEvent(sysProvEvent); // Will call sysProvEvent() from another thread.
#if CONFIG_IDF_TARGET_ESP32S2
WiFiProv.beginProvision(WIFI_PROV_SCHEME_SOFTAP, WIFI_PROV_SCHEME_HANDLER_NONE,
WIFI_PROV_SECURITY_1, pop, service_name);
Expand Down
3 changes: 2 additions & 1 deletion libraries/WiFi/examples/FTM/FTM_Initiator/FTM_Initiator.ino
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ xSemaphoreHandle ftmSemaphore;
bool ftmSuccess = true;

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

// Listen for FTM Report events
// Will call onFtmReport() from another thread with FTM Report events.
WiFi.onEvent(onFtmReport, ARDUINO_EVENT_WIFI_FTM_REPORT);

// Connect to AP that has FTM Enabled
Expand Down
3 changes: 2 additions & 1 deletion libraries/WiFi/examples/WPS/WPS.ino
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ String wpspin2string(uint8_t a[]){
return (String)wps_pin;
}

// WARNING: WiFiEvent is called from a separate FreeRTOS task (thread)!
void WiFiEvent(WiFiEvent_t event, arduino_event_info_t info){
switch(event){
case ARDUINO_EVENT_WIFI_STA_START:
Expand Down Expand Up @@ -103,7 +104,7 @@ void setup(){
Serial.begin(115200);
delay(10);
Serial.println();
WiFi.onEvent(WiFiEvent);
WiFi.onEvent(WiFiEvent); // Will call WiFiEvent() from another thread.
WiFi.mode(WIFI_MODE_STA);
Serial.println("Starting WPS");
wpsInitConfig();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ void onButton(){
delay(100);
}

// WARNING: WiFiEvent is called from a separate FreeRTOS task (thread)!
void WiFiEvent(WiFiEvent_t event){
switch(event) {
case ARDUINO_EVENT_WIFI_AP_START:
Expand Down Expand Up @@ -112,7 +113,7 @@ void WiFiEvent(WiFiEvent_t event){
void setup() {
Serial.begin(115200);
pinMode(0, INPUT_PULLUP);
WiFi.onEvent(WiFiEvent);
WiFi.onEvent(WiFiEvent); // Will call WiFiEvent() from another thread.
Serial.print("ESP32 SDK: ");
Serial.println(ESP.getSdkVersion());
Serial.println("Press the button to select the next mode");
Expand Down
6 changes: 4 additions & 2 deletions libraries/WiFi/examples/WiFiClientEvents/WiFiClientEvents.ino
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
const char* ssid = "your-ssid";
const char* password = "your-password";


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

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

delay(1000);

// Examples of different ways to register wifi events
// Examples of different ways to register wifi events;
// these handlers will be called from another thread.
WiFi.onEvent(WiFiEvent);
WiFi.onEvent(WiFiGotIP, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_GOT_IP);
WiFiEventId_t eventID = WiFi.onEvent([](WiFiEvent_t event, WiFiEventInfo_t info){
Expand Down
5 changes: 2 additions & 3 deletions libraries/WiFi/examples/WiFiIPv6/WiFiIPv6.ino
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,15 @@ void wifiConnectedLoop(){
delay(9000);
}

// WARNING: WiFiEvent is called from a separate FreeRTOS task (thread)!
void WiFiEvent(WiFiEvent_t event){
switch(event) {

case ARDUINO_EVENT_WIFI_AP_START:
//can set ap hostname here
WiFi.softAPsetHostname(AP_SSID);
//enable ap ipv6 here
WiFi.softAPenableIpV6();
break;

case ARDUINO_EVENT_WIFI_STA_START:
//set sta hostname here
WiFi.setHostname(AP_SSID);
Expand Down Expand Up @@ -106,7 +105,7 @@ void WiFiEvent(WiFiEvent_t event){
void setup(){
Serial.begin(115200);
WiFi.disconnect(true);
WiFi.onEvent(WiFiEvent);
WiFi.onEvent(WiFiEvent); // Will call WiFiEvent() from another thread.
WiFi.mode(WIFI_MODE_APSTA);
WiFi.softAP(AP_SSID);
WiFi.begin(STA_SSID, STA_PASS);
Expand Down
4 changes: 2 additions & 2 deletions libraries/WiFi/examples/WiFiUDPClient/WiFiUDPClient.ino
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,15 @@ void connectToWiFi(const char * ssid, const char * pwd){
// delete old config
WiFi.disconnect(true);
//register event handler
WiFi.onEvent(WiFiEvent);
WiFi.onEvent(WiFiEvent); // Will call WiFiEvent() from another thread.

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

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

//wifi event handler
// WARNING: WiFiEvent is called from a separate FreeRTOS task (thread)!
void WiFiEvent(WiFiEvent_t event){
switch(event) {
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
Expand Down
3 changes: 2 additions & 1 deletion libraries/WiFiProv/examples/WiFiProv/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ This example allows Arduino user to choose either BLE or SOFTAP as a mode of tra

## WiFi.onEvent()

Using this API user can register to receive WiFi Events and Provisioning Events
This API can be used to register a function to be called from another
thread for WiFi Events and Provisioning Events.

## WiFi.beginProvision()

Expand Down
2 changes: 2 additions & 0 deletions libraries/WiFiProv/examples/WiFiProv/WiFiProv.ino
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "WiFiProv.h"
#include "WiFi.h"

// WARNING: SysProvEvent is called from a separate FreeRTOS task (thread)!
void SysProvEvent(arduino_event_t *sys_event)
{
switch (sys_event->event_id) {
Expand Down