espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
13.73k stars 7.3k forks source link

WiFi won’t connect when using C++ (IDFGH-2755) #4822

Closed GilDev closed 4 years ago

GilDev commented 4 years ago

Environment

Problem Description

I’m trying to convert (kind of) the WiFi station example from C to C++, but the C++ version will never connect and the log is missing something as you can see below.

Expected Behavior

The ESP32 would connect like in the standard example.

Actual Behavior

It retries until it uses all the retries are used and then print:

I (14220) wifi station: connect to the AP fail
I (14230) wifi station: Failed to connect to SSID:~~~~~, password:~~~~~

Code to reproduce this issue

extern "C" {
    void app_main();
}

#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"

#include "lwip/err.h"
#include "lwip/sys.h"

/* The examples use WiFi configuration that you can set via project configuration menu

   If you'd rather not, just change the below entries to strings with
   the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
*/
#define EXAMPLE_ESP_WIFI_SSID      CONFIG_ESP_WIFI_SSID
#define EXAMPLE_ESP_WIFI_PASS      CONFIG_ESP_WIFI_PASSWORD
#define EXAMPLE_ESP_MAXIMUM_RETRY  CONFIG_ESP_MAXIMUM_RETRY

/* FreeRTOS event group to signal when we are connected*/
static EventGroupHandle_t s_wifi_event_group;

/* The event group allows multiple bits for each event, but we only care about two events:
 * - we are connected to the AP with an IP
 * - we failed to connect after the maximum amount of retries */
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT      BIT1

static const char *TAG = "wifi station";

static int s_retry_num = 0;

class Wifi
{
    public:
        Wifi(void);
        void wifi_init_sta(void);

    private:
        static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data);
};

Wifi::Wifi(void)
{}

void Wifi::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);
    }
}

void Wifi::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_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));

    wifi_config_t wifi_config;
    strcpy((char *) wifi_config.sta.ssid, EXAMPLE_ESP_WIFI_SSID);
    strcpy((char *) wifi_config.sta.password, EXAMPLE_ESP_WIFI_PASS);
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_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");
    }

    ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler));
    ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler));
    vEventGroupDelete(s_wifi_event_group);
}

void app_main(void)
{
    //Initialize NVS
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
      ESP_ERROR_CHECK(nvs_flash_erase());
      ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");

    Wifi wifi = Wifi();
    wifi.wifi_init_sta();
}

// If your code is longer than 30 lines, GIST is preferred.

Debug Logs

From the esp-idf example:

I (1560) wifi station: wifi_init_sta finished.
D (1560) esp_netif_lwip: esp_netif_start_api 0x3ffbf614
D (1570) esp_netif_lwip: esp_netif_get_hostname esp_netif:0x3ffbf614
D (1580) esp_netif_lwip: call api in lwip: ret=0x0, give sem
D (1590) event: running post WIFI_EVENT:2 with handler 0x400d4e20 and context 0x3ffb9efc on loop 0x3ffbe6ec
0x400d4e20: event_handler at /Users/gildev/Developer/projects/esp32/station/build/../main/station_example_main.c:46

I (1720) wifi: new:<7,0>, old:<1,0>, ap:<255,255>, sta:<7,0>, prof:1
D (2480) nvs: nvs_set_blob sta.apinfo 700
I (2500) wifi: state: init -> auth (b0)
I (2500) wifi: state: auth -> assoc (0)
I (2520) wifi: state: assoc -> run (10)
I (4460) wifi: connected with freebox_BECG, aid = 13, channel 7, BW20, bssid = ae:12:63:ab:c9:30
I (4470) wifi: security type: 2, phy: bgn, rssi: -68
D (4470) nvs: nvs_set sta.chan 1 7
D (4470) nvs: nvs_set_blob sta.apinfo 700
I (4480) wifi: pm start, type: 1

D (4480) event: running post WIFI_EVENT:4 with handler 0x400dccd4 and context 0x3ffbf8c4 on loop 0x3ffbe6ec
0x400dccd4: wifi_default_action_sta_connected at /Users/gildev/Developer/esp-idf/components/esp_wifi/src/wifi_default.c:84

From my program in C++:

I (1560) wifi station: wifi_init_sta finished.
D (1560) esp_netif_lwip: esp_netif_start_api 0x3ffbf614
D (1570) esp_netif_lwip: esp_netif_get_hostname esp_netif:0x3ffbf614
D (1580) esp_netif_lwip: call api in lwip: ret=0x0, give sem
D (1580) event: running post WIFI_EVENT:2 with handler 0x400d4e20 and context 0x3ffb9efc on loop 0x3ffbe6ec
0x400d4e20: Wifi::event_handler(void*, char const*, int, void*) at /Users/gildev/Developer/projects/esp32/station/build/../main/station_example_main.cpp:55

D (3650) event: running post WIFI_EVENT:5 with handler 0x400dcc80 and context 0x3ffbf8ec on loop 0x3ffbe6ec
0x400dcc80: wifi_default_action_sta_disconnected at /Users/gildev/Developer/esp-idf/components/esp_wifi/src/wifi_default.c:103

I have no idea of why it’s not initializing the WiFi properly, nothing changed aside from the class embedding both functions. Thanks for taking the time to look at this!

negativekelvin commented 4 years ago

You didn't zero out wifi_config

X-Ryl669 commented 4 years ago

To add to @negativekelvin's answer, the reason is mainly that C and C++ differs when using structure initialization. I don't know if it's still the case, but previously the code used field name assignment to initialize structure member. However, this is not valid in C++ and as such you must initialize the struct yourself. so, write:

ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));

    wifi_config_t wifi_config = {};
    strcpy((char *) wifi_config.sta.ssid, EXAMPLE_ESP_WIFI_SSID);
    strcpy((char *) wifi_config.sta.password, EXAMPLE_ESP_WIFI_PASS);

or memset(&wifi_config, 0, sizeof(wifi_config));

This is valid GNU's C but invalid C++:

   wifi_config_t wifi_config = { .sta = { .ssid = EXAMPLE_ESP_WIFI_SSID, .password = EXAMPLE_ESP_WIFI_PASS } };                                                   
GilDev commented 4 years ago

Thanks @negativekelvin @X-Ryl669! Indeed I saw the structure initialization was illegal in C++, that’s why I removed it, but I haven’t thought about zeroing it out again…

tim-seidel commented 3 years ago

Thanks guys! We tried everything else to get it working but this finally worked :)