Skip to content

Commit

Permalink
Add documentation on threading issues with WiFi.onEvent() to examples (
Browse files Browse the repository at this point in the history
…#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 <[email protected]>
Co-authored-by: Pedro Minatel <[email protected]>
  • Loading branch information
3 people committed Dec 5, 2023
1 parent 7ecde87 commit 67c027c
Show file tree
Hide file tree
Showing 15 changed files with 148 additions and 33 deletions.
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 @@ -552,6 +648,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 @@ -568,5 +666,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
9 changes: 5 additions & 4 deletions libraries/Ethernet/examples/ETH_LAN8720/ETH_LAN8720.ino
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@

static bool eth_connected = false;

void onEvent(arduino_event_id_t event, arduino_event_info_t info)
// 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 @@ -72,11 +74,10 @@ void testClient(const char * host, uint16_t port)
void setup()
{
Serial.begin(115200);
WiFi.onEvent(onEvent);
WiFi.onEvent(WiFiEvent); // Will call WiFiEvent() from another thread.
ETH.begin();
}


void loop()
{
if (eth_connected) {
Expand Down
20 changes: 11 additions & 9 deletions libraries/Ethernet/examples/ETH_TLK110/ETH_TLK110.ino
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,23 @@

#include <ETH.h>

#define ETH_TYPE ETH_PHY_TLK110
#define ETH_ADDR 31
#define ETH_MDC_PIN 23
#define ETH_MDIO_PIN 18
#define ETH_POWER_PIN 17
#define ETH_CLK_MODE ETH_CLOCK_GPIO0_IN
#define ETH_TYPE ETH_PHY_TLK110
#define ETH_ADDR 31
#define ETH_MDC_PIN 23
#define ETH_MDIO_PIN 18
#define ETH_POWER_PIN 17
#define ETH_CLK_MODE ETH_CLOCK_GPIO0_IN

static bool eth_connected = false;

void onEvent(arduino_event_id_t event, arduino_event_info_t info)
// 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 @@ -70,7 +72,7 @@ void testClient(const char * host, uint16_t port)
void setup()
{
Serial.begin(115200);
WiFi.onEvent(onEvent);
WiFi.onEvent(WiFiEvent); // Will call WiFiEvent() from another thread.
ETH.begin(ETH_TYPE, ETH_ADDR, ETH_MDC_PIN, ETH_MDIO_PIN, ETH_POWER_PIN, ETH_CLK_MODE);
}

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 @@ -40,6 +40,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 @@ -170,7 +171,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 @@ SemaphoreHandle_t 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 users to choose either BLE or SOFTAP as the mode of

### WiFi.onEvent()

Using this API, users 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
Loading

0 comments on commit 67c027c

Please sign in to comment.