ESP32 で無線 LAN を常に接続する方法を紹介します.
背景
ESP32 の特長である低消費電力を生かして乾電池駆動で使う場合,必要な時のみ無線 LAN に使うのがセオリーです.一方,ESP32 を無線 LAN 内蔵の単電源小型マイコンとしてフル活用する場合,無線 LAN の常時接続をしたくなります.
そこで,できる限り無線 LAN の接続を維持するサンプルを作ってみました.
方針
次の方針としました.
- 無線 LAN の接続状態を監視するタスクを走らせ、切断されたらすぐに再接続
- 監視タスクの動作は WatchDogTimer で監視
- 無線 LAN の接続が連続して一定回数失敗したら一端リセットして再起動
コード
コード全体は次のようになります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
#include <string.h> #include "esp_wifi.h" #include "esp_event_loop.h" #include "esp_task_wdt.h" #include "nvs_flash.h" #include "app.h" #include "wifi_task.h" #include "wifi_config.h" // wifi_config.h should define followings. // #define WIFI_SSID "XXXXXXXX" // WiFi SSID // #define WIFI_PASS "XXXXXXXX" // WiFi Password static const uint32_t FATAL_DISCON_COUNT = 5; static uint32_t wifi_discon_count = 0; static SemaphoreHandle_t wifi_start = NULL; static SemaphoreHandle_t wifi_stop = NULL; static void wifi_disconnect() { ESP_LOGI(TAG, "Disconnect WiFi."); ESP_ERROR_CHECK(esp_wifi_disconnect()); ESP_ERROR_CHECK(esp_wifi_stop()); } static const char *wifi_authmode_str(wifi_auth_mode_t mode) { switch (mode) { case WIFI_AUTH_OPEN: return "open"; case WIFI_AUTH_WEP: return "WEP"; case WIFI_AUTH_WPA_PSK: return "WPA_PSK"; case WIFI_AUTH_WPA2_PSK: return "WPA2_PSK"; case WIFI_AUTH_WPA_WPA2_PSK: return "WPA_WPA2_PSK"; case WIFI_AUTH_WPA2_ENTERPRISE: return "WPA2_ENTERPRISE"; default: return "?"; } } static const char *wifi_cipher_type_str(wifi_cipher_type_t type) { switch (type) { case WIFI_CIPHER_TYPE_NONE: return "none"; case WIFI_CIPHER_TYPE_WEP40: return "WEP40"; case WIFI_CIPHER_TYPE_WEP104: return "WEP104"; case WIFI_CIPHER_TYPE_TKIP: return "TKIP"; case WIFI_CIPHER_TYPE_CCMP: return "CCMP"; case WIFI_CIPHER_TYPE_TKIP_CCMP: return "TKIP and CCMP"; default: return "?"; } } static void wifi_log_rssi() { wifi_ap_record_t ap_info; if (esp_wifi_sta_get_ap_info(&ap_info) != OK) { ESP_LOGE(TAG, "Failed to get AP info."); return; } ESP_LOGI(TAG, "WiFi status: SSID=%s, CH=%d, AUTH=%s, CIPHER=%s, RSSI=%d", ap_info.ssid, ap_info.primary, wifi_authmode_str(ap_info.authmode), wifi_cipher_type_str(ap_info.pairwise_cipher), ap_info.rssi); } static esp_err_t event_handler(void *ctx, system_event_t *event) { switch(event->event_id) { case SYSTEM_EVENT_STA_START: ESP_LOGI(TAG, "Event: SYSTEM_EVENT_STA_START."); ESP_ERROR_CHECK(tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA, WIFI_HOSTNAME)); ESP_ERROR_CHECK(esp_wifi_connect()); break; case SYSTEM_EVENT_STA_GOT_IP: ESP_LOGI(TAG, "Event: SYSTEM_EVENT_STA_GOT_IP."); xSemaphoreGive(wifi_start); break; case SYSTEM_EVENT_STA_DISCONNECTED: ESP_LOGI(TAG, "Event: SYSTEM_EVENT_STA_DISCONNECTED."); xSemaphoreGive(wifi_stop); break; default: break; } return ESP_OK; } static void init_wifi() { esp_err_t ret; ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK(ret); tcpip_adapter_init(); ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL)); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); #ifdef WIFI_SSID wifi_config_t wifi_config = { .sta = { .ssid = WIFI_SSID, .password = WIFI_PASS, }, }; wifi_config_t wifi_config_cur; ESP_ERROR_CHECK(esp_wifi_get_config(WIFI_IF_STA, &wifi_config_cur)); if (strcmp((const char *)wifi_config_cur.sta.ssid, (const char *)wifi_config.sta.ssid) || strcmp((const char *)wifi_config_cur.sta.password, (const char *)wifi_config.sta.password)) { ESP_LOGI(TAG, "Save WIFI config."); ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config)); } #endif } static esp_err_t wifi_connect() { ESP_LOGI(TAG, "Start to connect to WiFi."); xSemaphoreTake(wifi_start, portMAX_DELAY); ESP_ERROR_CHECK(esp_wifi_start()); if (xSemaphoreTake(wifi_start, 10000 / portTICK_RATE_MS) == pdTRUE) { ESP_LOGI(TAG, "Succeeded in connecting to WiFi."); wifi_log_rssi(); wifi_discon_count = 0; xSemaphoreGive(wifi_start); return ESP_OK; } else { ESP_LOGE(TAG, "Failed to connect to WiFi."); wifi_discon_count++; xSemaphoreGive(wifi_start); xSemaphoreGive(wifi_stop); return ESP_FAIL; } } static void wifi_watch_task(void *param) { SemaphoreHandle_t mutex = (SemaphoreHandle_t)param; ESP_ERROR_CHECK(esp_task_wdt_init(60, true)); ESP_ERROR_CHECK(esp_task_wdt_add(NULL)); vSemaphoreCreateBinary(wifi_start); vSemaphoreCreateBinary(wifi_stop); init_wifi(); xSemaphoreTake(wifi_stop, portMAX_DELAY); if ((wifi_connect() == ESP_OK) && (mutex != NULL)) { xSemaphoreGive(mutex); } while (1) { if (xSemaphoreTake(wifi_stop, 30000 / portTICK_RATE_MS) == pdTRUE) { ESP_LOGI(TAG, "WiFi disconnect count: %d", wifi_discon_count); // NOTE: 接続に一定回数連続して失敗したら,何かがおかしいので再起動する. if (wifi_discon_count >= FATAL_DISCON_COUNT) { ESP_LOGI(TAG, "Too many connect failures, restarting..."); esp_restart(); } wifi_disconnect(); if ((wifi_connect() == ESP_OK) && (mutex != NULL)) { xSemaphoreGive(mutex); } } ESP_ERROR_CHECK(esp_task_wdt_reset()); } } void wifi_task_start(SemaphoreHandle_t mutex) { xTaskCreate(wifi_watch_task, "wifi_watch_task", 2048, mutex, 10, NULL); } |
使い方
無線 LAN に接続するには次のようにします.
1 2 3 4 5 6 |
SemaphoreHandle_t wifi_start = NULL; vSemaphoreCreateBinary(wifi_start); xSemaphoreTake(wifi_start, portMAX_DELAY); wifi_task_start(wifi_start); |
wifi_start は無線 LAN 接続が完了したことを示すセマフォです.
HTTP サーバー等,無線 LAN 接続が完了してから走らせたいタスクを走らせたい場合に次のようにして使います.必要ない場合 wifi_start は NULL に置き換えて OK です.
1 2 |
xSemaphoreTake(wifi_start, portMAX_DELAY); http_task_start(); |
コメント