Доброго здравия, уважаемые читатели!
Популярность SoC ESP32 (и последующих серий) в первую очередь обусловлена тем, что помимо достаточно мощного процессора, в нем имеется встроенный радиоканал для подключения к сетям WiFi и Bluetooth®. Я уже писал, как подключить ESP32 в режиме STA с использованием фреймворка Arduino, причем несколькими способами – в цикле и с помощью событий. Писал я как-то аналогичную статью и применительно для фреймворка ESP-IDF, но та статья получилась на мой взгляд откровенно ужасной, скомканной и непонятной. Исправлять её смысла не вижу, проще написать новую, а старую отправить “в архив”.
В рамках данной статьи я буду рассматривать в основном STA режим, то есть мы будем подключать наш микроконтроллер к существующей Wi-Fi сети (точке доступа).
Примечание для любителей Arduino: В данной статье рассматривается подключение к уже существующей сети WiFi в режиме станции (STA) с использованием фреймворка ESP-IDF. Но все описанное ниже применимо и для фреймворка Arduino ESP32 (за небольшим исключением – вы не сможете изменить параметры сборки через menuconfig). Кроме того, следует понимать, что класс WiFiClient для Arduino ESP32 использует ровно те же самые механизмы, которые описаны в данной статье. Поэтому данная статья может быть полезна и заядлым ардуинщикам, если вы пожелаете разобраться “а как оно работает”.
Данная статья на Дзене является сокращенной версией полной статьи: https://kotyara12.ru/iot/esp32-wifi2/. Кроме того, на основном сайте можно легко скопировать код примеров. Но где вам удобнее читать - решайте вы сами. Лайки и комментарии приветствуются, но не обязательны.
Введение
WiFi-радиоканал имеется на многих ESP‑чипах (но не во всех): ESP32, ESP32‑S2, ESP32‑S3, ESP32‑C2/C3/C5/C6/C61 и др., при этом используется одинаковая базовая модель работы. Поэтому, в рамках данной статьи я буду писать просто ESP или ESP32, подразумевая при этом всю линейку SoC, которая имеет физическую поддержку WiFi (если явно не указана конкретная линейка SoC).
1. Основные режимы работы Wi‑Fi на ESP
На SoC ESP предусмотрены следующие режимы работы Wi‑Fi-канала [ESP32 Wi-Fi; ESP32-S2 Wi-Fi; ESP32-C2 Wi-Fi; ESP32-C6 Wi-Fi; ESP32-C61 Wi-Fi]:
- STA (Station, клиентский режим) – ваше устройство ESP подключается к существующей точке доступа (роутеру). Это режим, когда ESP «как телефон / ноутбук» входит в уже существующую сеть. При этом ESP получает от роутера некоторый IP-адрес, определяемый роутером или точкой доступа. После авторизации и получения доступа к WiFi-сети, ESP получает доступ и к ресурсам всемирной паутины (но не всегда, так как это во многом зависит от настроек вашего роутера, к которому выполняется подключение).
В этом режиме, как правило, ESP самостоятельно инициирует подключения к известным ей серверам в сети и передает/получает какие-то данные. Хотя ничто не мешает принимать входящие подключения и в этом режиме (например для создания web-конфигуратора). - AP (Soft‑AP, режим точки доступа) – ESP само создаёт Wi‑Fi‑сеть, к которой могут подключаться другие устройства (телефон, ПК и т.п.). В этом случае ESP в общем случае не имеет доступа к сети Интернет.
Этот режим часто используется при создании так называемых web-интерфейсов. Вы подключаетесь к сети, созданной ESP, затем открываете определенный адрес в этой сети – после этого можно настраивать заранее определенные параметры ESP. - STA+AP (coexistence, смешанный или одновременный режим). В этом случае ESP одновременно:подключено как клиент к одной точке доступа ( в режиме STA),
и при этом само “раздаёт” Wi‑Fi как точка доступа (AP). - Sniffer / Promiscuous mode. Режим прослушивания Wi‑Fi‑кадров – мониторинг трафика 802.11, без полноценного подключения к сети. Используется для настройки параметров WiFi-драйвера с помощью специально сформированных кадров данных.
Специальные протоколы и режимы
Для некоторых серий SoC могут быть доступны специальные протоколы связи WiFi-модуля, которые разработаны Espressif и не являются стандартными (специальные режимы работают только между устройствами Espressif):
- ESP‑NOW — фирменный протокол Espressif для обмена данными между устройствами без использования классического Wi‑Fi‑подключения (низкая задержка, малый overhead)
- Long Range mode — расширенный режим передачи пакетов на большом расстоянии (до ~1 км) на частоте 2.4 ГГц без создания классического Wi‑Fi‑соединения.
Эти режимы доступны на ESP32 и ESP32‑C5 (для C5 — только на 2.4 ГГц). [ESP32 Wi-Fi overview; ESP32-C5 Wi-Fi overview] - NAN (Wi‑Fi Aware, Neighbor Awareness Networking). Технология обнаружения и взаимодействия устройств поблизости без классического подключения к AP. Позволяет устройствам обнаруживать друг друга и обмениваться данными без обязательного подключения к одной точке доступа.
2. Поддерживаемые стандарты и особенности протокола
2.1. Стандарты 802.11
- ESP32 (классический):IEEE 802.11b/g/n
Плюс фирменный режим 802.11 LR (Long Range). [ESP32 Wi-Fi overview; ESP-Drone Wi-Fi] - ESP32‑C5:IEEE 802.11b/g/n (2.4 ГГц)
IEEE 802.11a/ac/ax (5 ГГц)
Поддержка OFDMA, MU‑MIMO, BSS Color и др. [ESP32-C5 Wi-Fi overview]
2.2. Скорость и дальность
В документации указаны типичные характеристики:
- До 20 Mbit/s TCP и 30 Mbit/s UDP по воздуху.
- Дальность до 1 км в фирменном режиме Long Range (802.11 LR / ESP‑NOW Long Range) только для ESP32.
2.3. Безопасность и типы аутентификации
На ESP поддерживаются различные режимы безопасности:
- WPA / WPA2 / WPA3 (Personal)
- WPA2‑Enterprise / WPA3‑Enterprise
- WAPI, WPS, DPP (Device Provisioning Protocol)
2.4. Ключевые функции MAC‑уровня
Для ESP32/ESP32‑S/ESP32‑C перечислены важные функции MAC‑уровня:
- AMSDU, AMPDU — агрегация кадров для повышения пропускной способности.
- HT20/HT40 — изменение ширины канала 20/40 МГц (HT40 даёт более высокую скорость, HT20 даёт большую устойчивость связи при низком уровне сигнала).
- QoS — приоритизация трафика.
- Modem‑sleep — перевод WiFi в режим энергосбережения.
- Multiple antennas — поддержка нескольких антенн (на уровне драйвера).
- Channel State Information (CSI) — получение информации о состоянии канала передачи данных.
- OFDMA, MU‑MIMO, BSS Color (ESP32‑C5) — функции Wi‑Fi 6 для многопользовательской работы, доступны только для ESP32‑C5. [ESP32-C5 Wi-Fi overview]
Источники: [ESP32 Wi-Fi overview; ESP32-C5 Wi-Fi overview; ESP32-S2 Wi-Fi; ESP32-C3 Wi-Fi; ESP32 Wi-Fi MAC; ESP-Drone Wi-Fi]
Теория подключения к сети WiFi в режиме STA
В этом разделе мы попытаемся разобраться, “как оно устроено и работает“: какие задачи создаются, какие события происходят, какие действия необходимо предпринять.
1. Задачи, необходимые для функционирования WiFi-драйвера
За непосредственное “обслуживание” WiFi-соединений на ESP ответственны две служебные задачи: WiFi Task и LwIP Task. Они запускаются при старте WiFi-драйвера и работают в фоне. Но на самом деле для полноценного функционирования WiFi-драйвера необходима ещё одна служебная задача – Default Event Task. Все эти задачи запускаются и работают не только в ESP-IDF, а и при использовании Arduino ESP32.
WiFi Task (Wi-Fi Driver Task)
Это основная задача драйвера Wi-Fi. Для программиста она представлена как компонент esp_wifi, который управляет физическим уровнем и MAC-протоколами. Она отвечает за обработку низкоуровневых прерываний Wi-Fi, управление приемом/передачей кадров 802.11 и выполнение команд, поступающих через Wi-Fi API. По умолчанию привязана к Core 0 (настраивается через CONFIG_ESP_WIFI_TASK_CORE_ID), и имеет очень высокий приоритет FreeRTOS (23), что необходимо для обеспечения жестких временных рамок протокола Wi-Fi – не рекомендуется создавать прикладные задачи с более высоким приоритетом. [Wi-Fi Programming Model; Built-in Task Priorities].
LwIP Task (TCP/IP Task)
Специализированная задача для работы сетевого стека LwIP. Для программиста она представлена как API esp_netif. Отвечает за обработку сетевых пакетов (TCP, UDP, IP), управление сокетами и выполнение запросов Socket API от других задач. Данная задача создается автоматически при вызове esp_netif_init() и используется для любого типа TCP/IP соединений (в том числе WiFi, Ethernet, GPRS). Также, как и драйвер WiFi, эта задача имеет достаточно высокий приоритет — 18 (который можно настроить с помощью параметра ESP_TASK_TCPIP_PRIO, а также можно изменить размер стека CONFIG_LWIP_TCPIP_TASK_STACK_SIZE и привязку к ядру CONFIG_LWIP_TCPIP_TASK_AFFINITY) [lwIP FreeRTOS Task; Station Scenarios].
Event Task (Default Event Task)
Данная задача непосредственно не относится к стеку TCP/IP и WiFi-драйверу, но совершенно необходима для их нормального функционирования, так как драйвер построен на системе событий. Представлена в виде системы событий esp_event.
Default Event Task (иногда встречается как System Event Task) управляет системным циклом событий (default event loop). Когда в Wi-Fi драйвере что-то происходит (например, потеря связи), он отправляет событие в эту задачу. System Event Task вызывает зарегистрированные вами функции-обработчики (callbacks) [Wi-Fi Programming Model].
Данная задача должна быть создана перед запуском WiFi (или Ethernet) драйвера с помощью вызова esp_event_loop_create_default(). Имеет высокий приоритет — 20 (ESP_TASK_EVENT_PRIO) и всегда привязана к Core 0 [Built-in Task Priorities (ESP32)].
Примечание: для ваших прикладных задач вы можете создать дополнительный цикл событий, но драйвер WiFi может и должен работать только с системным циклом событий по умолчанию.
Кроме того, для нормальной работы драйвера может потребоваться NVS-раздел, поэтому перед запуском WiFi его необходимо проинициализировать. Но об этом мы ещё поговорим отдельно ниже. [NVS: энергонезависимая библиотека хранения параметров].
2. WiFi API (Application Program Interface)
Кратенько рассмотрим основные функции WiFi API. Их довольно много, поэтому я не стану подробно все их описывать, актуальные дополнительные сведения вы всегда сможете почерпнуть из официальной документации [Wi‑Fi driver; Wi‑Fi API reference; ESP extconn].
2.1. Инициализация и запуск
- esp_wifi_init / esp_wifi_deinit
Инициализирует / деинициализирует драйвер Wi‑Fi. Выделение ресурсов для драйвера WiFi, таких как структуры управления WiFi, буферы RX/TX, структуры WiFi NVS и т. д. Эта же команда также запускает задачу WiFi. Этот API необходимо вызвать до вызова всех остальных API Wi-Fi. - esp_wifi_start / esp_wifi_stop
Запускает / останавливает Wi‑Fi в соответствии с заданной конфигурацией — после start интерфейсы начинают работать в указанном режиме, после stop — прекращают передачу/приём. Перед использованием этого API необходимо выбрать режим работы с помощью esp_wifi_set_mode и esp_wifi_set_config.
2.2. Хранение и общая конфигурация
- esp_wifi_set_storage
Позволяет указать, где хранить параметры и конфигурацию Wi‑Fi (в RAM или NVS / FLASH). Например — используйте параметр WIFI_STORAGE_RAM, чтобы настройки действовали только до перезагрузки ESP. Значение по умолчанию — WIFI_STORAGE_FLASH. - esp_wifi_set_mode / esp_wifi_get_mode
Устанавливает / возвращает текущий режим работы: станция (WIFI_MODE_STA), точка доступа (WIFI_MODE_AP) или одновременно STA+AP (WIFI_MODE_APSTA), кроме того есть ещё и специальный режим NAN (WIFI_MODE_NAN). - esp_wifi_set_config / esp_wifi_get_config
Устанавливает / возвращает текущую структуру конфигурации интерфейса (для STA или AP): SSID, пароль, параметры сканирования, порог аутентификации и т.п. - esp_wifi_restore
Сбрасывает сохранённые настройки Wi‑Fi к значениям по умолчанию (включая данные, ранее записанные в NVS-разделе). Эта функция сбросит настройки, сделанные с использованием следующих API: esp_wifi_set_bandwidth, esp_wifi_set_protocol, esp_wifi_set_config, esp_wifi_set_mode.
2.3. Региональные параметры, протоколы, полоса и MAC
- esp_wifi_set_country / esp_wifi_get_country
Устанавливает / читает параметры страны (код страны, допустимые каналы, поведение 802.11d). Не рекомендуется использовать этот API, поскольку он не проверяет правила для каждой страны отдельно; поэтому пользователь должен будет заполнить все поля в соответствии с местными правилами. Вместо этого используйте следующую функцию esp_wifi_set_country_code(). - esp_wifi_set_country_code / esp_wifi_get_country_code
Более простой интерфейс для задания кода страны. При вызове этого API данные инициализации физического уровня переключатся на тип данных инициализации физического уровня, соответствующий информации о стране. Когда параметр ieee80211d_enabled включен, используется информация о стране точки доступа, к которой подключена станция. Настройки страны сохраняются во флэш-памяти. Поддерживаемые коды стран: “01” (безопасный режим для всего мира), “AT”, “AU”, “BE”, “BG”, “BR”, “CA”, “CH”, “CN”, “CY”, “CZ”, “DE”, “DK”, “EE”, “ES”, “FI”, “FR”, “GB”, “GR”, “HK”, “HR”, “HU”, “IE”, “IN”, “IS”, “IT”, “JP”, “KR”, “LI”, “LT”, “LU”, “LV”, “MT”, “MX”, “NL”, “NO”, “NZ”, “PL”, “PT”, “RO”, “SE”, “SI”, “SK”, “TW”, “US”. - esp_wifi_set_protocol / esp_wifi_get_protocol
Включает / считывает поддерживаемые протоколы 802.11 (b/g/n и др.) через битовую маску; что влияет на физический режим (phy mode). Этот API поддерживает установку максимального протокола. Например, если для протокола 2,4 ГГц установлен стандарт 802.11n, он автоматически настроится на 802.11b/g/n. - esp_wifi_set_bandwidth / esp_wifi_get_bandwidth
Устанавливает / считывает пропускную способность (20 МГц или 40 МГц) указанного интерфейса и указанного диапазона. Поддержка WIFI_BW_HT40 возможна только при наличии поддержки интерфейса 11N. Если интерфейс поддерживает 11AX/11AC, он поддерживает только настройку WIFI_BW_HT20. - esp_wifi_set_channel / esp_wifi_get_channel
Принудительно задаёт / возвращает номер канала. Этот API следует вызывать после esp_wifi_start() и перед esp_wifi_stop(). Когда устройство находится в режиме STA, этот API не следует вызывать во время сканирования или подключения к внешней точке доступа. Когда устройство находится в режиме SoftAP, этот API не следует вызывать, если SoftAP подключен к внешним станциям. Информация о канале, заданная этим API, не будет храниться в NVS. Поэтому, если вы хотите запомнить канал, использовавшийся до отключения Wi-Fi, вам нужно будет снова вызвать этот API после включения Wi-Fi, или вы можете вызвать функцию esp_wifi_set_config() для сохранения информации о канале в NVS. - esp_wifi_set_mac / esp_wifi_get_mac
Устанавливает / читает MAC‑адрес интерфейса (STA или AP). В esp‑hosted демо через эти функции реализованы команды set_sta_mac_addr, get_softap_mac_addr и т.п. [C demo; ESP extconn] - esp_wifi_set_max_tx_power
Установите максимальную мощность передачи после запуска Wi-Fi. Значение, установленное этим API, будет сопоставлено со значением max_tx_power переменной структуры wifi_country_t. Таблица соответствия {Power, max_tx_power} = {{8, 2}, {20, 5}, {28, 7}, {34, 8}, {44, 11}, {52, 13}, {56, 14}, {60, 15}, {66, 16}, {72, 18}, {80, 20}}. Один “шаг” мощности равен 0,25 дБм, то есть диапазон Power равный [8, 84] соответствует реальной мощности 2 дБм – 20 дБм. - esp_wifi_set_ps / esp_wifi_get_ps
Установить / считать текущий режим энергосбережения Wi-Fi. Возможны режимы WIFI_PS_NONE – без энергосбережения, WIFI_PS_MIN_MODEM – минимальное энергосбережение модема (в этом режиме станция просыпается для приема маяка каждые DTIM) и WIFI_PS_MAX_MODEM – максимальное энергосбережение модема (в этом режиме интервал приема маяков определяется параметром listen_interval в wifi_sta_config_t. Режим энергосбережения по умолчанию — WIFI_PS_MIN_MODEM.
2.4. Подключение и параметры STA или AP
- esp_wifi_connect / esp_wifi_disconnect
Запускает процесс подключения станции к AP / разрывает текущее подключение. Данный API влияет только на режимы WIFI_MODE_STA или WIFI_MODE_APSTA. Используется только после предварительной настройки с помощью wifi_config_t и запуска esp_wifi_start(), чаще всего в callback-обработчиках событий. Данный API пытается подключиться к точке доступа (AP) только один раз. Чтобы включить повторное подключение в случае сбоя соединения, используйте параметр failure_retry_cnt в wifi_sta_config_t. Пользователю рекомендуется реализовать логику повторного подключения в своих приложениях для случаев, когда указанная точка доступа внезапно отсутствует или требуется повторное подключение после того, как устройство получило событие отключения. - esp_wifi_sta_get_ap_info
Возвращает информацию о текущей точке доступа AP, к которой подключена станция ESP (SSID, BSSID, канал, RSSI и др.). - esp_wifi_ap_get_sta_list / esp_wifi_ap_get_sta_list_with_ip
Для режима AP возвращает список станций, подключённых к SoftAP (MAC‑адреса, при расширенном варианте — и IP). - esp_wifi_deauth_sta
Для режима AP принудительно отключает (deauth) конкретную станцию от SoftAP по её MAC‑адресу. - esp_wifi_sta_get_rssi
Возвращает текущий уровень сигнала (RSSI) для подключённой точки доступа. Действительно только для режимов WIFI_MODE_STA или WIFI_MODE_APSTA. - esp_wifi_sta_get_negotiated_phymode
Возвращает согласованный физический режим (например, 11b/g/n) для текущего соединения. Действительно только для режимов WIFI_MODE_STA или WIFI_MODE_APSTA. - esp_wifi_set_inactive_time / esp_wifi_get_inactive_time
Настраивает / читает время неактивности станции (используется для управления поведением при длительном отсутствии трафика). Данный API добавлен в IDF 4.2. - esp_wifi_set_rssi_threshold
Устанавливает порог RSSI, который может использоваться при выборе точки доступа или принятии решения о подключении; если среднее значение RSSI станет ниже порогового значения, задача WiFi отправит событие WIFI_EVENT_STA_BSS_RSSI_LOW. Если пользователь хочет получить еще одно событие WIFI_EVENT_STA_BSS_RSSI_LOW после получения предыдущего, необходимо повторно вызвать этот API с обновленным или тем же самым пороговым значением RSSI.
2.5. Сканирование
- esp_wifi_scan_start / esp_wifi_scan_stop
Запускает / останавливает сканирование доступных точек доступа. При вызове этого API найденные точки доступа сохраняются в динамически выделяемой памяти драйвера WiFi. Затем их нужно освободить в функциях esp_wifi_scan_get_ap_records(), esp_wifi_scan_get_ap_record() или esp_wifi_clear_ap_list(), поэтому вызовите любую из них, чтобы освободить память после завершения сканирования. Максимальное время активного и пассивного сканирования на канал ограничено 1500 миллисекундами. Значения выше 1500 мс могут привести к отключению станции от точки доступа и не рекомендуются. - esp_wifi_set_scan_parameters / esp_wifi_get_scan_parameters
Настраивает / читает параметры сканирования (список каналов, активное/пассивное сканирование, тайминги и т.п.). - esp_wifi_scan_get_ap_num
Возвращает количество найденных AP после завершения сканирования. - esp_wifi_scan_get_ap_records / esp_wifi_scan_get_ap_record
Возвращает список записей / одну запись о найденных точках доступа (SSID, BSSID, канал, RSSI, тип шифрования и др.). Возвращаемый список точек доступа отсортирован в порядке убывания по показателю RSSI. esp_wifi_scan_get_ap_records освободит всю память, занятую списком отсканированных точек доступа. esp_wifi_scan_get_ap_record освобождает память, выделенную под одну запись о точке доступа; если пользователь не получает все записи из списка отсканированных точек доступа, ему необходимо вызвать esp_wifi_clear_ap_list() для освобождения оставшейся памяти. - esp_wifi_clear_ap_list
Очищает сохранённый список результатов сканирования.
2.6. Прочее
- esp_wifi_set_vendor_ie / esp_wifi_set_vendor_ie_cb
Позволяет добавить в кадры Wi‑Fi (beacon, probe, assoc и др.) пользовательский Vendor‑Specific IE и зарегистрировать callback для обработки таких IE.
3. События WiFi-драйвера
В ESP-IDF процесс подключения к WiFi асинхронный и его функционирование базируется на системе событий, то есть библиотеке esp_event. Когда в Wi-Fi драйвере что-то происходит, он генерирует событие и отправляет его в цикл обработки событий по умолчанию. Ваше приложение может обрабатывать эти события в ваших callback-функциях обратного вызова, зарегистрированных с помощью esp_event_handler_register(). Реагируя на соответствующие события, можно легко управлять процессами подключения, отключения и переподключения к сети.
Кроме того, события Wi-Fi драйвера также обрабатываются и системным сетевым интерфейсом esp_netif, который предоставляет свой обработчик (default handler) с набором стандартных сценариев поведения. Например, когда станция Wi-Fi подключается к точке доступа, esp_netif автоматически запускает DHCP-клиент по умолчанию.
Создание функций обратного вызова мы обсудим несколько позже, а пока рассмотрим сами события (иначе в дальнейших рассуждениях будет ничего не понятно). Их список довольно обширен, но в рамках данной статьи нас интересуют только те из них, которые непосредственно связаны в STA-режимом: WIFI_EVENT_STA_... :
WIFI_EVENT_WIFI_READY
В текущей версии ESP-IDF драйвер Wi-Fi ни при каких условиях не генерирует это событие, которое к тому же может быть проигнорировано вашей функцией обратного вызова обработки событий приложения. Это событие может быть удалено в будущих версиях.
WIFI_EVENT_SCAN_DONE
Событие “сканирование завершено” генерируется функцией esp_wifi_scan_start() и возникает в следующих случаях:
- Сканирование полностью завершено, когда например, искомая точка доступа успешно обнаружена или были просканированы все каналы.
- Функция esp_wifi_scan_start() вновь вызывается до завершения текущего сканирования. Новое сканирование остановит текущее, и соответственно будет сгенерировано событие завершения сканирования.
Событие “Сканирование завершено” не возникает в следующих сценариях:
- Это заблокированное сканирование.
После получения этого события системная задача обработки сетевых событий (default handler) ничего не предпринимает. А вот функция обратного вызова вашего приложения (user handler) должна обязательно вызвать методы esp_wifi_scan_get_ap_num() и esp_wifi_scan_get_ap_records() для получения списка обнаруженных точек доступа и затем ещё более обязательно выполнить освобождение памяти, выделенной во время сканирования драйвером Wi-Fi (еще раз – не забудьте это сделать!). Более подробное описание этого события см. в документации по сканированию Wi-Fi на ESP32.
WIFI_EVENT_STA_START
Данное событие генерируется, если функция esp_wifi_start() вернула ESP_OK и текущий режим Wi-Fi — station или station/AP. После получения этого события системная задача обработки событий (default handler) инициализирует сетевой интерфейс LwIP (netif). При получении данного события, ваша функция обратного вызова обработки событий приложения как правило должна вызвать функцию esp_wifi_connect() для подключения к заранее настроенной точке доступа.
WIFI_EVENT_STA_STOP
Данное событие генерируется, если функция esp_wifi_stop() вернула ESP_OK и текущий режим Wi-Fi — station или station/AP. После получения этого события системная задача обработки событий (default handler) освободит ранее полученный IP-адрес, остановит DHCP-клиент, удалит все соединения, связанные со стеком TCP/UDP, очистит сетевой интерфейс станции LwIP и т. д. Как правило, от функции обратного вызова обработки событий уровня приложения (user handler) ничего не требуется.
WIFI_EVENT_STA_CONNECTED
Данное событие генерируется, если функция esp_wifi_connect() вернула ESP_OK и ESP-станция успешно подключается к заданной точке доступа. После получения этого события системная задача обработки событий (default handler) запускает DHCP-клиент и инициирует процесс по получению IP-адреса. Затем драйвер Wi-Fi будет готов к отправке и приему данных. Этот момент хорош для начала работы вашего сетевого приложения, но только при условии, что это приложение не зависит от LwIP, то есть от IP-адреса. Однако, если ваше приложение основано на LwIP, то необходимо дождаться события получения IP-адреса. То есть в большинстве случаев в пользовательском обработчике (user handler) ничего предпринимать не требуется.
WIFI_EVENT_STA_DISCONNECTED
Это событие может возникнуть в следующих сценариях:Когда вызывается esp_wifi_disconnect() или esp_wifi_stop(), и если при этом станция уже была подключена к точке доступа.
Если была вызвана esp_wifi_connect(), но драйвер Wi-Fi не может установить соединение с точкой доступа по определенным причинам ну не шмогла я, не шмогла, например, сканирование не обнаружило заданную точку доступа или истекло время ожидания аутентификации. Если в процессе сканирования было найдено более одной точки доступа с одинаковым SSID, событие WIFI_EVENT_STA_DISCONNECTED будет сгенерировано только после того, как станция не сможет подключиться ко всем найденным точкам доступа.
Когда соединение Wi-Fi прерывается по определенным причинам, например, станция постоянно теряет N-маяки, точка доступа отключает станцию или изменяется режим аутентификации точки доступа.
После получения этого события default handler по умолчанию выполняет следующее:Отключение сетевого стека LwIP.
Уведомление задачи LwIP о необходимости очистки UDP/TCP-соединений, вызывающих некорректный статус всех сокетов.
Для приложений, напрямую работающих с сокетами, функция обратного вызова приложения может закрыть все сокеты после получения этого события.
Наиболее распространенный сценарий обработки данного события в user handler — это вызов функции esp_wifi_connect() для попытки повторного подключения к точке доступа Wi-Fi. Однако, если событие возникает из-за принудительного вызова esp_wifi_disconnect(), ваше приложение не должно вызывать функцию esp_wifi_connect() для повторного переподключения. Ваше приложение должно самостоятельно определить, вызвано ли событие вызовом функции esp_wifi_disconnect() или другими внешними причинами. Иногда приложению требуется более эффективная стратегия для повторного подключения – например, чтобы попытаться подключиться к резервной точке доступа (cм. раздел «Повторное подключение к Wi-Fi и сканирование при подключении к Wi-Fi»).Ещё один момент, заслуживающий особого внимания, — это то, что по умолчанию LwIP разрывает все TCP-соединения при получении сообщения WIFI_EVENT_STA_DISCONNECTED. В большинстве случаев это не проблема. Однако для некоторых специальных приложений это может быть нежелательно, например:Приложение устанавливает TCP-соединение для поддержания связи на уровне приложения keep-alive, данные для обеспечения активности которого отправляются каждые 60 секунд.
По определенным причинам соединение Wi-Fi обрывается, и возникает событие WIFI_EVENT_STA_DISCONNECTED. Согласно реализации по умолчанию, все TCP-соединения будут разорваны, и сокет keep-alive окажется в некорректном состоянии. Однако, поскольку разработчик приложения считает, что сетевой уровень должен игнорировать эту ошибку на уровне Wi-Fi, приложение не закрывает сокет.
Допустим, через пять секунд соединение Wi-Fi восстанавливается, поскольку в обработчике событий приложения вызывается esp_wifi_connect(). Более того, станция подключается к той же точке доступа и получает тот же IPv4-адрес, что и раньше (срок аренды не истек).
Через пятьдесят секунд, когда приложение попытается отправить данные через постоянно активный keep-alive сокет, этот сокет вернет ошибку, и приложение вынуждено будет закрыть сокет и создать его заново.
В описанном выше сценарии в идеале сокеты приложения и сетевой уровень вовсе не должны быть затронуты, поскольку соединение Wi-Fi обрывается лишь временно и очень быстро восстанавливается.
IP_EVENT_STA_GOT_IP
Это событие (а также два, описанные ниже) генерируется уже не WiFi-драйвером, а DHCP-клиентом (который в свою очередь управляется промежуточным универсальным сетевым слоем esp_netif). Это событие возникает, когда DHCP-клиент успешно получает IPv4-адрес от DHCP-сервера или когда IPv4-адрес изменяется. Данное событие означает, что вот это всё уже, всё готово, и приложение может начать свою работу (например, создание сокетов).IP-адрес IPV4 может быть изменен по следующим причинам:DHCP-клиент не может обновить/перепривязать IPV4-адрес, и IPV4-адрес станции сбрасывается до 0.
DHCP-клиент переназначается на другой адрес по инициативе роутера.
Был изменен статический IPv4-адрес.
Изменен ли IPv4-адрес или нет, указывается в структуре данных события ip_event_got_ip_t в поле ip_change.Сокеты основаны на IPv4-адресе, а это значит, что если IPv4-адрес изменится, все сокеты, связанные с этим IPv4-адресом, станут неактивными. Поэтому после получения этого события ваше приложение (user handler) должно закрыть все активные сокеты и перезапустить сетевые приложения, когда IPv4-адрес изменится на действительный.
IP_EVENT_GOT_IP6
Это событие возникает, когда поддержка IPV6 SLAAC автоматически настраивает адрес для ESP32 или когда этот адрес изменяется. Событие означает, что всё готово, и приложение может начать выполнять свои задачи, например, создавать сокеты.
IP_EVENT_STA_LOST_IP
Это событие генерируется, когда IPv4-адрес становится недействительным.Данное событие IP_EVENT_STA_LOST_IP не возникает непосредственно сразу же после отключения от Wi-Fi точки доступа. Вместо этого запускается таймер потери IPV4-адреса (он настраивается с помощью параметров CONFIG_ESP_NETIF_LOST_IP_TIMER_ENABLE и CONFIG_ESP_NETIF_IP_LOST_TIMER_INTERVAL ). Если IPV4-адрес заново получен до истечения таймера, событие IP_EVENT_STA_LOST_IP не генерируется. В противном случае по истечении времени таймера и генерируется указанное событие.В большинстве случаев, ваше приложение может игнорировать это событие, поскольку это всего лишь отладочное событие, информирующее о потере IPv4-адреса.
WIFI_EVENT_STA_BEACON_TIMEOUT
Если станция не получает сигнал маяка от подключенной точки доступа в течение заданного времени неактивности, происходит таймаут сигнала маяка, и возникает ошибка WIFI_EVENT_STA_BEACON_TIMEOUT . Чаще всего, после этого ESP “теряет” соединение и генерируется событие WIFI_EVENT_STA_DISCONNECTED. Ваше приложение может изменить время неактивности через API функцию esp_wifi_set_inactive_time().
4. Общий сценарий подключения
Общий сценарий работы ESP32 в режиме станции разделен на несколько условных логических фаз (я тебе говорил – “фаза”, а ты “ноль” – “ноль”!). Типичный сценарий, включая описанные выше события и участвующие в этом задачи, представлен на схеме ниже [Wi-Fi Station General Scenario]. Выглядит может быть и страшновато, а на деле все довольно просто:
Примечание: здесь Main task – задача, запускаемая при старте ESP32 посредством функции app_main(), App task – прикладная задача, которая создана из app_main(). App task контролирует и управляет процессом подключения к WiFi в данном примере. Но на практике это совсем не обязательно, и App Task может отсутствовать вовсе.
В целом, создать код для «солнечных» (оптимистичных) сценариев, включающих только обработку событий WIFI_EVENT_STA_START и WIFI_EVENT_STA_CONNECTED, весьма несложно. Сложнее написать сценарии «дождливого дня», предусматривающих обработку ошибок и событий типа WIFI_EVENT_STA_DISCONNECTED. Эффективная обработка «дождливых» сценариев имеет фундаментальное значение для надежных приложений Wi-Fi. Всё это мы обсудим ниже.
4.1. Фаза инициализации (Wi-Fi/LwIP Init Phase)
На этом этапе создается программная и аппаратная среда для работы сети – запускается стек TCP/IP (LwIP), система событий и выполняется подготовка драйвера Wi-Fi. На этом этапе необходимо выполнить следующие действия:
- Вызвать esp_netif_init() для инициализации стека LwIP и создания задачи управления сетевыми интерфейсами (если это не было сделано ранее).
- Создать системный цикл событий (Event Loop) с помощью esp_event_loop_create_default() (если это не было сделано ранее).
- Создать интерфейс станции через esp_netif_create_default_wifi_sta(). Это связывает драйвер Wi-Fi со стеком TCP/IP.
- Вызвать esp_wifi_init(), чтобы выделить ресурсы для драйвера Wi-Fi и запустить задачу Wi-Fi Driver Task [Wi-Fi Operation Steps].
- Зарегистрировать обработчики событий WIFI_EVENT_STA_*, которые вы должны предварительно создать (пока этот этап опустим и обсудим подробнее ниже)
Простейший пример фазы инициализации:
Но пример выше не обрабатывает ошибки, которые могут возникнуть в процессе.
Этот код уже соответствует букве закона почти всем требованиям. Выглядит это, конечно, громоздко, но зато это более надежно.
Примечания:
- Код проверки на наличие ошибок вполне можно “обернуть” в макрос ESP_ERROR_CHECK() или его аналоги (но не во всех случаях) – это будет выглядеть более компактно.
- Разумеется, nvs_flash_init() и esp_event_loop_create_default() нужно выполнять, только если вы не сделали этого ранее где-то в другой части прошивки.
4.2. Фаза конфигурации (Wi-Fi Config Phase)
После инициализации необходимо настроить параметры конкретного подключения, чтобы драйвер знал, к какой сети подключаться и какие методы защиты использовать. На этом этапе необходимо выполнить следующие действия:
- Вызвать esp_wifi_set_mode(WIFI_MODE_STA) и затем esp_wifi_set_config(), передав структуру с SSID и паролем для целевой точки доступа.
- Здесь же можно настроить другие параметры подключения, например параметры сканирования, шифрование, ширину канала и т.д. и т.п.
Параметры конфигурации сети wifi_config_t выглядят следующим образом:
Для STA режима нас интересует структура wifi_sta_config_t (ниже я опустил добрую половину “не очень интересных” полей для сокращения записи):
Некоторые параметры wifi_sta_config_t стоит рассмотреть более подробно:
- ssid – Строковый идентификатор (SSID) целевой точки доступа, к которой мы хотим подключить ESP.
- password – Пароль целевой точки доступа.
- scan_method – Метод сканирования, имеет два варианта WIFI_FAST_SCAN и WIFI_ALL_CHANNEL_SCAN. При сканировании в режиме WIFI_FAST_SCAN поиск завершается, как только будет найдена первая точка доступа, соответствующая заданным критериям. При сканировании WIFI_ALL_CHANNEL_SCAN поиск сканирует все доступные частоты и находит все имеющиеся точки доступа, что занимает заметно больше времени. По умолчанию выполняется сканирование WIFI_FAST_SCAN.
- bssid_set – Если это значение равно 0, станция пробует подключиться к первой точке доступа, SSID которой совпадает с полем «ssid», а MAC-адрес «bssid» игнорируется. Во всех остальных случаях станция подключается к точке доступа, SSID которой совпадает с полем «ssid», а BSSID — с полем «bssid».
- bssid – MAC-адрес целевой точки доступа. Действительно только в случае если «bssid_set» = 1.
- channel – Если channel равен 0, станция сканирует каналы с 1 по N в поисках целевой точки доступа. В противном случае станция начинает сканирование с канала, номер которого совпадает со значением в поле «channel», а затем сканирует каналы с 1 по N, пропуская конкретный канал, чтобы найти целевую точку доступа. Например, если канал равен 3, порядок сканирования будет таким: 3, 1, 2, 4,…, N. Это позволяет ускорить процесс сканирования. Если вы не знаете, на каком канале работает целевая точка доступа, установите значение 0.
- sort_method – Это поле действительно только для включенной опции WIFI_ALL_CHANNEL_SCAN.Если sort_method имеет значение WIFI_CONNECT_AP_BY_SIGNAL, найденные точки доступа сортируются по уровню сигнала, и первой будет выбрана точка доступа с лучшим сигналом. Например, станция хочет подключиться к точке доступа с SSID «apxx». Если сканирование обнаружит две точки доступа с SSID «apxx», при этом уровень сигнала первой точки доступа составляет -90 дБм, а второй — -30 дБм, станция сначала подключится ко второй точке доступа и не будет подключаться к первой, пока не удастся подключиться ко второй.
Если sort_method имеет значение WIFI_CONNECT_AP_BY_SECURITY, найденные точки доступа сортируются по уровню безопасности. Например, станция хочет подключиться к точке доступа с SSID «apxx». Если сканирование обнаружит две точки доступа с SSID «apxx» и уровень безопасности первой из них будет открытым, а второй — WPA2, станция сначала подключится ко второй точке доступа и не будет подключаться к первой, пока не удастся подключиться ко второй. - threshold – Пороговые значения RSSI и минимального уровня безопасности, которые используется для фильтрации найденных точек доступа. Если уровень сигнала или режим безопасности ниже установленного порога, точка доступа будет отклонена. Если значение RSSI равно 0, то используется пороговое значение по умолчанию, что составляет -127 dBi. Если пороговое значение authmode равно 0, то используется пороговое значение по умолчанию «открыто».
- rm_enabled – Отвечает за включение/выключение RRM (Radio Resource Management; IEEE 802.11k) на стороне станции.
- btm_enabled – Включено ли управление BSS Transition Management (WNM BTM; IEEE 802.11v) для данного соединения. BSS Transition Management (Basic Service Set — Transition Management) — функция в беспроводных сетях Wi-Fi, которая управляет бесшовным переходом между точками доступа (AP). Цель — сделать процесс роуминга (переключения между соседними точками доступа) более плавным, сократить время, затрачиваемое на переключение.
Рекомендуется не задавать «bssid» при включенном BTM. - mbo_enabled – Разрешена ли поддержка MBO для данного соединения. MBO (Multi Band Operation) — технология в Wi-Fi-сетях, которая позволяет эффективно использовать несколько частотных диапазонов или каналов. Основана на стандартах 802.11ax и 802.11k/v/r.
Рекомендуется не задавать «bssid» при включенном MBO. Включение MBO автоматически включает «rm_enabled» и «btm_enabled».
Не обязательно заполнять всю эту структуру wifi_sta_config_t. В простейшем случае достаточно первых двух полей, и их мы захардкодим в виде макросов #define (которые компилятор потом неявно преобразует в const char[]).
По большому счету, для подключения этого уже будет вполне достаточно.
...
4.3. Фаза запуска (Wi-Fi Start Phase)
Фактическая активация драйвера Wi-Fi.
Важно! К этому моменту должны быть созданы и зарегистрированы обработчики событий WIFI_EVENT и IP_EVENT! Обработчики событий мы обсудим немного позже, я пока опустил это, чтобы “не мешать всё в кучу”. Но просто учтите это в своих разработках.
- Вызвать esp_wifi_start(), после чего драйвер опубликует системное событие WIFI_EVENT_STA_START.
- Драйвер Wi-Fi отправляет событие WIFI_EVENT_STA_START сначала системной задаче обработки событий; после чего эта задача выполнит ряд стандартных действий и вызовет функцию обратного вызова для событий приложения.
- Рекомендуется создать обработчик этого события (WIFI_EVENT_STA_START) и вызывать esp_wifi_connect() внутри этого обработчика, чтобы запустить процесс подключения к точке доступа [ESP32-S3 Wi-Fi Station General Scenario].
Простейший код может выглядеть как-то так:
4.4. Фаза подключения (Wi-Fi Connect Phase)
Внутренний процесс установки связи, который включает сканирование, аутентификацию и ассоциацию.
Запускается с помощью esp_wifi_connect(), который обычно находится внутри обработчика события WIFI_EVENT_STA_START. Минимальный код может уместиться в одну строчку:
На этом этапе выполняются следующие процессы:
- Scan: поиск целевой точки доступа. Если заданная точка доступа не найдена, генерируется событие WIFI_EVENT_STA_DISCONNECTED с кодом причины (например, WIFI_REASON_NO_AP_FOUND).
- Auth/Association: Обмен пакетами аутентификации и запросами на ассоциацию. Ошибки на этом этапе (неверный пароль и т.д.) также приводят к событию отключения
- WIFI_EVENT_STA_DISCONNECTED с другим кодом причины.
- При успешном завершении подключения генерируется событие WIFI_EVENT_STA_CONNECTED (на которое можно в принципе не реагировать). Это также запускает DHCP-клиент в стеке LwIP, чтобы клиент мог получить сетевой IP-адрес [ESP32 Wi-Fi Connect Phase].
Важно: Ничего не предпринимаем, просто тупо сидим на попе ровно и ждем завершения следующей фазы. До получения события IP_EVENT_STA_GOT_IP нельзя начинать работу с сокетами (создавать TCP/UDP соединения).
4.5. Фаза получения IP (Wi-Fi ‘Got IP’ Phase)
Финальный этап подготовки сетевого подключения. На этом этапе происходит получение сетевого адреса от DHCP-сервера точки доступа. Запускать какие-либо команды API при этом не нужно – выделение динамического IP-адреса происходит автоматически и по инициативе роутера.
Когда адрес будет успешно получен, LwIP генерирует событие IP_EVENT_STA_GOT_IP. Вот теперь можно начинать работу с сетевыми службами и создавать TCP/UDP соединения [ESP32 Wi-Fi ‘Got IP’ Phase]. Можно также напечатать IP-адрес в “консоль”:
Важно: До получения события IP_EVENT_STA_GOT_IP нельзя начинать работу с сокетами (создавать TCP/UDP соединения). Это одна из самых частых ошибок при разработке [ESP32 Wi-Fi ‘Got IP’ Phase].
4.6. Фаза отключения Wi-Fi (Wi-Fi Disconnect Phase)
Эта фаза не является фазой подключения, но рано или поздно она всё равно неизбежно происходит. Она происходит, когда связь с точкой доступа (AP) разрывается по любой причине: инициативе самого устройства, точки доступа или из-за внешних факторов:
- Программное “контролируемое” отключение через вызов функций esp_wifi_disconnect() или esp_wifi_stop(), если станция уже подключена.
- Сбой при вызове esp_wifi_connect(), например, если не найдена точка доступа или истекло время аутентификации.
- Разрыв соединения из-за помех или потери сигнала (потеря N маяков/beacons), принудительное отключение со стороны точки доступа или изменение режима аутентификации на точке доступа [WIFI_EVENT_STA_DISCONNECTED].
При этом на уровне WiFi драйвера генерируется событие WIFI_EVENT_STA_DISCONNECTED, что уведомляет ваше приложение и стек LwIP о необходимости закрыть все UDP/TCP соединения. После этого все сокеты приложения переходят в некорректное состояние и не могут работать [Wi-Fi Disconnect Phase].
Что необходимо сделать вашему приложению:
- Если вашему приложению требуется постоянное подключение к WiFi, то рекомендуется вызвать esp_wifi_connect() для повторного подключения (за исключением случаев, когда отключение было вызвано намеренно через esp_wifi_disconnect()).
- Закрыть все открытые сокеты и создать их заново после восстановления связи [Wi-Fi Disconnect Phase; Wi-Fi Reconnect (ESP32)].
Например, если вам необходимо поддерживать постоянное WiFi-подключение, то можно сразу же попытаться его восстановить через esp_wifi_connect();
4.7. Фаза смены IP-адреса (IP Address Change Phase)
Эта фаза возникает, если IP-адрес устройства изменяется (например, при переподключении к другой сети или по истечении срока аренды адреса на DHCP-сервере). Когда LwIP обнаруживает изменение IP-адреса, вновь генерируется событие IP_EVENT_STA_GOT_IP, и, если адрес изменился по сравнению с предыдущим, поле ip_change в структуре данных события устанавливается в true.
Последствия для системы и приложения: как и при физическом отключении, все существующие соединения TCP/UDP становятся невалидными, так как они привязаны к старому IP-адресу.
Что необходимо сделать вашему приложению:
- При получении события с флагом изменения адреса приложение должно закрыть все текущие сокеты и инициировать новые соединения. Это критически важно, так как старые сокеты больше не смогут передавать или принимать данные.
Важное замечание: По умолчанию LwIP настроен на немедленный разрыв всех TCP-соединений при получении события о дисконнекте, чтобы избежать «зависших» состояний сокетов в приложении [WIFI_EVENT_STA_DISCONNECTED].
4.8. Фаза деинициализации Wi-Fi (Wi-Fi Deinit Phase)
Эта фаза может использоваться для корректного завершения работы драйвера и освобождения ресурсов системы после отключения от WiFi, если оно вам больше не понадобится. Например, если ваше устройство подключается к сети лишь периодически, а большую часть времени находится в offline. Если же Вы будете использовать WiFi-драйвер в течение всего runtime вашего устройства, то необходимости в деинициализации нет.
Согласно документации ESP-IDF, для полной деинициализации рекомендуется выполнить следующие шаги в строгой последовательности [FAQ: Disable Wi-Fi; ESP32 Wi-Fi Deinit Phase]:
- Разрыв соединения: вызовите функцию esp_wifi_disconnect(), чтобы корректно завершить текущее Wi-Fi соединение.
- Остановка драйвера: вызовите esp_wifi_stop(). После этого генерируется событие WIFI_EVENT_STA_STOP, система освобождает IP-адрес, останавливает DHCP-клиент и удаляет связанные TCP/UDP соединения.
- Выгрузка драйвера: Вызовите esp_wifi_deinit(), чтобы полностью выгрузить драйвер, задачи связанные с ним, и вернуть ресурсы, которые они занимали.
Дополнительно в процессе очистки следует выполнить очистку сетевых интерфейсов и обработчиков событий:
- Очистить стандартные обработчики драйвера через esp_wifi_clear_default_wifi_driver_and_handlers().
- Удалить сетевой интерфейс с помощью esp_netif_destroy().
- Отменить регистрацию экземпляров обработчиков событий (unregister event handlers) для событий Wi-Fi и IP [Wi-Fi Shutdown and Cleanup].
5. Создание обработчиков событий
Как вы, наверное, уже поняли, перечисленные выше фазы вовсе не обязательно следуют строго последовательно друг за другом (несмотря на то, что они пронумерованы). Начиная с фазы запуска процесс подключения идет асинхронно и фазы могут сменять друг друга в зависимости от внешних условий и (или) логики вашей программы. Да и сроки завершения той или иной фазы заранее никому не известны.
Например после события WIFI_EVENT_STA_CONNECTED совсем не обязательно прилетит ожидаемое событие IP_EVENT_STA_GOT_IP, вполне может быть и событие WIFI_EVENT_STA_DISCONNECTED. А целенаправленно ожидать событие получения IP-адреса IP_EVENT_STA_GOT_IP сразу вскоре после события WIFI_EVENT_STA_START может и вовсе безнадежным занятием.
Вы можете написать один единый обработчик и для группы событий WIFI_EVENT, и для группы IP_EVENT (в примерах, как правило, так и сделано). А можете сделать раздельными. Как вам больше нравится. Это не играет вообще никакой роли и ни на что не влияет. Лично я предпочитаю в реальных проектах “разделить” группы событий WIFI_EVENT и IP_EVENT по разным обработчикам. Это позволяет убрать лишние проверки типа if (event_base == WIFI_EVENT) в обработчике и выиграть несколько байт и тактов процессора ;). Можно даже написать индивидуальный обработчик для каждого события в отдельности, вот только и регистрировать их также придется индивидуально, а вот это, как правило, уже сильно лениво.
5.1. Пример обработчика для целей отладки и тестирования
Ниже приведен пример простейшего обработчика, который:
- принимает все события групп WIFI_EVENT и IP_EVENT,
- для каждого события просто выводит в отладочный лог сообщение,
- при получении события WIFI_EVENT_STA_START дополнительно запускает подключение путем вызова esp_wifi_connect().
Регистрация созданного обработчика (пример):
Приведенный выше пример можно использовать, чтобы на практике разобраться, какие сообщения в какой момент приходят. Но даже его (в слегка упрощенном виде – убрав лишние сообщения) вполне можно использовать в оптимистичном сценарии подключения (их ещё называют “сценарий солнечного дня”): когда все идет по плану, точка доступа существует, пароль подходит и ESP удается подключиться с первого раза.
5.2. Пример обработчика с автоматическим переподключением
У примера обработчика выше есть большая проблема: ESP пытается подключиться к WiFi-сети ровно один раз. Кроме того, даже если сразу пошло все отлично и удалось подключиться к точке доступа c первой попытки – это отнюдь не гарантирует что подключение будет работать непрерывно и не пропадет в любой момент по внешней причине.
Для решения этой проблемы достаточно добавить обработку события WIFI_EVENT_STA_DISCONNECTED, например так:
В данном примере при разрыве соединения или неудачной попытке подключения будет получено событие WIFI_EVENT_STA_DISCONNECTED, что автоматически приведет к ещё одной попытке подключения. Но это тоже не очень идеально.
Во-первых – бесконечный цикл подключения к одной и той же точке доступа не всегда есть хорошечно. Например если внезапно роутер вышел из строя насовсем и навсегда. Поэтому в реальных сценариях стоит ограничить количество попыток переподключения к точке доступа; а также предпринять какие-то специальные меры в случае неудачи: например попробовать подключиться к резервной сети. Иногда приложению требуется более сложная стратегия переподключения: например, если событие отключения возникает из-за вызова соответствующей функции esp_wifi_disconnect(), тогда вашему приложению может быть не нужно запускать повторное подключение.
Вторая проблема – оповещение прикладных задач. Какие-то другие прикладные задачи могут и должны использовать WiFi-подключение. Например это может быть задача, которая отправляет данные куда-то на сервер. Тогда этой самой задаче может потребоваться в любой момент времени проверить – есть соединение соединение с сетью “вот прямо сейчас” или оно отсутствует. Использовать обработчики событий для оповещения прикладных задач не удобно.
5.3. Пример обработчика с ограничением числа попыток переподключения и группой событий
Решение первой проблемы из предыдущего варианта банальное – добавить счетчик попыток подключения. Если количество неудачных попыток превысило лимит – выходим из бесконечного цикла переподключений и пробуем что-то ещё.
Решение второй проблемы также довольно простое: для хранения текущего состояния подключения удобно использовать группу событий. Таким образом, при получении того или иного события WIFI_EVENT в обработчике можно устанавливать тот или иной заранее определенный бит (флаг) в данной группе. Тогда любая задача сможет в любой момент времени и полностью потокобезопасно проверить, есть ли подключение к точке доступа или его нет. А кроме того, это дает возможность прикладным сетевым задачам “уходить в спячку” на время отсутствия подключения и автоматический запуск при подключении к сети.
Пример одного из вариантов такой реализации:
Теперь вы в любой момент можете проверить состояние WiFi-подключения из любой задачи, просто проверив состояние флага WIFI_CONNECTED_BIT.
Типичный сценарий подключения к WiFi сети
Теперь давайте соберем все эти знания в кучу, перемешаем, три раза встряхнем, настучим в бубен админу и создадим достаточно простой, но при этом вполне рабочий код, который уже можно использовать для подключения ESP32 к WiFi точке доступа.
Выглядит это довольно громоздко, но если разобраться – то совсем не сложно (как, впрочем, почти всегда в ESP-IDF). Взамен вам дается возможность подстраивать алгоритмы подключения под ваши конкретные нужды, не ограничиваясь стандартной логикой Arduino ESP32. Конечно же, никто же не заставляет таскать этот самый код из проекта в проект – вполне можно (и даже нужно!) оформить его в виде библиотеки-компонента и подключать к любым проектам по мере необходимости.
Обратите внимание: в приведенном примере мы обошлись без App Task (который присутствует на схеме Espressif), да и Main Task, в общем-то, после старта WiFi и не нужен – даже если убрать бесконечный цикл, WiFi продолжит работать, работать и работать…
Проверим результаты (часть лога я вырезал):
...
Взболтать, но не пере-mesh-ивать
Ещё одна задачка, иногда встречающаяся на практике. Допустим, вы имеете несколько роутеров, расположенных в разных частях дома, при этом объединенных в одну локальную сеть, с одинаковыми SSID и паролями, с поддержкой WNM BTM. Такие сеть часто называются mesh-сетями (здесь имеется в виду не технология ESP-WIFI-MESH (ви не понимаете, это другое)).
При подключении к такой WiFi-сети станция ESP должна сама выбрать, к какой точке доступа подключиться, например ориентируясь по уровню сигнала RSSI.
Для этой задачи подойдёт самый стандартный STA‑режим, но только с «полным» сканированием и сортировкой точек доступа по уровню сигнала RSSI. Тогда драйвер при подключении сам выберет точку доступа с лучшим уровнем сигнала среди всех найденных с нужным SSID.
Вот, собственно, и всё – никаких хитростей.
Ну а на этом на Дзене все, полную версию статьи вы можете дочитать здесь: https://kotyara12.ru/iot/esp32-wifi2/