В уроке 19 мы написали проект, который обеспечивает соединение с точкой доступа. То есть мы настроили наш модуль для работы по беспроводной сети в режиме станции. Только при условии исчезновения соединения с точкой доступа вследствие временного отключения последней или из-за каких-то других причин нам пришлось столкнуться с рядом трудностей, хотя с помощью неких костылей они всё же были преодолены, но это не совсем так, как хотелось бы. Поэтому на данном уроке мы постараемся немного усовершенствовать наш проект, так сказать, заставить нашу станцию быть немного умнее.
Схема остаётся такая же — отладочная плата, подключенная к USB компьютера
И проект мы также сделаем из проекта того же урока с именем WIFI_STA и назовём его WIFI_STA_SMART.
Откроем наш проект в Espressif IDE и в функции app_main файла main.c удалим бесконечный цикл совсем вместе с телом.
Вот эта переменная нам также не потребуется
wifi_ap_record_t info;
Создадим цикл событий по умолчанию
1 2 3 |
ESP_LOGI(TAG, "nvs_flash_init: 0x%04x", ret); ret = esp_event_loop_create_default(); ESP_LOGI(TAG, "esp_event_loop_create_default: %d", ret); |
Перейдём в файл wifi.c и в объявлении функции инициализации wifi_init_sta добавим возвращаемый параметр
esp_err_t wifi_init_sta(void)
Поправим также и прототип в заголовочном файле.
В теле данной функции объявим переменную такого же типа
1 2 3 |
esp_err_t wifi_init_sta(void) { esp_err_t ret; |
Дальнейший код в теле удалим совсем, так как изменений будет очень много и легче его заново написать.
Функцию event_handler удалим вместе с телом.
Вот эти глобальные вещи также удалим
static int s_retry_num = 0;
static EventGroupHandle_t s_wifi_event_group;
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
Сегодня мы будем работать с семафором, а не с событиями.
Объявим глобальный семафор, назначение которого мы узнаем позже
1 2 |
static const char *TAG = "wifi"; static xSemaphoreHandle s_semph_get_ip_addrs; |
В функции wifi_init_sta проверим существование нашего семафора
1 2 3 4 |
esp_err_t ret; if (s_semph_get_ip_addrs != NULL) { return ESP_ERR_INVALID_STATE; } |
А если мы не выйдем из-за какой-нибудь ошибки, то вернём успешный статус
1 2 3 |
return ESP_ERR_INVALID_STATE; } return ESP_OK; |
Выше добавим функцию, которая будет запускать нашу сеть
1 2 3 4 5 |
//------------------------------------------------------------- static void net_start(void) { } //------------------------------------------------------------- |
Вызовем её в функции wifi_init_sta
1 2 3 |
return ESP_ERR_INVALID_STATE; } net_start(); |
Объявим глобальную переменную, которая будет считать количество сетевых интерфейсов
1 2 |
static xSemaphoreHandle s_semph_get_ip_addrs; static int s_active_interfaces = 0; |
Выше добавим функцию, останавливающую соединение, в которой пока только уменьшим на 1 количество сетевых интерфейсов
1 2 3 4 5 6 |
//------------------------------------------------------------- static void net_stop(void) { s_active_interfaces--; } //------------------------------------------------------------- |
В функции wifi_init_sta зарегистрируем данную функцию в качестве обработчика событий
1 2 3 |
net_start(); ret = esp_register_shutdown_handler(&net_stop); ESP_LOGI(TAG, "esp_register_shutdown_handler(&net_stop) : %d", ret); |
Выше функции net_start добавим функцию в форме указателя, которая будет запускать беспроводной модуль
1 2 3 4 5 6 |
//------------------------------------------------------------- static esp_netif_t *wifi_start(void) { esp_err_t ret; } //------------------------------------------------------------- |
Объявим и проинициализируем переменную типа структуры управления настройками WiFi, такими так буфер RX/TX, структура WiFi NVS и т. д, после чего инициализируем WiFi данными настройками
1 2 3 4 |
esp_err_t ret; wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ret = esp_wifi_init(&cfg); ESP_LOGI(TAG, "esp_wifi_init : %d", ret); |
Объявим глобальный указатель на контейнер с информацией об интерфейсе
1 2 |
static xSemaphoreHandle s_semph_get_ip_addrs; static esp_netif_t *s_esp_netif = NULL; |
В функции net_start вызовем функцию wifi_start присвоив указатель на контейнер нашему указателю
1 2 3 |
static void net_start(void) { s_esp_netif = wifi_start(); |
В функции wifi_start объявим указатель на символьный массив для хранения текстового описания сетевого интерфейса
1 2 |
esp_err_t ret; char *desc; |
Объявим переменную типа структуры для хранения свойств интерфейса и проинициализируем её параметрами по умолчанию с помощью специального макроса, а затем текстовое описание дескриптора дополним именем нашего модуля, взятого из массива TAG и присвоим полю обратно
1 2 3 4 |
ESP_LOGI(TAG, "esp_wifi_init : %d", ret); esp_netif_inherent_config_t esp_netif_config = ESP_NETIF_INHERENT_DEFAULT_WIFI_STA(); asprintf(&desc, "%s: %s", TAG, esp_netif_config.if_desc); esp_netif_config.if_desc = desc; |
Назначим интерфейсу приоритет
1 2 |
esp_netif_config.if_desc = desc; esp_netif_config.route_prio = 128; |
Инициализируем интерфейс WiFi
1 2 |
esp_netif_config.route_prio = 128; esp_netif_t *netif = esp_netif_create_wifi(WIFI_IF_STA, &esp_netif_config); |
Освободим память, зарезервированную под символьный массив
1 2 |
esp_netif_t *netif = esp_netif_create_wifi(WIFI_IF_STA, &esp_netif_config); free(desc); |
Установим обработчики по умолчанию
1 2 |
free(desc); esp_wifi_set_default_wifi_sta_handlers(); |
У нас будет отдельный обработчик для завершения соединения.
Поэтому выше добавим функцию для данного обработчика, а также функцию для обработчика событий получения адреса
1 2 3 4 5 6 7 8 9 10 11 |
//------------------------------------------------------------- static void on_wifi_disconnect(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { } //------------------------------------------------------------- static void on_got_ip(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { } //------------------------------------------------------------- |
Вернемся в функцию wifi_start и зарегистрируем наши обработчики
1 2 3 4 5 |
esp_wifi_set_default_wifi_sta_handlers(); ret = esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &on_wifi_disconnect, NULL); ESP_LOGI(TAG, "esp_event_handler_register(WIFI_EVENT) : %d", ret); ret = esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &on_got_ip, NULL); ESP_LOGI(TAG, "esp_event_handler_register(IP_EVENT) : %d", ret); |
В качестве хранилища установим память
1 2 3 |
ESP_LOGI(TAG, "esp_event_handler_register(IP_EVENT) : %d", ret); ret = esp_wifi_set_storage(WIFI_STORAGE_RAM); ESP_LOGI(TAG, "esp_wifi_set_storage : %d", ret); |
В файле Kconfig.projbuild внизу добавим пункт для выбора способа сканирования сети
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
choice WIFI_SCAN_METHOD prompt "WiFi Scan Method" default WIFI_SCAN_METHOD_ALL_CHANNEL help WiFi scan method: If "Fast" is selected, scan will end after find SSID match AP. If "All Channel" is selected, scan will end after scan all the channel. config WIFI_SCAN_METHOD_FAST bool "Fast" config WIFI_SCAN_METHOD_ALL_CHANNEL bool "All Channel" endchoice |
Также добавим пункты для выбора методов сортировки имён точек доступа, установки порога мощности, режима порога и выбора метода шифрования
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 |
choice WIFI_CONNECT_AP_SORT_METHOD prompt "WiFi Connect AP Sort Method" default WIFI_CONNECT_AP_BY_SIGNAL help WiFi connect AP sort method: If "Signal" is selected, Sort matched APs in scan list by RSSI. If "Security" is selected, Sort matched APs in scan list by security mode. config WIFI_CONNECT_AP_BY_SIGNAL bool "Signal" config WIFI_CONNECT_AP_BY_SECURITY bool "Security" endchoice config WIFI_SCAN_RSSI_THRESHOLD int "WiFi minimum rssi" range -127 0 default -127 help The minimum rssi to accept in the scan mode. choice WIFI_SCAN_AUTH_MODE_THRESHOLD prompt "WiFi Scan auth mode threshold" default WIFI_AUTH_OPEN help The weakest authmode to accept in the scan mode. config WIFI_AUTH_OPEN bool "OPEN" config WIFI_AUTH_WEP bool "WEP" config WIFI_AUTH_WPA_PSK bool "WPA PSK" config WIFI_AUTH_WPA2_PSK bool "WPA2 PSK" config WIFI_AUTH_WPA_WPA2_PSK bool "WPA WPA2 PSK" config WIFI_AUTH_WPA2_ENTERPRISE bool "WPA2 ENTERPRISE" config WIFI_AUTH_WPA3_PSK bool "WPA3 PSK" config WIFI_AUTH_WPA2_WPA3_PSK bool "WPA2 WPA3 PSK" config WIFI_AUTH_WAPI_PSK bool "WAPI PSK" endchoice |
В конфигураторе выберем тип шифрования
Пункт ESP_MAXIMUM_RETRY можно удалить.
Сохраним конфигурацию и пересоберём проект.
Теперь в файле wifi.c добавим макросы, которые будут зависеть от настроек в конфигураторе
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 |
#include "wifi.h" //------------------------------------------------------------- #if CONFIG_WIFI_SCAN_METHOD_FAST #define WIFI_SCAN_METHOD WIFI_FAST_SCAN #elif CONFIG_WIFI_SCAN_METHOD_ALL_CHANNEL #define WIFI_SCAN_METHOD WIFI_ALL_CHANNEL_SCAN #endif //------------------------------------------------------------- #if CONFIG_WIFI_CONNECT_AP_BY_SIGNAL #define WIFI_CONNECT_AP_SORT_METHOD WIFI_CONNECT_AP_BY_SIGNAL #elif CONFIG_WIFI_CONNECT_AP_BY_SECURITY #define WIFI_CONNECT_AP_SORT_METHOD WIFI_CONNECT_AP_BY_SECURITY #endif //------------------------------------------------------------- #if CONFIG_WIFI_AUTH_OPEN #define WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_OPEN #elif CONFIG_WIFI_AUTH_WEP #define WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WEP #elif CONFIG_WIFI_AUTH_WPA_PSK #define WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_PSK #elif CONFIG_WIFI_AUTH_WPA2_PSK #define WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_PSK #elif CONFIG_WIFI_AUTH_WPA_WPA2_PSK #define WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_WPA2_PSK #elif CONFIG_WIFI_AUTH_WPA2_ENTERPRISE #define WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_ENTERPRISE #elif CONFIG_WIFI_AUTH_WPA3_PSK #define WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA3_PSK #elif CONFIG_WIFI_AUTH_WPA2_WPA3_PSK #define WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_WPA3_PSK #elif CONFIG_WIFI_AUTH_WAPI_PSK #define WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WAPI_PSK #endif //------------------------------------------------------------- |
В функции wifi_start объявим и проинициализируем переменную типа структуры для свойств станции
1 2 3 4 5 6 7 8 9 10 11 12 |
ESP_LOGI(TAG, "esp_wifi_set_storage : %d", ret); wifi_config_t wifi_config = { .sta = { .ssid = CONFIG_ESP_WIFI_SSID, .password = CONFIG_ESP_WIFI_PASSWORD, .scan_method = WIFI_SCAN_METHOD, .sort_method = WIFI_CONNECT_AP_SORT_METHOD, .threshold.rssi = CONFIG_WIFI_SCAN_RSSI_THRESHOLD, .threshold.authmode = WIFI_SCAN_AUTH_MODE_THRESHOLD, }, }; |
Отобразим попытку подключения в терминале, взяв имя точки доступа из поля структуры
1 2 3 4 |
.threshold.authmode = WIFI_SCAN_AUTH_MODE_THRESHOLD, }, }; ESP_LOGI(TAG, "Connecting to %s...", wifi_config.sta.ssid); |
Установим режим станции, нормальный режим работы WiFI без энергосбережения и применим конфигурационные установки для нашей станции
1 2 3 4 5 6 7 |
ESP_LOGI(TAG, "Connecting to %s...", wifi_config.sta.ssid); ret = esp_wifi_set_mode(WIFI_MODE_STA); ESP_LOGI(TAG, "esp_wifi_set_mode : %d", ret); ret = esp_wifi_set_ps(WIFI_PS_NONE); ESP_LOGI(TAG, "esp_wifi_set_ps : %d", ret); ret = esp_wifi_set_config(WIFI_IF_STA, &wifi_config); ESP_LOGI(TAG, "esp_wifi_set_config : %d", ret); |
Запустим станцию, попытаемся соединиться с точкой доступа и возвратим интерфейс
1 2 3 4 5 |
ESP_LOGI(TAG, "esp_wifi_set_config : %d", ret); ret = esp_wifi_start(); ESP_LOGI(TAG, "esp_wifi_start : %d", ret); esp_wifi_connect(); return netif; |
В функции-обработчике on_got_ip получим указатель на структуру события
1 2 3 4 |
static void on_got_ip(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; |
Выше добавим функцию, которая будет узнавать тип интерфейса
1 2 3 4 5 6 |
//------------------------------------------------------------- static bool is_our_netif(const char *prefix, esp_netif_t *netif) { return strncmp(prefix, esp_netif_get_desc(netif), strlen(prefix) - 1) == 0; } //------------------------------------------------------------- |
Вернемся в функцию on_got_ip и отобразим в терминале имя интерфейса, и, если это интерфейс типа IPV4, то отобразим и адрес
1 2 3 4 5 6 |
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; if (!is_our_netif(TAG, event->esp_netif)) { ESP_LOGW(TAG, "Got IPv4 from another interface \"%s\": ignored", esp_netif_get_desc(event->esp_netif)); return; } ESP_LOGI(TAG, "Got IPv4 event: Interface \"%s\" address: " IPSTR, esp_netif_get_desc(event->esp_netif), IP2STR(&event->ip_info.ip)); |
Объявим глобальный указатель на сетевой адрес
1 2 |
static const char *TAG = "wifi"; static esp_ip4_addr_t s_ip_addr; |
Вернемся в функцию on_got_ip, скопируем информацию в нашу переменную из поля структуры, зажжем светодиод и отдадим семафор
1 2 3 4 |
ESP_LOGI(TAG, "Got IPv4 event: Interface \"%s\" address: " IPSTR, esp_netif_get_desc(event->esp_netif), IP2STR(&event->ip_info.ip)); memcpy(&s_ip_addr, &event->ip_info.ip, sizeof(s_ip_addr)); gpio_set_level(CONFIG_LED_GPIO, 1); xSemaphoreGive(s_semph_get_ip_addrs); |
В функции-обработчике on_wifi_disconnect погасим светодиод и выведем о попытке разрыва соединения соответствующее сообщение в терминал
1 2 3 4 5 |
static void on_wifi_disconnect(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { gpio_set_level(CONFIG_LED_GPIO, 0); ESP_LOGI(TAG, "Wi-Fi disconnected, trying to reconnect..."); |
Попытаемся заново соединиться с точкой доступа, если невозможно, то выйдем из функции, в другом случае отобразим код ошибки, вдруг у нас какая-то другая ошибка
1 2 3 4 5 6 |
ESP_LOGI(TAG, "Wi-Fi disconnected, trying to reconnect..."); esp_err_t err = esp_wifi_connect(); if (err == ESP_ERR_WIFI_NOT_STARTED) { return; } ESP_LOGI(TAG, "esp_wifi_connect() : %d", err); |
В функции net_start увеличим количество интерфейсов на 1
1 2 |
s_esp_netif = wifi_start(); s_active_interfaces++; |
И здесь же мы создаём счётный семафор с количеством элементов, равным количеству интерфейсов
1 2 |
s_active_interfaces++; s_semph_get_ip_addrs = xSemaphoreCreateCounting(s_active_interfaces, 0); |
Теперь займёмся остановкой интерфейса, для чего выше добавим ещё одну функцию
1 2 3 4 5 6 |
//------------------------------------------------------------- static void wifi_stop(void) { esp_err_t ret; } //------------------------------------------------------------- |
Также ещё выше добавим функцию, которая будет вычислять интерфейс по имени
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
//------------------------------------------------------------- static esp_netif_t *get_example_netif_from_desc(const char *desc) { esp_netif_t *netif = NULL; char *expected_desc; asprintf(&expected_desc, "%s: %s", TAG, desc); while ((netif = esp_netif_next(netif)) != NULL) { if (strcmp(esp_netif_get_desc(netif), expected_desc) == 0) { free(expected_desc); return netif; } } free(expected_desc); return netif; } //------------------------------------------------------------- |
Здесь всё просто и объяснений, я думаю, никаких не требуется.
Вернёмся в функцию wifi_stop и получим наш интерфейс
1 2 |
esp_err_t ret; esp_netif_t *wifi_netif = get_example_netif_from_desc("sta"); |
Разрегистрируем обработчики событий
1 2 3 4 5 |
esp_netif_t *wifi_netif = get_example_netif_from_desc("sta"); ret = esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &on_wifi_disconnect); ESP_LOGI(TAG, "esp_event_handler_unregister(WIFI_EVENT) : %d", ret); ret = esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &on_got_ip); ESP_LOGI(TAG, "esp_event_handler_unregister(IP_EVENT : %d", ret); |
Попытаемся остановить WiFi
1 2 3 4 5 6 |
ESP_LOGI(TAG, "esp_event_handler_unregister(IP_EVENT : %d", ret); ret = esp_wifi_stop(); if (ret == ESP_ERR_WIFI_NOT_INIT) { return; } ESP_LOGI(TAG, "esp_wifi_stop : %d", ret); |
Деинициализируем WiFi
1 2 3 |
ESP_LOGI(TAG, "esp_wifi_stop : %d", ret); ret = esp_wifi_deinit(); ESP_LOGI(TAG, "esp_wifi_deinit : %d", ret); |
Очистим обработчики по умолчанию и уничтожим соответствующие объекты
1 2 3 |
ESP_LOGI(TAG, "esp_wifi_deinit : %d", ret); ret = esp_wifi_clear_default_wifi_driver_and_handlers(wifi_netif); ESP_LOGI(TAG, "esp_wifi_clear_default_wifi_driver_and_handlers : %d", ret); |
Уничтожим объект esp_netif
1 2 |
ESP_LOGI(TAG, "esp_wifi_clear_default_wifi_driver_and_handlers : %d", ret); esp_netif_destroy(wifi_netif); |
Очистим ссылку на интерфейс
1 2 |
esp_netif_destroy(wifi_netif); s_esp_netif = NULL; |
Перейдём в функцию net_stop и вызовем только что написанную нами функцию
1 2 3 |
static void net_stop(void) { wifi_stop(); |
Осталось нам только завершить кое-какие процедуры в функции инициализации wifi_init_sta. Сначала попытаемся забрать наши семафоры
1 2 3 4 |
ESP_LOGI(TAG, "esp_register_shutdown_handler(&net_stop) : %d", ret); for (int i = 0; i < s_active_interfaces; ++i) { xSemaphoreTake(s_semph_get_ip_addrs, portMAX_DELAY); } |
В данном цикле мы будем находиться до тех пор, пока не заберём все семафоры, то есть пока все наши интерфейсы не соединятся.
Зажжем светодиод и объявим указатель на интерфейс
1 2 3 4 |
xSemaphoreTake(s_semph_get_ip_addrs, portMAX_DELAY); } gpio_set_level(CONFIG_LED_GPIO, 1); esp_netif_t *netif = NULL; |
Объявим переменную типа структуры для сетевого адреса нашей станции
1 2 |
esp_netif_t *netif = NULL; esp_netif_ip_info_t ip; |
Добавим цикл с количеством итераций, равных количеству интерфейсов, в котором получим дескриптор очередного интерфейса
1 2 3 4 |
esp_netif_ip_info_t ip; for (int i = 0; i < esp_netif_get_nr_of_ifs(); ++i) { netif = esp_netif_next(netif); } |
Отобразим в терминале информацию о соединении
1 2 3 4 5 6 7 |
netif = esp_netif_next(netif); if (is_our_netif(TAG, netif)) { ESP_LOGI(TAG, "Connected to %s", esp_netif_get_desc(netif)); ret = esp_netif_get_ip_info(netif, &ip); ESP_LOGI(TAG, "esp_netif_get_ip_info : %d", ret); ESP_LOGI(TAG, "- IPv4 address: " IPSTR, IP2STR(&ip.ip)); } |
В функции app_main файла main.c добавим возврат параметра в вызов функции инициализации wifi
ret = wifi_init_sta();
Ниже выведем в терминал код ошибки
1 2 |
ret = wifi_init_sta(); ESP_LOGI(TAG, "wifi_init_sta: %d", ret); |
Также произведём инициализацию сетевого интерфейса немного выше
1 2 3 |
ESP_LOGI(TAG, "nvs_flash_init: 0x%04x", ret); ret = esp_netif_init(); ESP_LOGI(TAG, "esp_netif_init: %d", ret); |
Ну и наконец-то пришло время нам всё это проверить.
Прошьём контроллер и посмотрим информацию в терминале
Отлично! Вроде везде нолики, значит ошибок возвращённых нет.
Проверим пинг
Теперь попробуем отключить точку доступа на какое-то время.
Попытки соединиться с точкой доступа теперь происходят постоянно
Включим точку доступа и увидим, что соединение снова удачно установилось
Итак, на данном уроке нам удалось усовершенствовать нашу WiFi-станцию, в результате чего мы добились того, что нам уже не нужно в бесконечном цикле постоянно вызывать функцию инициализации, если превышено количество попыток соединения.
Всем спасибо за внимание!
Предыдущий урок Программирование МК ESP32 Следующий урок
Недорогие отладочные платы ESP32 можно купить здесь Недорогие отладочные платы ESP32
Недорогие отладочные платы ESP32/ESP32-C3/ESP32-S3 можно купить здесь Недорогие отладочные платы ESP32
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в Дзен (нажмите на картинку)
Добавить комментарий