Unverified Commit 67c027ce authored by Daniel Egnor's avatar Daniel Egnor Committed by GitHub

Add documentation on threading issues with WiFi.onEvent() to examples (#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: default avatarPedro Minatel <pedro.minatel@espressif.com>
Co-authored-by: default avatarPedro Minatel <pminatel@gmail.com>
parent 7ecde877
......@@ -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
****************
......@@ -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
......@@ -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
......@@ -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:
......@@ -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) {
......
......@@ -14,12 +14,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:
......@@ -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);
}
......
......@@ -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) {
......@@ -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
......
......@@ -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) {
......@@ -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
......
......@@ -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) {
......@@ -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
......
......@@ -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) {
......@@ -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);
......
......@@ -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;
......@@ -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
......
......@@ -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:
......@@ -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();
......
......@@ -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:
......@@ -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");
......
......@@ -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);
......@@ -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");
......@@ -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){
......
......@@ -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);
......@@ -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);
......
......@@ -47,7 +47,7 @@ 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);
......@@ -55,7 +55,7 @@ void connectToWiFi(const char * ssid, const char * 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:
......
......@@ -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()
......
......@@ -11,13 +11,13 @@ Note: This sketch takes up a lot of space for the app and may not be able to fla
#include "WiFiProv.h"
#include "WiFi.h"
// #define USE_SOFT_AP // Uncomment if you want to enforce using Soft AP method instead of BLE
const char * pop = "abcd1234"; // Proof of possession - otherwise called a PIN - string provided by the device, entered by user in the phone app
// #define USE_SOFT_AP // Uncomment if you want to enforce using the Soft AP method instead of BLE
const char * pop = "abcd1234"; // Proof of possession - otherwise called a PIN - string provided by the device, entered by the user in the phone app
const char * service_name = "PROV_123"; // Name of your device (the Espressif apps expects by default device name starting with "Prov_")
const char * service_key = NULL; // Password used for SofAP method (NULL = no password needed)
bool reset_provisioned = true; // When true the library will automatically delete previously provisioned data.
// WARNING: SysProvEvent is called from a separate FreeRTOS task (thread)!
void SysProvEvent(arduino_event_t *sys_event)
{
switch (sys_event->event_id) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment