How does the espressif SDK handle wifi?
This article is part of a series.
- Part 1: Can I program a an ESP32C6 with the esp-idf?
- Part 2: Can I program an ESP32C6 with the Swift example code?
- Part 3: How does the espressif SDK handle inputs?
- Part 4: This Article
- Part 5: Can I combine an LED and a button on the ESP32C6 with Swift?
- Part 6: Can I add Wifi to the ESP32C6 project with Swift?
- Part 7: Can I make a Swift-wrapped HTTP GET request from the ESP32C6?
Next step for me was to see if I could get any of the WiFi examples working. Which I did. This post is definately more notes than a how-to.
Here are the docs I used:
- https://docs.espressif.com/projects/esp-idf/en/v5.5.1/esp32c6/api-reference/network/index.html#wi-fi
- https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html
- https://www.espressif.com/sites/default/files/documentation/esp32-c6_technical_reference_manual_en.pdf (search term(s): “modem”, “wifi”)
Here are the code examples I mainly looked at:
- https://github.com/espressif/esp-idf/tree/v5.5.1/examples/wifi/scan
- https://github.com/espressif/esp-idf/tree/v5.5.1/examples/wifi/getting_started/station
- https://github.com/espressif/esp-idf/blob/v5.5.1/examples/wifi/fast_scan/
- (for station mode) https://github.com/espressif/esp-idf/blob/v5.5.1/examples/wifi/power_save/
- https://github.com/espressif/esp-idf/tree/v5.5.1/examples/protocols/http_request
- https://github.com/espressif/esp-idf/blob/v5.5.1/examples/common_components/protocol_examples_common/wifi_connect.c
- https://github.com/espressif/arduino-esp32/tree/master/libraries/WiFi/src
What are the different modes?
- Station mode (aka STA mode or Wi-Fi client mode). ESP32 connects to an access point.
- AP mode (aka Soft-AP mode or Access Point mode). Stations connect to the ESP32.
- Station/AP-coexistence mode (ESP32 is concurrently an access point and a station connected to another access point).
What was it like running the different examples.
I followed the same pattern from the getting started, copying the folders and then running them.
. $HOME/esp/esp-idf/export.sh
SOME_PORT=/dev/$(ls /dev/ | grep cu.usbmodem)
cd ~/esp
cp -r $IDF_PATH/examples/wifi/scan wifi_scan
cd wifi_scan
idf.py set-target esp32c6
idf.py menuconfig # especially for the secon two
idf.py build # just to catch any build errors first
idf.py -p $SOME_PORT flash monitor
WiFi Scan
The scan sketch failed to run the first time I tried to build and flash it with the following error"
File "/Users/$MY_USER/.espressif/python_env/idf5.5_py3.13_env/lib/python3.13/site-packages/serial/serialposix.py", line 517, in _reconfigure_port termios.tcsetattr( ~~~~~~~~~~~~~~~~~^ self.fd, ^^^^^^^^ termios.TCSANOW, ^^^^^^^^^^^^^^^^ [iflag, oflag, cflag, lflag, ispeed, ospeed, cc]) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ termios.error: (6, 'Device not configured')
But it worked again just fine when I re-ran it. No extra work on my part.
I was not able to see many WiFI networks because I have not attached an external antenna (search on page for antenna), but I could see my home one just fine.
Also noted the introduction of ESP_ERROR_CHECK(someFunction);
to catch esp_err_t
errors. These depend on an esp_check.h
include I don’t actually see in the header, so some other library must include it publicly, or it may even be in the base project include some how, although I did not find an explicit reference to it in project.cmake, target.cmake or idf.cmake.
New libraries
- “esp_wifi.h”: Simple Wifi
- “esp_event.h”: Event Loop Library “The event loop library allows components to declare events so that other components can register handlers – codes that executes when those events occur. This allows loosely-coupled components to attach desired behavior to state changes of other components without application involvement… One common case is, if a high-level library is using the Wi-Fi library: it may subscribe to ESP32 Wi-Fi Programming Model directly and act on those events.”
- “freertos/event_groups.h”: Event Groups API
- “nvs_flash.h”: Non-Volatile Storage Library Handles remembering the WiFi Access Points found.
- “regex.h”: C regex lib (current working directory over system headers) (Might be vestigial? Seems to build without it.)
Libraries Seen Before
- “esp_log.h”: Logging Library
- <string.h> : C String Library (system)
- “freertos/FreeRTOS.h” : FreeRTOS main
Not explicitly imported:
- https://docs.espressif.com/projects/esp-idf/en/v5.5.1/esp32c6/api-reference/network/esp_netif.html
- https://docs.espressif.com/projects/esp-idf/en/v5.5.1/esp32c6/api-reference/system/esp_err.html
Other useful links
The meat of the program is in wifi_scan
, which I include below, commented.
static void wifi_scan(void)
{
//Initialize the TCP/IP stack and the event loop
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
//Create the network interface handle and make sure it actually
//exists.
esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
assert(sta_netif);
//initialize wifi with config
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
//Use the user settings to limit the number of networks reported
//using the "access point info" type size to reserve the memory needed
uint16_t number = DEFAULT_SCAN_LIST_SIZE;
wifi_ap_record_t ap_info[DEFAULT_SCAN_LIST_SIZE];
uint16_t ap_count = 0;
memset(ap_info, 0, sizeof(ap_info));
//use the Station Mode
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
//Start The Wifi
ESP_ERROR_CHECK(esp_wifi_start());
//Optionally set up the scanning configuration to limit which channels to scan
// and then DO THE SCAN
#ifdef USE_CHANNEL_BITMAP
wifi_scan_config_t *scan_config = (wifi_scan_config_t *)calloc(1,sizeof(wifi_scan_config_t));
if (!scan_config) {
ESP_LOGE(TAG, "Memory Allocation for scan config failed!");
return;
}
array_2_channel_bitmap(channel_list, CHANNEL_LIST_SIZE, scan_config);
esp_wifi_scan_start(scan_config, true);
free(scan_config);
#else
esp_wifi_scan_start(NULL, true);
#endif /*USE_CHANNEL_BITMAP*/
// Print out all the info based on that "access point info" type
ESP_LOGI(TAG, "Max AP number ap_info can hold = %u", number);
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(&ap_count));
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&number, ap_info));
ESP_LOGI(TAG, "Total APs scanned = %u, actual AP number ap_info holds = %u", ap_count, number);
for (int i = 0; i < number; i++) {
ESP_LOGI(TAG, "SSID \t\t%s", ap_info[i].ssid);
ESP_LOGI(TAG, "RSSI \t\t%d", ap_info[i].rssi);
//defined and implemented station_example_main.c
print_auth_mode(ap_info[i].authmode);
if (ap_info[i].authmode != WIFI_AUTH_WEP) {
//defined and implemented station_example_main.c
print_cipher_type(ap_info[i].pairwise_cipher, ap_info[i].group_cipher);
}
ESP_LOGI(TAG, "Channel \t\t%d", ap_info[i].primary);
}
}
WiFi Getting Started Station
Similar to scan, but also handles a login. Also sets up for other “event types” going forward.
Station Example also ran fine after menuconfig (not before). It includes teh LwIP libraries, which I think are being used by the IP_EVENT
handler?
New Includes
- “freertos/task.h” : FreeRTOS TaskHandle
- “esp_system.h” : ESP System
- “lwip/err.h” : Lightweight TCP/IP stack, Errors
- “lwip/sys.h” : Lightweight TCP/IP stack, Errors System
Seen Before
- <string.h>
- “freertos/FreeRTOS.h”
- “freertos/event_groups.h”
- “esp_wifi.h”
- “esp_event.h”
- “esp_log.h”
- “nvs_flash.h”
The two main functions. An event handler, and an initializer.
The event_handler
is the handler given to the esp_event_handler_instance_register
in the start up.
I’m unclear if Swift will play nice with this event handler.
static void event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) {
esp_wifi_connect();
s_retry_num++;
ESP_LOGI(TAG, "retry to connect to the AP");
} else {
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
}
ESP_LOGI(TAG,"connect to the AP fail");
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
}
```C
void wifi_init_sta(void)
{
s_wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
esp_event_handler_instance_t instance_any_id;
esp_event_handler_instance_t instance_got_ip;
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&event_handler,
NULL,
&instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&event_handler,
NULL,
&instance_got_ip));
wifi_config_t wifi_config = {
.sta = {
.ssid = EXAMPLE_ESP_WIFI_SSID,
.password = EXAMPLE_ESP_WIFI_PASS,
/* Authmode threshold resets to WPA2 as default if password matches WPA2 standards (password len => 8).
* If you want to connect the device to deprecated WEP/WPA networks, Please set the threshold value
* to WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK and set the password with length and format matching to
* WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK standards.
*/
.threshold.authmode = ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD,
.sae_pwe_h2e = ESP_WIFI_SAE_MODE,
.sae_h2e_identifier = EXAMPLE_H2E_IDENTIFIER,
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
ESP_ERROR_CHECK(esp_wifi_start() );
ESP_LOGI(TAG, "wifi_init_sta finished.");
/* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
* number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);
/* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
* happened. */
if (bits & WIFI_CONNECTED_BIT) {
ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
} else if (bits & WIFI_FAIL_BIT) {
ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
} else {
ESP_LOGE(TAG, "UNEXPECTED EVENT");
}
}
WiFi HTTP Request
New Libraries
This minimal example just uses lwip directly.
- “lwip/err.h”
- “lwip/sockets.h”
- “lwip/sys.h”
- “lwip/netdb.h”
- “lwip/dns.h”
Note there is also a more complete http client example that uses an esp written http client library
These examples use a dependency on protocol_examples_common found in the idf_component.yml
${IDF_PATH}/examples/common_components/protocol_examples_common
The example itself says its’ not a good WiFi implementation.
/**
* @brief Configure Wi-Fi or Ethernet, connect, wait for IP
*
* This all-in-one helper function is used in protocols examples to
* reduce the amount of boilerplate in the example.
*
* It is not intended to be used in real world applications.
* See examples under examples/wifi/getting_started/ and examples/ethernet/
* for more complete Wi-Fi or Ethernet initialization code.
*
* Read "Establishing Wi-Fi or Ethernet Connection" section in
* examples/protocols/README.md for more information about this function.
*
* @return ESP_OK on successful connection
*/
esp_err_t example_connect(void);
/**
* Counterpart to example_connect, de-initializes Wi-Fi or Ethernet
*/
esp_err_t example_disconnect(void);
Did look briefly into trying to make my own Wifi wrapper component:
- https://espressif-docs.readthedocs-hosted.com/projects/idf-component-manager/en/latest/guides/packaging_components.html
- https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/build-system.html
- https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/build-system.html#writing-pure-cmake-components
- https://docs.espressif.com/projects/esp-idf/en/v5.5.1/esp32c6/api-guides/c.html
But decided to wait and see if I needed to do that to make WiFi in Swift work or not. I suspect I might, but not today.
Summary
I have now seen the board do everything I plan for the Swift demo for Sketching this year using just the esp-idf tools. Next step is to transfer this knowledge to Swift!
This article is part of a series.
- Part 1: Can I program a an ESP32C6 with the esp-idf?
- Part 2: Can I program an ESP32C6 with the Swift example code?
- Part 3: How does the espressif SDK handle inputs?
- Part 4: This Article
- Part 5: Can I combine an LED and a button on the ESP32C6 with Swift?
- Part 6: Can I add Wifi to the ESP32C6 project with Swift?
- Part 7: Can I make a Swift-wrapped HTTP GET request from the ESP32C6?