How does the espressif SDK handle wifi?

This article is part of a series.

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:

Here are the code examples I mainly looked at:

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

Libraries Seen Before

Not explicitly imported:

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

Seen Before

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.

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:

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.