Перейти к содержимому

Подключение к сети WiFi в режиме STA на ESP-IDF

Доброго здравия, уважаемые читатели!

Популярность SoC ESP32 (и последующих серий) в первую очередь обусловлена тем, что помимо достаточно мощного процессора, в нем имеется встроенный радиоканал для подключения к сетям WiFi и Bluetooth®. Я уже писал, как подключить ESP32 в режиме STA с использованием фреймворка Arduino, причем несколькими способами – в цикле и с помощью событий. Писал я как-то аналогичную статью и применительно для фреймворка ESP-IDF, но та статья получилась на мой взгляд откровенно ужасной, скомканной и непонятной. Исправлять её смысла не вижу, проще написать новую, а старую отправить “в архив”.

Иллюстрация STA-режима из документации Espressif

В рамках данной статьи я буду рассматривать в основном STA режим, то есть мы будем подключать наш микроконтроллер к существующей Wi-Fi сети (точке доступа).

 

Примечание для любителей Arduino: В данной статье рассматривается подключение к уже существующей сети WiFi в режиме станции (STA) с использованием фреймворка ESP-IDF. Но все описанное ниже применимо и для фреймворка Arduino ESP32 (за небольшим исключением – вы не сможете изменить параметры сборки через menuconfig). Кроме того, следует понимать, что класс WiFiClient для Arduino ESP32 использует ровно те же самые механизмы, которые описаны в данной статье. Поэтому данная статья может быть полезна и заядлым ардуинщикам, если вы пожелаете разобраться “а как оно работает”.

 


Введение

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-FiESP32-C2 Wi-FiESP32-C6 Wi-FiESP32-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 overviewESP32-C5 Wi-Fi overview]
  • NAN (Wi‑Fi Aware, Neighbor Awareness Networking). Технология обнаружения и взаимодействия устройств поблизости без классического подключения к AP. Позволяет устройствам обнаруживать друг друга и обмениваться данными без обязательного подключения к одной точке доступа.

 

2. Поддерживаемые стандарты и особенности протокола

2.1. Стандарты 802.11

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-FiESP32-C3 Wi-FiESP32 Wi-Fi MACESP-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 TaskStation 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 demoESP 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 declarations */
typedef enum {
    WIFI_EVENT_WIFI_READY = 0,           /**< WiFi ready */
    WIFI_EVENT_SCAN_DONE,                /**< Finished scanning AP */
    WIFI_EVENT_STA_START,                /**< Station start */
    WIFI_EVENT_STA_STOP,                 /**< Station stop */
    WIFI_EVENT_STA_CONNECTED,            /**< Station connected to AP */
    WIFI_EVENT_STA_DISCONNECTED,         /**< Station disconnected from AP */
    WIFI_EVENT_STA_AUTHMODE_CHANGE,      /**< the auth mode of AP connected by device's station changed */

    WIFI_EVENT_STA_WPS_ER_SUCCESS,       /**< Station wps succeeds in enrollee mode */
    WIFI_EVENT_STA_WPS_ER_FAILED,        /**< Station wps fails in enrollee mode */
    WIFI_EVENT_STA_WPS_ER_TIMEOUT,       /**< Station wps timeout in enrollee mode */
    WIFI_EVENT_STA_WPS_ER_PIN,           /**< Station wps pin code in enrollee mode */
    WIFI_EVENT_STA_WPS_ER_PBC_OVERLAP,   /**< Station wps overlap in enrollee mode */

    WIFI_EVENT_AP_START,                 /**< Soft-AP start */
    WIFI_EVENT_AP_STOP,                  /**< Soft-AP stop */
    WIFI_EVENT_AP_STACONNECTED,          /**< a station connected to Soft-AP */
    WIFI_EVENT_AP_STADISCONNECTED,       /**< a station disconnected from Soft-AP */
    WIFI_EVENT_AP_PROBEREQRECVED,        /**< Receive probe request packet in soft-AP interface */

    WIFI_EVENT_FTM_REPORT,               /**< Receive report of FTM procedure */

    /* Add next events after this only */
    WIFI_EVENT_STA_BSS_RSSI_LOW,         /**< AP's RSSI crossed configured threshold */
    WIFI_EVENT_ACTION_TX_STATUS,         /**< Status indication of Action Tx operation */
    WIFI_EVENT_ROC_DONE,                 /**< Remain-on-Channel operation complete */

    WIFI_EVENT_STA_BEACON_TIMEOUT,       /**< Station beacon timeout */

    WIFI_EVENT_CONNECTIONLESS_MODULE_WAKE_INTERVAL_START,   /**< Connectionless module wake interval start */

    WIFI_EVENT_AP_WPS_RG_SUCCESS,        /**< Soft-AP wps succeeds in registrar mode */
    WIFI_EVENT_AP_WPS_RG_FAILED,         /**< Soft-AP wps fails in registrar mode */
    WIFI_EVENT_AP_WPS_RG_TIMEOUT,        /**< Soft-AP wps timeout in registrar mode */
    WIFI_EVENT_AP_WPS_RG_PIN,            /**< Soft-AP wps pin code in registrar mode */
    WIFI_EVENT_AP_WPS_RG_PBC_OVERLAP,    /**< Soft-AP wps overlap in registrar mode */

    WIFI_EVENT_ITWT_SETUP,               /**< iTWT setup */
    WIFI_EVENT_ITWT_TEARDOWN,            /**< iTWT teardown */
    WIFI_EVENT_ITWT_PROBE,               /**< iTWT probe */
    WIFI_EVENT_ITWT_SUSPEND,             /**< iTWT suspend */
    WIFI_EVENT_TWT_WAKEUP,               /**< TWT wakeup */
    WIFI_EVENT_BTWT_SETUP,               /**< bTWT setup */
    WIFI_EVENT_BTWT_TEARDOWN,            /**< bTWT teardown*/

    WIFI_EVENT_NAN_STARTED,              /**< NAN Discovery has started */
    WIFI_EVENT_NAN_STOPPED,              /**< NAN Discovery has stopped */
    WIFI_EVENT_NAN_SVC_MATCH,            /**< NAN Service Discovery match found */
    WIFI_EVENT_NAN_REPLIED,              /**< Replied to a NAN peer with Service Discovery match */
    WIFI_EVENT_NAN_RECEIVE,              /**< Received a Follow-up message */
    WIFI_EVENT_NDP_INDICATION,           /**< Received NDP Request from a NAN Peer */
    WIFI_EVENT_NDP_CONFIRM,              /**< NDP Confirm Indication */
    WIFI_EVENT_NDP_TERMINATED,           /**< NAN Datapath terminated indication */
    WIFI_EVENT_HOME_CHANNEL_CHANGE,      /**< WiFi home channel change,doesn't occur when scanning */

    WIFI_EVENT_STA_NEIGHBOR_REP,         /**< Received Neighbor Report response */

    WIFI_EVENT_MAX,                      /**< Invalid WiFi event ID */
} wifi_event_t;

WIFI_EVENT_WIFI_READY

В текущей версии ESP-IDF драйвер Wi-Fi ни при каких условиях не генерирует это событие, которое к тому же может быть проигнорировано вашей функцией обратного вызова обработки событий приложения. Это событие может быть удалено в будущих версиях.

WIFI_EVENT_SCAN_DONE

Событие “сканирование завершено” генерируется функцией esp_wifi_scan_start() и возникает в следующих случаях:

  • Сканирование полностью завершено, когда например, искомая точка доступа успешно обнаружена или были просканированы все каналы.
  • Сканирование досрочно останавливается при вызове esp_wifi_scan_stop().
  • Функция esp_wifi_scan_start() вновь вызывается до завершения текущего сканирования. Новое сканирование остановит текущее, и соответственно будет сгенерировано событие завершения сканирования.

Событие “Сканирование завершено” не возникает в следующих сценариях:

  • Это заблокированное сканирование.
  • Сканирование вызвано функцией esp_wifi_connect() в процессе подготовки к подключению.

После получения этого события системная задача обработки сетевых событий (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. На этом этапе необходимо выполнить следующие действия:

  1. Вызвать esp_netif_init() для инициализации стека LwIP и создания задачи управления сетевыми интерфейсами (если это не было сделано ранее).
  2. Создать системный цикл событий (Event Loop) с помощью esp_event_loop_create_default() (если это не было сделано ранее).
  3. Создать интерфейс станции через esp_netif_create_default_wifi_sta(). Это связывает драйвер Wi-Fi со стеком TCP/IP.
  4. Вызвать esp_wifi_init(), чтобы выделить ресурсы для драйвера Wi-Fi и запустить задачу Wi-Fi Driver Task [Wi-Fi Operation Steps].
  5. Зарегистрировать обработчики событий WIFI_EVENT_STA_*, которые вы должны предварительно создать (пока этот этап опустим и обсудим подробнее ниже)

Простейший пример фазы инициализации: 

// Инициализация NVS для Wi‑Fi (хранение калибровочных данных и, при необходимости, конфигурации)
nvs_flash_init();
// Инициализация LwIP (TCP/IP стек)
esp_netif_init();
// Создание системного event loop (если это не было сделано ранее)
esp_event_loop_create_default();
// Создание стандартного STA-интерфейса
esp_netif_create_default_wifi_sta();
// Инициализация Wi‑Fi драйвера
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
esp_wifi_init(&cfg);

Но пример выше не обрабатывает ошибки, которые могут возникнуть в процессе.

#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "esp_wifi.h"
#include "esp_log.h"

static const char *TAG = "wifi_init";

esp_err_t wifi_init_phase(void)
{
    esp_err_t ret;

    // --- Инициализация NVS ---
    ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_LOGW(TAG, "NVS partition problem, erasing...");
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to init NVS: %s", esp_err_to_name(ret));
        return ret;
    }

    // --- Инициализация LwIP / esp-netif ---
    ret = esp_netif_init();
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to init esp_netif: %s", esp_err_to_name(ret));
        return ret;
    }

    // --- Создание event loop ---
    ret = esp_event_loop_create_default();
    if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) {
        // ESP_ERR_INVALID_STATE, если loop уже создан — не считаем фатальной
        ESP_LOGE(TAG, "Failed to create default event loop: %s", esp_err_to_name(ret));
        return ret;
    }

    // --- Default STA netif ---
    esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
    if (sta_netif == NULL) {
        ESP_LOGE(TAG, "Failed to create default WiFi STA interface");
        return ESP_FAIL;
    }

    // --- Создаем Wi‑Fi driver ---
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ret = esp_wifi_init(&cfg);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to init WiFi driver: %s", esp_err_to_name(ret));
        return ret;
    }

    return ESP_OK;
}

Этот код уже соответствует букве закона почти всем требованиям. Выглядит это, конечно, громоздко, но зато это более надежно.

Примечания:

  1. Код проверки на наличие ошибок вполне можно “обернуть” в макрос ESP_ERROR_CHECK() или его аналоги (но не во всех случаях) – это будет выглядеть более компактно.
  2. Разумеется, nvs_flash_init() и esp_event_loop_create_default() нужно выполнять, только если вы не сделали этого ранее где-то в другой части прошивки.

 

4.2. Фаза конфигурации (Wi-Fi Config Phase)

После инициализации необходимо настроить параметры конкретного подключения, чтобы драйвер знал, к какой сети подключаться и какие методы защиты использовать. На этом этапе необходимо выполнить следующие действия:

  1. Вызвать esp_wifi_set_mode(WIFI_MODE_STA) и затем esp_wifi_set_config(), передав структуру с SSID и паролем для целевой точки доступа.
  2. Здесь же можно настроить другие параметры подключения, например параметры сканирования, шифрование, ширину канала и т.д. и т.п.

Параметры конфигурации сети wifi_config_t выглядят следующим образом:

union wifi_config_t {
    wifi_ap_config_t        ap;   // Configuration of AP
    wifi_sta_config_t       sta;  // Configuration of STA
    wifi_nan_sync_config_t  nan;  // Configuration of NAN
};

Для STA режима нас интересует структура wifi_sta_config_t (ниже я опустил добрую половину “не очень интересных” полей для сокращения записи):

struct wifi_sta_config_t {
    uint8_t  ssid[32];          // SSID целевой точки доступа
    uint8_t  password[64];      // Пароль точки доступа
    wifi_scan_method_t scan_method; // Метод сканирования сети WIFI_FAST_SCAN или WIFI_ALL_CHANNEL_SCAN
    bool     bssid_set;         // 0 – игнорировать bssid, 1 – использовать bssid
    uint8_t  bssid[6];          // MAC-адрес целевой AP (если bssid_set = 1)
    uint8_t  channel;           // Подсказка канала (0 – без предпочтения)
    uint16_t listen_interval;   // Интервал прослушивания (в интервалах beacon)
    wifi_sort_method_t sort_method;   // Сортировка AP по сигналу или по безопасности
    wifi_scan_threshold_t threshold;  // Порог по RSSI и authmode для отбора AP
    wifi_pmf_config_t    pmf_cfg;     // Protected Management Frames
    uint32_t rm_enabled;      // Включены ли Radio Measurements
    uint32_t btm_enabled;     // Включено ли управление переходом между BSS (BSS Transition Management, BTM) для данного соединения. Рекомендуется не задавать BSSID при включенном BTM.
    uint32_t mbo_enabled;     // Включен ли MBO для данного соединения. Рекомендуется не задавать BSSID при включенном MBO. Включение MBO автоматически включает BTM и RM, указанные выше.
    // ++ Дополнительные поля (например, WPA3/SAE)
};

Некоторые параметры 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[]).  

#include "esp_wifi.h"
#include "esp_log.h"

#define WIFI_SSID "network-ssid"
#define WIFI_PASS "network-pass"

static const char *TAG = "wifi_init";

void wifi_config_phase(void)
{
    wifi_config_t wifi_config = {
        .sta = {
            .ssid = WIFI_SSID,
            .password = WIFI_PASS,
        },
    };

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));

    ESP_LOGI(TAG, "Wi-Fi config applied, SSID: %s", WIFI_SSID);
}

По большому счету, для подключения этого уже будет вполне достаточно.

 

4.2.1. Настройка параметров WiFi-подключения с помощью системы KConfig

Чаще всего “жестко” зашивать SSID и пароль прямо в коде программы (те самые #define WIFI_SSID "network-ssid" и #define WIFI_PASS "network-pass")  бывает не разумно и не удобно. Можно (и так довольно часто делается) перенести эти макросы в систему KConfig, дабы иметь возможность быстро изменить с помощью menuconfig.

В этом случает вам потребуется создать дополнительный текстовый файл KConfig, который может выглядеть таким образом:

menu "WiFi STA Configuration"
    config WIFI_STA_SSID
        string "WiFi SSID"
        default "myssid"
        help
            Network name (SSID) to connect to AP.

    config WIFI_STA_PASSWORD
        string "WiFi password"
        default "mypassword"
        help
            Password to connect to AP.
endmenu

В этом случае в вашей программе макросы будут доступны как CONFIG_WIFI_STA_SSID и CONFIG_WIFI_STA_PASSWORD:

#include "esp_wifi.h"
#include "esp_log.h"

#define WIFI_SSID "network-ssid"
#define WIFI_PASS "network-pass"

static const char *TAG = "wifi_init";

void wifi_config_phase(void)
{
    wifi_config_t wifi_config = {
        .sta = {
            .ssid = CONFIG_WIFI_STA_SSID,
            .password = CONFIG_WIFI_STA_PASS,
        },
    };

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));

    ESP_LOGI(TAG, "Wi-Fi config applied, SSID: %s", WIFI_SSID);
}

Разумеется, это только пример – вы можете добавить свои параметры и использовать их в своих проектах.

На ESP можно реализовать возможность настройки SSID и пароля для подключения к точке доступа в runtime, причем несколькими различными способами, например с помощью SoftAP или Promiscuous mode. Но сейчас мы не будем это рассматривать, так как это не входит в рамки данной статьи.

 

4.2.2. Использование NVS для хранения параметров WiFi

Драйвер WiFi может хранить некоторые служебные параметры в NVS-разделе, если он существует и включен соответствующий режим WIFI_STORAGE_FLASH. Переключить это поведение можно с помощью функции API esp_wifi_set_storage()  с параметрами WIFI_STORAGE_RAM или WIFI_STORAGE_FLASH. Обратите внимание: по умолчанию включен режим WIFI_STORAGE_FLASH.

Конфигурация драйвера считывается при вызове esp_wifi_init(), поэтому NVS-раздел должен быть проинициализирован ранее. Запись параметров происходит при вызове любого конфигурационного API, например esp_wifi_set_mode() и esp_wifi_set_config().

Для режима STA в NVS-разделе nvs.net80211 сохраняются следующие ключи, которые соответствуют полям wifi_sta_config_t и связанным с ними настройкам станции [Station keysProvisioning log]:

  • opmode – режим работы Wi‑Fi (wifi_mode_t, u8)
  • country – страна/регион (wifi_country_t, blob)
  • band_mode – диапазон (2.4/5 ГГц и т.п., u8)
  • sta.ssid – SSID точки доступа (тип blob_sz_fill)
  • sta.pswd – пароль (тип blob_fill)
  • bssid.set – флаг, задан ли BSSID (u8)
  • sta.bssid – BSSID (MAC точки доступа) (blob)
  • sta.lis_intval – listen interval (u16)
  • sta.phym – PHY‑режим (u8)
  • sta.phybw – ширина канала (u8)
  • sta.sort_method – метод сортировки AP (u8)
  • sta.minrssi – минимальный RSSI (i8)
  • sta.minauth – минимальный уровень аутентификации (u8)
  • sta.pmf_r – требование PMF (u8)
  • sta.btm_e – включён ли BTM (u8)
  • sta.rrm_e – включён ли RRM (u8)
  • sta.mbo_e – включён ли MBO (u8)
  • sta.phym5g – PHY‑режим 5 ГГц (u8, если поддерживается)
  • sta.phybw5g – ширина канала 5 ГГц (u8, если поддерживается)
  • sta.ft – включён ли Fast Transition (u8)
  • sta.owe – включён ли OWE (u8)

Поэтому вызывать esp_wifi_set_mode(WIFI_MODE_STA) и esp_wifi_set_config() при каждом запуске ESP нет необходимости, конечно же при условии что эти параметры уже были ранее установлены и сохранены в NVS. Обратите внимание, что даже если параметры были успешно считаны, то при любом вызове esp_wifi_set_config() все ранее сохраненные данные будут перезаписаны новыми значениями. Это означает, если вам необходимо сохранять параметры WiFi в NVS, то вы сами решаете, нужно ли вызывать esp_wifi_set_config() или нет, в зависимости от того, удалось ли прочитать настройки из NVS.

Пример кода в этом случае может быть таким [WiFi NVS Config]:

#include "nvs_flash.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"

static const char *TAG = "wifi_nvs";

static void wifi_init_with_nvs(void)
{
    // Инициализация 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());
        ESP_ERROR_CHECK(nvs_flash_init());
    }

    // Базовая инициализация Wi‑Fi
    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();
    // Здесь драйвер читает конфигурацию из NVS 
    // https://github.com/espressif/esp-idf/blob/master/examples/wifi/wifi_nvs_config/README.md
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    // Проверяем, была ли в NVS ранее сохранённая конфигурация
    wifi_config_t current_cfg;
    esp_err_t err = esp_wifi_get_config(WIFI_IF_STA, &current_cfg);

    bool have_saved_cfg = (err == ESP_OK) && (current_cfg.sta.ssid[0] != 0);

    if (have_saved_cfg) {
        ESP_LOGI(TAG, "WiFi config found in NVS, SSID: %s", current_cfg.sta.ssid);
        // НИЧЕГО не вызываем: ни esp_wifi_set_config(), ни запись в NVS
    } else {
        ESP_LOGW(TAG, "No WiFi config in NVS, setting default one");
        // Настраиваем WiFi заново
        wifi_config_t wifi_config = {
            .sta = {
                .ssid = "my-ssid",
                .password = "my-password",
            },
        };
        // Это один раз создаст профиль и сохранит его в NVS
        ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
        ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
    }

    // Запускаем Wi‑Fi (в обоих случаях)
    ESP_ERROR_CHECK(esp_wifi_start());
}

void app_main(void)
{
    wifi_init_with_nvs();
}

Если же вы хотите, чтобы при выключении устройства эти сведения вообще не сохранялись, вы можете вызвать esp_wifi_set_storage(WIFI_STORAGE_RAM) для сохранения этой информации только в оперативной памяти. Само собой, при перезагрузке эти данные будут полностью утеряны.

 

4.2.3. Дополнительные параметры STA-соединения

В этой же фазе можно настроить дополнительные параметры. Допустим, мы знаем что ESP будет работать на большом удалении от точке доступа при низком уровне сигнала. Тогда сужение полосы пропускания несколько улучшит надежность передачи пакетов, хотя и снизит скорость передачи.

#include "esp_wifi.h"
#include "esp_log.h"

#define WIFI_SSID "my-ssid"
#define WIFI_PASS "my-password"

static const char *TAG = "wifi_init";

void wifi_config_phase(void)
{
    wifi_config_t wifi_config = {
        .sta = {
            .ssid = WIFI_SSID,
            .password = WIFI_PASS,
        },
    };

    // Режим Station
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));

    // Применяем конфигурацию к STA интерфейсу
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));

    // Зафиксировать полосу 20 МГц (HT20)
    // 40 МГц (WIFI_BW40) поддерживается только в 11n, поэтому для более устойчивой работы в «зашумлённой» среде оставляем HT20.
    ESP_ERROR_CHECK(esp_wifi_set_bandwidth(WIFI_IF_STA, WIFI_BW20));

    ESP_LOGI(TAG, "Wi-Fi config applied (HT20), SSID: %s", WIFI_SSID);
}

 

4.3. Фаза запуска (Wi-Fi Start Phase)

Фактическая активация драйвера Wi-Fi.

Важно! К этому моменту должны быть созданы и зарегистрированы обработчики событий WIFI_EVENT и IP_EVENT! Обработчики событий мы обсудим немного позже, я пока опустил это, чтобы “не мешать всё в кучу”. Но просто учтите это в своих разработках.

  1. Вызвать esp_wifi_start(), после чего драйвер опубликует системное событие WIFI_EVENT_STA_START.
  2. Драйвер Wi-Fi отправляет событие WIFI_EVENT_STA_START сначала системной задаче обработки событий; после чего эта задача выполнит ряд стандартных действий и вызовет функцию обратного вызова для событий приложения.
  3. Рекомендуется создать обработчик этого события (WIFI_EVENT_STA_START) и вызывать esp_wifi_connect() внутри этого обработчика, чтобы запустить процесс подключения к точке доступа [ESP32-S3 Wi-Fi Station General Scenario].

Простейший код может выглядеть как-то так:

// Запускаем Wi‑Fi драйвер
ESP_ERROR_CHECK(esp_wifi_start());
ESP_LOGI(TAG, "esp_wifi_start() called, waiting for connection result...");

 

4.4. Фаза подключения (Wi-Fi Connect Phase)

Внутренний процесс установки связи, который включает сканирование, аутентификацию и ассоциацию.

Запускается с помощью esp_wifi_connect(), который обычно находится внутри обработчика события WIFI_EVENT_STA_START. Минимальный код может уместиться в одну строчку:

// Запускаем внутренний процесс сканирования/подключения
ESP_ERROR_CHECK(esp_wifi_connect());

На этом этапе выполняются следующие процессы:

  • 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_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.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 PhaseWi-Fi Reconnect (ESP32)].

Например, если вам необходимо поддерживать постоянное WiFi-подключение, то можно сразу же попытаться его восстановить:

if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
    esp_wifi_connect();
    ESP_LOGW(TAG, "Trying to connect to WiFi");
}

Ещё один момент, который следует учитывать, — это то, что при повторном подключении может быть подключен не к одной и той же точке доступа, если с одним и тем же SSID подключено несколько точек доступа. При повторном подключении всегда выбираются наиболее подходящие на данный момент точки доступа.

 

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-FiESP32 Wi-Fi Deinit Phase]:

  1. Разрыв соединения: вызовите функцию esp_wifi_disconnect(), чтобы корректно завершить текущее Wi-Fi соединение.
  2. Остановка драйвера: вызовите esp_wifi_stop(). После этого генерируется событие WIFI_EVENT_STA_STOP, система освобождает IP-адрес, останавливает DHCP-клиент и удаляет связанные TCP/UDP соединения.
  3. Выгрузка драйвера: Вызовите 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].

Пример кода:

esp_err_t wifi_deinit_phase(void)
{
    esp_err_t ret;

    // 8.1: Отключиться от AP
    ret = esp_wifi_disconnect();
    if (ret != ESP_OK && ret != ESP_ERR_WIFI_NOT_CONNECT) {
        ESP_LOGW(TAG, "esp_wifi_disconnect() returned %s", esp_err_to_name(ret));
    }

    // 8.2: Остановить драйвер
    ret = esp_wifi_stop();
    if (ret == ESP_ERR_WIFI_NOT_INIT) {
        ESP_LOGE(TAG, "Wi-Fi stack not initialized");
        return ret;
    }

    // 8.3: Выгрузить драйвер
    ESP_ERROR_CHECK(esp_wifi_deinit());

    // Очистить привязку драйвера и обработчики по умолчанию
    ESP_ERROR_CHECK(esp_wifi_clear_default_wifi_driver_and_handlers(tutorial_netif));

    // Выгрузить LwIP
    esp_netif_destroy(tutorial_netif);

    return ESP_OK;
}

Важно! В приведенном примере не учтено отключение пользовательских обработчиков событий, которые мы ещё и не создавали. Чем мы, собственно, сейчас и займемся.

 


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().
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_netif_ip_addr.h"

static const char *TAG = "wifi_events";

static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
{
    if (event_base == WIFI_EVENT) {
        switch (event_id) {
        case WIFI_EVENT_SCAN_DONE:
            ESP_LOGI(TAG, "Event received: WIFI_EVENT_SCAN_DONE");
            break;
        case WIFI_EVENT_STA_START:
            ESP_LOGI(TAG, "Event received: WIFI_EVENT_STA_START");
            esp_wifi_connect();
            break;
        case WIFI_EVENT_STA_STOP:
            ESP_LOGI(TAG, "Event received: WIFI_EVENT_STA_STOP");
            break;
        case WIFI_EVENT_STA_CONNECTED:
            ESP_LOGI(TAG, "Event received: WIFI_EVENT_STA_CONNECTED");
            break;
        case WIFI_EVENT_STA_DISCONNECTED:
            ESP_LOGI(TAG, "Event received: WIFI_EVENT_STA_DISCONNECTED");
            break;
        case WIFI_EVENT_STA_AUTHMODE_CHANGE:
            ESP_LOGI(TAG, "Event received: WIFI_EVENT_STA_AUTHMODE_CHANGE");
            break;
        default:
            ESP_LOGI(TAG, "Unhandled WIFI_EVENT id=%" PRId32, event_id);
            break;
        }
    } else if (event_base == IP_EVENT) {
        switch (event_id) {
        case IP_EVENT_STA_GOT_IP: {
            ESP_LOGI(TAG, "Event received: 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));
            break;
        }
        case IP_EVENT_STA_LOST_IP:
            ESP_LOGI(TAG, "Event received: IP_EVENT_STA_LOST_IP");
            break;
        case IP_EVENT_GOT_IP6:
            ESP_LOGI(TAG, "Event received: IP_EVENT_GOT_IP6");
            break;
        default:
            ESP_LOGI(TAG, "Unhandled IP_EVENT id=%" PRId32, event_id);
            break;
        }
    }
}

Регистрация созданного обработчика (пример):

ESP_ERROR_CHECK(esp_event_handler_instance_register(
    WIFI_EVENT, 
    ESP_EVENT_ANY_ID, 
    &event_handler, NULL, NULL));
ESP_ERROR_CHECK(esp_event_handler_instance_register(
    IP_EVENT, 
    ESP_EVENT_ANY_ID, 
    &event_handler, NULL, NULL));

Приведенный выше пример можно использовать, чтобы на практике разобраться, какие сообщения в какой момент приходят. Но даже его (в слегка упрощенном виде – убрав лишние сообщения) вполне можно использовать в оптимистичном сценарии подключения (их ещё называют “сценарий солнечного дня”): когда все идет по плану, точка доступа существует, пароль подходит и ESP удается подключиться с первого раза. 

 

5.2. Пример обработчика с автоматическим переподключением

У примера обработчика выше есть большая проблема: ESP пытается подключиться к WiFi-сети ровно один раз. Кроме того, даже если сразу пошло все отлично и удалось подключиться к точке доступа c первой попытки – это отнюдь не гарантирует что подключение будет работать непрерывно и не пропадет в любой момент по внешней причине.

Для решения этой проблемы достаточно добавить обработку события WIFI_EVENT_STA_DISCONNECTED, например так:

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) {
        // STA запущен — пробуем подключиться в первый раз
        ESP_LOGW(TAG, "Connect to WiFi...");
        esp_wifi_connect();
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        // Попытка не удалась или связь потеряна - попытка подключиться вновь и вновь
        ESP_LOGW(TAG, "Retry to connect to WiFi...");
        esp_wifi_connect();
    } 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));
    }
}

В данном примере при разрыве соединения или неудачной попытке подключения будет получено событие WIFI_EVENT_STA_DISCONNECTED, что автоматически приведет к ещё одной попытке подключения. Но это тоже не очень идеально.

Во-первых – бесконечный цикл подключения к одной и той же точке доступа не всегда есть хорошечно. Например если внезапно роутер вышел из строя насовсем и навсегда. Поэтому в реальных сценариях стоит ограничить количество попыток переподключения к точке доступа; а также предпринять какие-то специальные меры в случае неудачи: например попробовать подключиться к резервной сети. Иногда приложению требуется более сложная стратегия переподключения: например, если событие отключения возникает из-за вызова соответствующей функции esp_wifi_disconnect(), тогда вашему приложению может быть не нужно запускать повторное подключение.

Вторая проблема – оповещение прикладных задач. Какие-то другие прикладные задачи могут и должны использовать WiFi-подключение. Например это может быть задача, которая отправляет данные куда-то на сервер. Тогда этой самой задаче может потребоваться в любой момент времени проверить – есть соединение соединение с сетью “вот прямо сейчас” или оно отсутствует. Использовать обработчики событий для оповещения прикладных задач не удобно.

 

5.3. Пример обработчика с ограничением числа попыток переподключения и группой событий

Решение первой проблемы из предыдущего варианта банальное – добавить счетчик попыток подключения. Если количество неудачных попыток превысило лимит – выходим из бесконечного цикла переподключений и пробуем что-то ещё.

Решение второй проблемы также довольно простое: для хранения текущего состояния подключения удобно использовать группу событий. Таким образом, при получении того или иного события WIFI_EVENT в обработчике можно устанавливать тот или иной заранее определенный бит (флаг) в данной группе. Тогда любая задача сможет в любой момент времени и полностью потокобезопасно проверить, есть ли подключение к точке доступа или его нет. А кроме того, это дает возможность прикладным сетевым задачам “уходить в спячку” на время отсутствия подключения и автоматический запуск при подключении к сети.

Пример одного из вариантов такой реализации:

// Флаг "подключено"
#define WIFI_CONNECTED_BIT BIT0
// Флаг "не удалось подключиться"
#define WIFI_FAIL_BIT      BIT1

static const char *TAG = "wifi_example";

// Группа событий
static EventGroupHandle_t s_wifi_event_group;
// Счетчик попыток подключения
static int s_retry_num = 0;

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) {
        // Сбрасываем флаги подключения
        xEventGroupClearBits(s_wifi_event_group, (WIFI_FAIL_BIT | WIFI_CONNECTED_BIT));
        // STA запущен — пробуем подключиться
        esp_wifi_connect();
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        // Сбрасываем флаг подключения, если он был установлен
        xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
        // Автоматическое переподключение с ограничением по числу попыток
        if (s_retry_num < 30) {
            esp_wifi_connect();
            s_retry_num++;
            ESP_LOGW(TAG, "Retry to connect to WiFi (%d)", s_retry_num);
        } else {
            // Устанавливаем флаг "не удалось подключиться"
            xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
            ESP_LOGE(TAG, "Failed to connect to WiFi");
        }
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        // Устанавливаем флаг "подключено"
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
        // Сбрасываем счетчик попыток
        s_retry_num = 0;
        // Выводим 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));
    }
}

Теперь вы в любой момент можете проверить состояние WiFi-подключения из любой задачи, просто проверив состояние флага WIFI_CONNECTED_BIT.

 


Типичный сценарий подключения к WiFi сети

Теперь давайте соберем все эти знания в кучу, перемешаем, три раза встряхнем, настучим в бубен админу и создадим достаточно простой, но при этом вполне рабочий код, который уже можно использовать для подключения ESP32 к WiFi точке доступа.

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "freertos/task.h"

#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"

#define WIFI_SSID "network-ssid"
#define WIFI_PASS "network-pass"

#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT      BIT1

static const char *TAG = "wifi_example";

static EventGroupHandle_t s_wifi_event_group;
static int s_retry_num = 0;

// ===== Обработчик событий Wi-Fi/IP с переподключением =====
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) {
        // Сбрасываем флаги подключения
        xEventGroupClearBits(s_wifi_event_group, (WIFI_FAIL_BIT | WIFI_CONNECTED_BIT)); 
        // STA запущен — пробуем подключиться
        esp_wifi_connect();
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        // Сбрасываем флаг подключения, если он был установлен
        xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT); 
        // Автоматическое переподключение с ограничением по числу попыток
        if (s_retry_num < 30) {
            esp_wifi_connect();
            s_retry_num++;
            ESP_LOGW(TAG, "Retry to connect to WiFi (%d)", s_retry_num);
        } else {
            // Устанавливаем флаг "не удалось подключиться"
            xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
            ESP_LOGE(TAG, "Failed to connect to WiFi"); 
        }
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        // Устанавливаем флаг "подключено"
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); 
        // Сбрасываем счетчик попыток
        s_retry_num = 0;
        // Выводим 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));
    }
}

// ===== Инициализация и постоянная работа Wi-Fi (без деинициализации) =====
static void wifi_init_and_connect(void)
{
    // Инициализация NVS (нужен Wi-Fi драйверу)
    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);

    // Создаем Event group для сигнализации о статусе подключения
    s_wifi_event_group = xEventGroupCreate();

    // Запускаем TCP/IP стек и event loop
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    // Создаём стандартный STA-интерфейс
    esp_netif_create_default_wifi_sta();

    // Инициализация Wi-Fi драйвера
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    // Регистрация обработчиков Wi-Fi и IP событий
    ESP_ERROR_CHECK(esp_event_handler_instance_register(
        WIFI_EVENT,
        ESP_EVENT_ANY_ID,
        &event_handler,
        NULL,
        NULL));  // instance не нужен, деинициализации не делаем

    ESP_ERROR_CHECK(esp_event_handler_instance_register(
        IP_EVENT,
        IP_EVENT_STA_GOT_IP,
        &event_handler,
        NULL,
        NULL)); // instance не нужен, деинициализации не делаем

    // Конфигурация STA
    wifi_config_t wifi_config = {
        .sta = {
            .ssid = WIFI_SSID,
            .password = WIFI_PASS,
        },
    };

    // Настраиваем WiFi-драйвер
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));

    // Запуск Wi-Fi
    ESP_ERROR_CHECK(esp_wifi_start());
    ESP_LOGI(TAG, "WiFi started, waiting for connection...");

    // Ожидаем успешное подключение или неудачу
    EventBits_t bits = xEventGroupWaitBits(
        s_wifi_event_group,
        WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
        pdFALSE,
        pdFALSE,
        portMAX_DELAY);

    if (bits & WIFI_CONNECTED_BIT) {
        ESP_LOGI(TAG, "Connected to AP");
    } else if (bits & WIFI_FAIL_BIT) {
        ESP_LOGE(TAG, "Failed to connect to AP");
    } else {
        ESP_LOGE(TAG, "Unexpected event bits: 0x%" PRIx32, bits);
    }

    // Дальше ничего делать не нужно, Wi-Fi остаётся запущенным, 
    // обработчик будет автоматически переподключать при разрыве.
}

void app_main(void)
{
    wifi_init_and_connect();

    // Основная логика приложения; Wi-Fi работает в фоне
    while (1) {
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

Выглядит это довольно громоздко, но если разобраться – то совсем не сложно (как, впрочем, почти всегда в ESP-IDF). Взамен вам дается возможность подстраивать алгоритмы подключения под ваши конкретные нужды, не ограничиваясь стандартной логикой Arduino ESP32. Конечно же, никто же не заставляет таскать этот самый код из проекта в проект – вполне можно (и даже нужно!) оформить его в виде библиотеки-компонента и подключать к любым проектам по мере необходимости.

Обратите внимание: в приведенном примере мы обошлись без App Task (который присутствует на схеме Espressif), да и Main Task, в общем-то, после старта WiFi и не нужен – даже если убрать бесконечный цикл, WiFi продолжит работать, работать и работать…

Проверим результаты (часть лога я вырезал):

I (401) cpu_start: Multicore app
I (409) cpu_start: Pro cpu start user code
I (409) cpu_start: cpu freq: 160000000 Hz
I (409) app_init: Application information:
I (409) app_init: Project name:     wifi-sta-test01
...
I (495) main_task: Started on CPU0
...
I (505) main_task: Calling app_main()
I (535) wifi:wifi driver task: 3ffc0114, prio:23, stack:6656, core=0
I (545) wifi:wifi firmware version: 79fa3f41ba
I (545) wifi:wifi certification version: v7.0
I (545) wifi:config NVS flash: enabled
...
I (645) phy_init: phy_version 4860,6b7a6e5,Feb  6 2025,14:47:07
I (715) wifi:mode : sta (40:22:d8:60:b5:70)
I (725) wifi:enable tsf
I (725) wifi_example: WiFi started, waiting for connection...
I (735) wifi:new:<1,0>, old:<1,0>, ap:<255,255>, sta:<1,0>, prof:1, snd_ch_cfg:0x0
I (735) wifi:state: init -> auth (0xb0)
I (1235) wifi:state: auth -> init (0x600)
I (1255) wifi:new:<1,0>, old:<1,0>, ap:<255,255>, sta:<1,0>, prof:1, snd_ch_cfg:0x0
W (1255) wifi_example: Retry to connect to WiFi (1)
I (2475) wifi:new:<11,2>, old:<1,0>, ap:<255,255>, sta:<11,2>, prof:1, snd_ch_cfg:0x0
I (2475) wifi:state: init -> auth (0xb0)
I (3515) wifi:state: auth -> assoc (0x0)
I (3525) wifi:state: assoc -> run (0x10)
I (3635) wifi:connected with K12, aid = 10, channel 11, 40D, bssid = ff:1e:52:69:20:29
I (3645) wifi:security: WPA3-SAE, phy: bgn, rssi: -37
I (3665) wifi:pm start, type: 1

I (3665) wifi:dp: 1, bi: 102400, li: 3, scale listen interval from 307200 us to 307200 us
I (3665) wifi:<ba-add>idx:0 (ifx:0, ff:1e:52:69:20:29), tid:0, ssn:2, winSize:64
I (3675) wifi:AP's beacon interval = 102400 us, DTIM period = 1
I (5545) wifi:<ba-add>idx:1 (ifx:0, ff:1e:52:69:20:29), tid:5, ssn:0, winSize:64
I (5805) esp_netif_handlers: sta ip: 192.168.33.125, mask: 255.255.255.0, gw: 192.168.33.1
I (5805) wifi_example: Got IP: 192.168.33.125
I (5805) wifi_example: Connected to AP

Как видите, все прекрасно работает. И всё оказалось проще, чем казалось в начале.

 

Добавляем сторожевой таймер на случай, если что-то пошло не так

Например иногда процесс подключения может “зависнуть” очень надолго, буквально по перезагрузки роутера. Именно по этой причине может изредка зависать подключение в ArduinoESP32, поэтому же иногда зависали ESP и в моих первых прошивках – выяснено опытным путем. Это происходит не то чтобы часто, но иногда случается, и внешне было непонятно почему.

Оказалось, что иногда может случиться так, что подключение к точке доступа выполняется, и событие WIFI_EVENT_STA_CONNECTED мы успешно получили; а вот следующего, “финального” события IP_EVENT_STA_GOT_IP так и не дождались.

Произойти это может по разным причинам: из-за проблем на DHCP-сервере (который обычно функционирует на точке доступа, роутере) или очень слабого сигнала AP. И станция ESP “зависает” в ожидании этого события, так как штатный таймаут в этом случае не возможен. То есть мы получаем патовую ситуацию, коннект к AP есть, но IP-адрес не получен, поэтому TCP/IP соединения функционировать не могут. Все чего-то ждут, но непонятно чего и когда всё это кончится…

Решение данной проблемы довольно простое: используйте программный таймер – после получения события WIFI_EVENT_STA_CONNECTED  запустите “аварийный” таймер, а при получении IP_EVENT_STA_GOT_IP остановите его. Если вдруг “что-то” пойдет не так, callback таймера должен перезапустить попытку подключения с точке доступа. 

Один из вариантов такого решения:

#include <stdio.h>
#include <string.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "freertos/timers.h"

#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"

#define WIFI_SSID       "network-ssid"
#define WIFI_PASS       "network-pass"

#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT      BIT1

static const char *TAG = "wifi_example";

static EventGroupHandle_t s_wifi_event_group;
static int s_retry_num = 0;

static TimerHandle_t s_dhcp_timer = NULL;
#define DHCP_TIMEOUT_MS  10000   // 10 секунд ожидания IP

// Callback для таймера: срабатывает, если за отведённое время IP не получен
static void dhcp_timeout_cb(TimerHandle_t xTimer)
{
    ESP_LOGW(TAG, "DHCP timeout, restarting Wi-Fi connection");
    // Принудительно разрываем и заново инициируем подключение
    esp_wifi_disconnect();
    esp_wifi_connect();
}

// Обработчик событий Wi-Fi и IP
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_CONNECTED) {
        ESP_LOGI(TAG, "Wi-Fi connected, starting DHCP watchdog timer");
        // Запускаем таймер
        if (s_dhcp_timer) {
            xTimerStop(s_dhcp_timer, 0);
            xTimerChangePeriod(s_dhcp_timer, pdMS_TO_TICKS(DHCP_TIMEOUT_MS), 0);
            xTimerStart(s_dhcp_timer, 0);
        }

    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        ESP_LOGI(TAG, "Wi-Fi disconnected");
        // При любом дисконнекте останавливаем таймер
        if (s_dhcp_timer) {
            xTimerStop(s_dhcp_timer, 0);
        }

        if (s_retry_num < 30) {
            esp_wifi_connect();
            s_retry_num++;
            ESP_LOGW(TAG, "Retrying to connect to WiFi");
        } else {
            xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
        }

    } 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));

        // IP получен — аварийный таймер больше не нужен
        if (s_dhcp_timer) {
            xTimerStop(s_dhcp_timer, 0);
        }

        s_retry_num = 0;
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
    }
}

static 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 = WIFI_SSID,
            .password = WIFI_PASS,
        },
    };

    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());

    // Создаём таймер один раз
    s_dhcp_timer = xTimerCreate("dhcp_timer",
                                pdMS_TO_TICKS(DHCP_TIMEOUT_MS),
                                pdFALSE,      // однократный; перезапускаем вручную
                                NULL,
                                dhcp_timeout_cb);

    // Ждём либо успешного получения IP, либо провала
    EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
                                           WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
                                           pdFALSE,
                                           pdFALSE,
                                           portMAX_DELAY);

    if (bits & WIFI_CONNECTED_BIT) {
        ESP_LOGI(TAG, "Connected!");
    } else if (bits & WIFI_FAIL_BIT) {
        ESP_LOGE(TAG, "Failed to connect!");
    }
}

void app_main(void)
{
    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);

    wifi_init_sta();
}

Этот же таймер можно применять и в других случаях, например при ожидании события WIFI_EVENT_STA_CONNECTED после запуска попытки подключения. Хотя при попытке подключения таймаут и есть, но ещё один таймаут “сверху” точно не помешает.

PS: После успешного подключения и получения IP таймер можно удалить, но код придется немного переделать.

 

Пример поочередного подключения к нескольким разным AP

Вы наверняка уже обратили внимание – на подключение к точке доступа в первом варианте дается 30 попыток, после чего попытки подключения прекращаются полностью. При определенных условиях с такой логикой ESP может и вовсе “уйти в глухую несознанку”, что таки тоже не есть хорошо. Вам необходимо предусмотреть какую-то логику, чтобы после N безуспешных попыток подключения к точке доступа выполнялось что-то ещё: либо попробовать подключиться к резервной точке доступа, либо перейти в режим SoftAP и поднять Web-сервер для настройки, либо просто программно перегрузить саму ESP.

В качестве примера можно привести вариант сценария для списка из трех заранее пред-настроенных точек доступа, к которым ESP пытается подключиться поочередно:

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "freertos/task.h"

#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"

#define WIFI_SSID_1 "AP1"
#define WIFI_PASS_1 "12345678"
#define WIFI_SSID_2 "AP2"
#define WIFI_PASS_2 "87654321"
#define WIFI_SSID_3 "AP3"
#define WIFI_PASS_3 "12340987"

#define WIFI_CONNECTED_BIT BIT0

static const char *ssid_list[3] = {WIFI_SSID_1, WIFI_SSID_2, WIFI_SSID_3};
static const char *pass_list[3] = {WIFI_PASS_1, WIFI_PASS_2, WIFI_PASS_3};

static const char *TAG = "wifi_example";

static EventGroupHandle_t s_wifi_event_group;
static int s_retry_num = 0;
static int s_ssid_index = 0;
static wifi_config_t wifi_config = {
    .sta = {
        .ssid = "",
        .password = "",
    },
};

// ===== Обработчик событий Wi-Fi/IP с переподключением =====
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) {
        // Сбрасываем флаг подключения, если он был установлен
        xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT); 
        // STA запущен — пробуем подключиться
        esp_wifi_connect();
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        // Сбрасываем флаг подключения, если он был установлен
        xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT); 
        // Автоматическое переподключение с ограничением по числу попыток
        if (s_retry_num < 5) {
            esp_wifi_connect();
            s_retry_num++;
            ESP_LOGW(TAG, "Retry to connect to %s (%d)", ssid_list[s_ssid_index], s_retry_num);
        } else {
            ESP_LOGE(TAG, "Failed to connect to %s", ssid_list[s_ssid_index]); 
            // Сбрасываем счетчик попыток
            s_retry_num = 0;
            // Переключаем точку доступа
            s_ssid_index++;
            if (s_ssid_index > 2) s_ssid_index = 0;
            // Настраиваем новые параметры AP
            strncpy((char *)wifi_config.sta.ssid, ssid_list[s_ssid_index], sizeof(wifi_config.sta.ssid));
            strncpy((char *)wifi_config.sta.password, pass_list[s_ssid_index], sizeof(wifi_config.sta.password));
            ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
            esp_wifi_connect();
            ESP_LOGI(TAG, "Try connect to AP %s", ssid_list[s_ssid_index]); 
        }
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        // Устанавливаем флаг "подключено"
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); 
        // Сбрасываем счетчик попыток
        s_retry_num = 0;
        // Выводим 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));
    }
}

// ===== Инициализация и постоянная работа Wi-Fi (без деинициализации) =====
static void wifi_init_and_connect(void)
{
    // Инициализация NVS (нужен Wi-Fi драйверу)
    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);

    // Создаем Event group для сигнализации о статусе подключения
    s_wifi_event_group = xEventGroupCreate();

    // Запускаем TCP/IP стек и event loop
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    // Создаём стандартный STA-интерфейс
    esp_netif_create_default_wifi_sta();

    // Инициализация Wi-Fi драйвера
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    // Регистрация обработчиков Wi-Fi и IP событий
    ESP_ERROR_CHECK(esp_event_handler_instance_register(
        WIFI_EVENT,
        ESP_EVENT_ANY_ID,
        &event_handler,
        NULL,
        NULL));  // instance не нужен, деинициализации не делаем

    ESP_ERROR_CHECK(esp_event_handler_instance_register(
        IP_EVENT,
        IP_EVENT_STA_GOT_IP,
        &event_handler,
        NULL,
        NULL)); // instance не нужен, деинициализации не делаем

    // Конфигурация STA
    s_ssid_index = 0;
    strncpy((char *)wifi_config.sta.ssid, ssid_list[s_ssid_index], sizeof(wifi_config.sta.ssid));
    strncpy((char *)wifi_config.sta.password, pass_list[s_ssid_index], sizeof(wifi_config.sta.password));

    // Настраиваем WiFi-драйвер
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));

    // Запуск Wi-Fi
    ESP_ERROR_CHECK(esp_wifi_start());
    ESP_LOGI(TAG, "WiFi started, try connect to AP %s", ssid_list[s_ssid_index]); 

    // Ожидаем успешное подключение или неудачу
    EventBits_t bits = xEventGroupWaitBits(
        s_wifi_event_group,
        WIFI_CONNECTED_BIT,
        pdFALSE,
        pdFALSE,
        portMAX_DELAY);

    if (bits & WIFI_CONNECTED_BIT) {
        ESP_LOGI(TAG, "Connected to AP");
    } else {
        ESP_LOGE(TAG, "Unexpected event bits: 0x%" PRIx32, bits);
    }

    // Дальше ничего делать не нужно, Wi-Fi остаётся запущенным, 
    // обработчик будет автоматически переподключать при разрыве.
}

void app_main(void)
{
    wifi_init_and_connect();

    // Основная логика приложения; Wi-Fi работает в фоне
    while (1) {
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

Этот пример работает так: если не удалось подключиться к первой AP1, то спустя N попыток будем пробовать подключиться ко второй AP2, потом к AP3, затем начнем сначала. Это даст возможность, например, подключить ESP к заранее созданной резервной точке доступа на смартфоне, и перенастроить ESP либо обновить прошивку.

Но нет предела совершенству – усовершенствовать его можно до бесконечности. Вы наверняка найдете здесь то, что можно улучшить. Я лишь даю вам пищу для размышления.

 


Взболтать, но не пере-mesh-ивать

Ещё одна задачка, иногда встречающаяся на практике. Допустим, вы имеете несколько роутеров, расположенных в разных частях дома, при этом объединенных в одну локальную сеть, с одинаковыми SSID и паролями, с поддержкой WNM BTM. Такие сеть часто называются mesh-сетями (здесь имеется в виду не технология ESP-WIFI-MESH (ви не понимаете, это другое)).

При подключении к такой WiFi-сети станция ESP должна сама выбрать, к какой точке доступа подключиться, например ориентируясь по уровню сигнала RSSI. 

Для этой задачи подойдёт самый стандартный STA‑режим, но только с «полным» сканированием и сортировкой точек доступа по уровню сигнала RSSI. Тогда драйвер при подключении сам выберет точку доступа с лучшим уровнем сигнала среди всех найденных с нужным SSID.

wifi_config_t wifi_config = {
    .sta = {
        .ssid = WIFI_SSID,
        .password = WIFI_PASS,
        // Полное сканирование всех каналов
        .scan_method = WIFI_ALL_CHANNEL_SCAN,
        // Сортировка по уровню сигнала (RSSI)
        .sort_method = WIFI_CONNECT_AP_BY_SIGNAL,
    },
};

ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));

Вот, собственно, и всё – никаких хитростей. 

 


Настройка WiFi STA режима на ESP32 “извне”, например используя смартфон

Во всех примерах данной статьи мы использовали параметры подключения к точке доступа (SSID и пароль) как жестко вшитые в прошивку константы, либо как константы из системы конфигурации сборки menuconfig. Хотя такой подход очень часто встречается на практике из-за своей простоты и банальности, но обладает очень существенным недостатком – невозможность смены SSID и пароля без перепрошивки SoC, что может оказаться критическим недостатком.

Между тем, эту проблему можно решить, причем несколькими способами. В документации этот режим работы называется provisioning / провижининг (перевод: предоставление ресурсов). И хотя в рамках данной статьи я не планирую обсуждать это подробно, но давайте хотя бы перечислим некоторые возможные способы.

 

1. Режим STA + точка доступа “по умолчанию”.

В этом случае вы изначально закладываете в прошивку какие-то параметры подключения “по умолчанию”, точно также как это и было изложено в данной статье. Например это может быть смартфон с заранее настроенной точкой доступа – тогда первоначальный запуск или восстановление настроек можно будет сделать где угодно.

После первого запуска (либо если не удалось подключиться к ранее сохраненной в памяти точке доступа) ESP подключается к этой точке доступа “по умолчанию” и на нее можно отправить реальные SSID и пароль для подключения к другой точке доступа (например используя MQTT протокол). Когда ESP получает новые SSID и пароль, она сохраняет их в NVS разделе как это было описано выше, и при следующей перезагрузке пытается подключиться уже с новыми, сохраненными в NVS, данными.

Достоинством этого метода является его простота и легкость реализации, особенно если ESP всегда управляется удаленно. Я думаю, вы сами сможете реализовать этот подход по материалам данной статьи. 

Недостатком такого подхода является необходимость иметь под рукой точку доступа “по умолчанию” по крайней мере при первом запуске. Например вы не можете отдать или продать свое устройство, которое заранее настроено на вашу точку доступа “по умолчанию” – конечный пользователь не сможет перенастроить его без вашего участия.

 

2. SoftAP + Web (HTTP) Provisioning

Звучит страшно, но на деле все просто – это самый классический способ с использованием веб-интерфейса.

ESP поднимает собственную точку доступа (SoftAP). Пользователь подключается к этой сети со смартфона/ПК и через веб‑страницу (HTTP‑сервер на ESP) вводит SSID/пароль от домашнего роутера. После этого ESP переключается в STA и подключается к заданной ранее сети. Такой сценарий прямо описан как типичный workflow для Wi‑Fi‑Provisioning. [Soft-AP tutorial]

В стандартной прошивке ESP‑AT это называется SoftAP Provisioning (WEB Provisioning): ESP работает в режиме SoftAP+STA, поднимает веб‑сервер, пользователь заходит по IP (обычно 192.168.4.1), видит страницу, вводит SSID/пароль, ESP подключается к роутеру. [Web server example]

Здесь возможны варианты.

  1. Переходить в режим SoftAP можно в следующих случаях:
    • автоматически при первом включении или при невозможности подключиться к сохраненной точке доступа заданное число раз
    • нажатии какой-то специальной кнопки
  2. Можно использовать совмещенный режим STA+SoftAP и всегда держать “наготове” web-сервер для изменения настроек.

Достоинством этого метода является его универсальность – на смартфоне вам не потребуется устанавливать дополнительных программ. Плюс высокая надёжность, простая реализация на стороне ESP, не требует использования BLE. 

Недостаток: пользователь должен вручную переключать Wi‑Fi сети на телефоне. К недостаткам можно отнести и необходимость создания кода, который по сути будет являться бесполезным балластом большую часть времени. Кроме того, если вы выбрали стратегию STA+SoftAP+Web сервер как постоянно работающую связку, это приведет к бесполезному расходу памяти и ресурсов.

 

3. SmartConfig Provisioning

Для реализации этого способа на телефоне вам понадобится приложение EspTouch (Android/iOS) или другое SmartConfig‑совместимое приложение. Телефон, уже подключённый к домашнему Wi‑Fi, с помощью специально установленного приложения посылает в эфир специальные UDP‑пакеты (разной длины), в которых закодированы SSID и пароль. ESP при этом работает в режиме «прослушивания» (promiscuous/sniffer), обнаруживает и декодирует эти пакеты и извлекает из них параметры сети. Затем эти параметры, как и в других случаях, сохраняются в NVS разделе и ESP перезагружается в STA режиме. [Provisioning scheme]

В прошивке ESP‑AT SmartConfig доступен как один из стандартных способов провижининга (ESP‑TOUCH, AirKiss, ESP‑TOUCH v2 и т.д.). [ESP32-S2 AT provisioning]

Плюсы данного подхода: пользователю не нужно переключать Wi‑Fi сеть, телефон всегда остаётся в своей домашней сети.

Недостатки: успех операции настройки сильно зависит от окружающей радиосреды. Ну и конечно же то, что в этом случает требуется специальное мобильное приложение (EspTouch / WeChat AirKiss и т.п.).

 

4. BluFi (BLE‑based Provisioning)

Суть данного метода: ESP рекламируется по BLE (имя вида PROV_xxxx и т.п.), ваш телефон с помощью специального приложения находит устройство, устанавливает с ним BLE‑соединение и с помощью GATT‑сервиса передаёт SSID и пароль. Пользователь при этом не переключает Wi‑Fi сеть, только включает Bluetooth. [Provisioning schemeRainMaker tips]

В прошивке ESP‑AT BluFi — один из основных рекомендованных способов провижининга (в таблицах он помечен как «Recommended»). [ESP32 AT provisioningESP32-C3 AT provisioning]

Для реализации этого способа потребуется:

  • Поддержка BLE (не все чипы это поддерживают, например ESP32‑S2 не имеет поддержки BLE).
  • В ESP‑IDF: можно использовать как пример bluetooth/blufi и реализовать обработку Wi‑Fi настроек.
  • В ESP‑AT: прошивка с поддержкой BluFi (для некоторых чипов/конфигураций включено по умолчанию, см. таблицы [Provisioning scheme; ESP32 AT provisioningESP32-C2 AT provisioning]).
  • На телефоне необходимо установить приложение BluFi (или другие официальные Android/iOS приложения).

Плюсы данного метода: хороший UX, телефон остаётся в интернете, высокая надёжность.

Минусы данного метода: нужен BLE и дополнительный BLE‑код на устройстве, что увеличивает размер прошивки и потребление памяти во время провижининга. Ну и конечно же то, что в этом случает требуется специальное мобильное приложение BluFi (или другие официальные Android/iOS приложения).

 

5. WPS Provisioning

В данном случае используется стандарт WPS: для его запуска потребуется либо кнопка на роутере и на устройстве, либо какой-либо способ для программного запуска WPS. В этом случае ESP автоматически получает параметры сети от роутера. В ESP‑AT WPS также рассматривается как один из способов провижининга. [ESP32 AT provisioningESP32-S2 AT provisioning]

Для успешной реализации этого способа потребуется поддержка WPS в прошивке (ESP‑IDF/ESP‑AT) и включенный WPS на роутере.

Плюсы данного метода: не нужно отдельное мобильное приложение, высокая надёжность способа.

Минусы подхода: чуть более сложная операция для пользователя (нажатие кнопки или использованием специального меню роутера, не все роутеры поддерживают или включают WPS.

 

Подведем итоги:

  • BluFi (BLE) — «Recommended», высокая успешность, хороший UX, но нужен BLE.
  • SmartConfig — «Moderate», средняя успешность, простой UX, но зависит от среды.
  • SoftAP (WEB) — «Moderate», высокая успешность, но более сложный UX (ручное переключение Wi‑Fi).
  • WPS — «Moderate», высокая успешность, но сложнее в использовании и зависит от роутера.

В реестре компонентов Espressif существуют готовые компоненты (библиотеки), которые работают “поверх” ESP-IDF и могут реализовывать несколько способов одновременно.

Некоторые способы я планирую обсудить в следующих статьях, но вы вполне можете ознакомиться с ними самостоятельно по приведенным в тексте ссылкам.

 


Как использовать protocol_examples_common для подключения к wifi

Если вы когда-либо пробовали запускать примеры для различных сетевых протоколов из папки examples, то наверное заметили, что в них как правило отсутствует код, в котором непосредственно реализовано подключение к точке доступа в режиме STA. Даже такой простой, как в данной статье. Дело в том, что все эти примеры используют специальный компонент protocol_examples_common,  в котором как раз и реализована данная функциональность.

Найти его можно по пути ${IDF_PATH}/examples/common_components/protocol_examples_common

Этот компонент реализует наиболее распространенные методы подключения ESP32 к сети – например это могут быть WiFi STA, Ethernet, PPP-соединения. Но он предназначен для  использования в тестовых приложения и примерах ESP-IDF для демонстрации функциональности сетевых протоколов и других библиотек, для которых этап подключения является обязательным условием. Следует учитывать, что данный компонент не предназначен для использования в реальных проектах – об этом явно предупреждает нас описание функции подключения example_connect():

/**
 * @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); 

 

Использование protocol_examples_common в проекте

Прежде чем использовать этот компонент в проекте, необходимо включить его в сборку.

  • Для ESP-IDF необходимо добавить зависимость от protocol_examples_common для главного компонента приложения в файле idf_component.yml:
    dependencies:
      protocol_examples_common:
        path: ${IDF_PATH}/examples/common_components/protocol_examples_common 

     

  • Для PlatformIO + ESP-IDF этот фокус не прокатывает, так как PlatformIO имеет свою, несколько отличающуюся систему сборки. Поэтому добавить данный компонент придется путем добавления пути к нему в список EXTRA_COMPONENT_DIRS. Это можно сделать в файле CMakeLists.txt проекта:
    cmake_minimum_required(VERSION 3.16.0)
    
    # This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
    list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
    
    include($ENV{IDF_PATH}/tools/cmake/project.cmake)
    project(https_requests)
    

     

После этого можно будет включить заголовочный файл в файл проекта:

#include "protocol_examples_common.h"

Использовать protocol_examples_common не просто, а очень просто! Но следует помнить, что инициализацию NVS, Default Event Loop и NETIF вы должны выполнить самостоятельно.

ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());

/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
 * Read "Establishing Wi-Fi or Ethernet Connection" section in
 * examples/protocols/README.md for more information about this function.
 */
ESP_ERROR_CHECK(example_connect());
 

Функция example_connect() выполняется в блокирующем режиме – то есть код, расположенный после нее, будет выполнен только после успешного подключения к выбранным сетевым интерфейсам.

 

Настройка protocol_examples_common

Но прежде чем компилировать проект, необходимо настроить подключение(я) с помощью стандартной утилиты menuconfig.

Выберите предпочтительный интерфейс (Wi-Fi STA, Ethernet, Thread, PPPoS) для подключения к сети и настройте его. Можно включить несколько интерфейсов одновременно, при этом этап подключения будет блокироваться до тех пор, пока все выбранные интерфейсы не получат IP-адреса. Также можно отключить все интерфейсы, пропустив этап подключения.

  • WiFi
    Выберите способ подключения к WiFi (для чипсетов, поддерживающих его) и настройте основные параметры подключения WiFi:

    • SSID WiFi
    • Пароль WiFi
    • Максимальное количество попыток подключения (подключение будет прервано, если оно не удастся после указанного количества попыток)
    • Метод сканирования WiFi (включая пороговое значение RSSI и пороговое значение режима авторизации)
  • Ethernet
    Выберите подключение Ethernet, если ваша плата его поддерживает, например Espressif Ethernet Kit. Вы также можете выбрать устройство Ethernet SPI (если ваш чипсет не поддерживает внутренний EMAC или если вы предпочитаете его). Также можно использовать Ethernet MAC OpenCores, если вы запускаете пример под QEMU.
  • Thread
    Выберите подключение Thread, если ваша плата поддерживает радиомодуль IEEE802.15.4 или работает с OpenThread RCP. Вы можете настроить сеть Thread в меню OpenThread->Основные функции Thread->Операционный набор данных Thread.
  • PPP
    Метод соединения «точка-точка» создает простой IP-туннель к устройству-партнеру (на котором запущен PPP-сервер), обычно этим сервером является машина Linux со службой pppd. В настоящее время поддерживается только PPP-соединения через последовательный порт (с использованием UART или USB CDC). Это полезно для простого тестирования сетевых уровней, но с помощью дополнительной настройки на стороне сервера можно имитировать стандартную модель подключения к Интернету. Сервер PPP также может быть представлен сотовым модемом с предварительно настроенным подключением и уже переключенным в режим PPP (однако эта настройка не очень простая, поэтому производитель рекомендует использовать стандартную библиотеку для модемов, реализующую команды и режимы, например, [esp_modem].

 


Конфигурация драйвера WiFi через menuconfig

В заключение стоит немного поговорить за опции menuconfig, которые относятся к драйверу WiFi. Все опции сгруппированы в три отдельных раздела:

  • ESP NETIF Adapter – параметры универсального сетевого интерфейса esp_netif
  • PHY – параметры физического уровня (радиоканала 2,4/5 ГГц)
  • Wi-Fi – параметры WiFi-драйвера

Что характерно – обнаглели в документации Espressif не нашлось полного описания опций menuconfig ни для одного из указанных разделов. Поэтому все что описано в данном разделе – просто перевод help-а из KConfig. Если я где-то ошибся, прошу меня поправить в комментариях. Но и из этих описаний при желании можно узнать много интересного и полезного.

Состав меню конфигурации во многом зависит от серии чипа и других задействованных опций – поэтому ваш список может отличаться от приведенного ниже. Кроме того, все описанное ниже относится только к версии ESP-IDF 5.4.1 – со временем данная информация может устареть.

 

Component config → ESP NETIF Adapter

  • (120)IP Address lost timer interval (seconds) – IP-адрес может быть потерян по ряду причин, например, при отключении станции от точки доступа или при неудачной попытке обновления IP-адреса по DHCP и т. д. Если таймер потери IP-адреса включен, он будет запускаться каждый раз при потере IP-адреса. Событие SYSTEM_EVENT_STA_LOST_IP будет сгенерировано, если истекло время ожидания таймера. Таймер потери IP-адреса останавливается, если станция получает IP-адрес вновь до истечения таймера. Значение 0 указывает на то, что таймер потери IP-адреса отключен, в противном случае таймер включен.
  • [ ]Use only ESP-NETIF headers – Реализация функций ESP-NETIF не предоставляется. Этот параметр используется для добавления пользовательского варианта стека TCP/IP и переопределения соответствующей функциональности esp_netif.
  •     TCP/IP Stack Library (LwIP) – Выберите используемый стек TCP/IP, например, LwIP, uIP и т. д.
  • [*]Report data traffic via events – Включите эту опцию, если функции esp_netif_transmit() и esp_netif_receive() должны генерировать события. Это может быть использовано для мигания индикаторов трафика данных.
  • [ ]Use esp_err_t to report errors from esp_netif_receive – Включите эту опцию, чтобы функция esp_netif_receive() возвращала код ошибки. Это полезно для информирования верхних уровней о том, что входящий пакет в стек TCP/IP не получен, чтобы верхние уровни могли реализовать управление потоком.
    Эта опция отключена по умолчанию из-за обратной совместимости и будет включена в версии 6.0 (IDF-7194).
  • [ ]Enable netif L2 TAP support – Пользовательская программа может считывать/записывать кадры канального уровня L2 с/на устройство ESP TAP.
    В настоящее время устройство ESP TAP может быть связано только с физическими интерфейсами Ethernet.
  • [ ]Enable LwIP IEEE 802.1D bridge – Включите поддержку моста LwIP IEEE 802.1D в ESP-NETIF. Обратите внимание, что параметр “Количество клиентов, хранящих данные в netif” (LWIP_NUM_NETIF_CLIENT_DATA) необходимо правильно настроить, чтобы мост LwIP был доступен!
  • [ ]Enable DNS server per interface – Включите эту опцию, чтобы использовать DNS-сервер, принадлежащий выбранному сетевому интерфейсу по умолчанию. Эта функция собирает информацию о DNS-серверах и netif из основных модулей LWIP. При выборе нового netif по умолчанию глобальные DNS-серверы в LWIP обновляются с указанием соответствующих серверов netif.

 

Component config → PHY

  • [*]Store phy calibration data in NVS – Если эта опция включена, при запуске радиоканала будет инициализирован NVS, и калибровочные данные будут загружены оттуда. Калибровка PHY будет пропущена при пробуждении из режима глубокого сна. Если калибровочные данные не найдены, будет выполнена полная калибровка, которая будет сохранена в NVS. Обычно выполняется только частичная калибровка.
    Если эта опция отключена, всегда будет выполнена полная калибровка.
    Если высока вероятность того, что ваша плата получит при калибровке некорректные данные, выберите «n».
    В двух случаях, например, следует выбрать «n»:
    1. Если ваша плата часто загружается с отключенной антенной.
    2. Из-за особенностей конструкции вашей платы результаты калибровки каждый раз слишком нестабильны.
    Если вы не уверены, выберите «y».
  • [ ]Use a partition to store PHY init data – Если эта опция включена, данные инициализации PHY будут загружены из раздела ‘phy‘. При использовании пользовательской таблицы разделов убедитесь, что раздел с данными PHY существует (тип: ‘data‘, подтип: ‘phy‘). В таблицах разделов по умолчанию это делается автоматически.
    Если данные инициализации PHY хранятся в указанном разделе, их необходимо предварительно прошить туда, иначе возникнет ошибка во время выполнения.
    Если эта опция не включена, данные инициализации PHY будут встроены в двоичный файл приложения.
    Если вы не уверены, выберите ‘n’.
  • (20)Max WiFi TX power (dBm) – Установите ограничение максимальной мощность передачи для модуля Wi-Fi. Фактическая мощность передачи при высоких скоростях передачи данных может быть ниже этого значения.
  • [ ]Reduce PHY TX power when brownout reset – При обнаружении понижения напряжения (brownout) и сброса, уменьшить мощность PHY TX, чтобы код продолжал работать.
  • [ ]Enable RF certification test functions Calibration mode – Выберите режим калибровки PHY. Если продолжительность загрузки не критична, рекомендуется использовать метод полной калибровки. Метод калибровки не используется только при выходе устройства из глубокого сна.
    Во время инициализации RF по умолчанию используется метод частичной калибровки.
    Полная калибровка занимает примерно на 100 мс больше, чем частичная.
  • [ ]Enable pll track logging – Если эта функция включена, во время отслеживания PLL будут создаваться записи в журнале.
  • [ ]Record PHY used time – Выберите этот параметр, чтобы поддерживать запись и запрос времени использования физических устройств.

 

Component config → Wi-Fi

  • (10)Max number of WiFi static RX buffers – Задайте количество статических RX-буферов Wi-Fi. Каждый буфер занимает приблизительно 1,6 КБ оперативной памяти. 
    Статические rx-буферы выделяются при вызове esp_wifi_init, они не освобождаются пока не будет вызван esp_wifi_deinit. Оборудование Wi-Fi использует эти буферы для приема всех кадров стандарта 802.11.
    Большее количество может обеспечить более высокую пропускную способность, но увеличивает использование памяти. Если включено ESP_WIFI_AMPDU_RX_ENABLED, рекомендуется установить это значение равным или большим, чем ESP_WIFI_RX_BA_WIN, чтобы обеспечьте более высокую пропускную способность и совместимость как со станциями, так и с точками доступа.
  • (32)Max number of WiFi dynamic RX buffers – Задайте количество динамических буферов приема Wi-Fi; значение 0 означает, что будет выделено неограниченное количество буферов приема (при условии наличия достаточного объема свободной оперативной памяти). Размер каждого динамического буфера приема зависит от размера полученного кадра данных.
    Для каждого полученного кадра данных драйвер Wi-Fi создает копию в буфер приема, а затем передает ее на верхний уровень стека TCP/IP. Динамический буфер приема освобождается после того, как верхний уровень успешно получит кадр данных.
    В некоторых приложениях кадры данных Wi-Fi могут приниматься быстрее, чем приложение может их обрабатывать. В таких случаях может возникнуть нехватка памяти, если количество буферов приема неограничено (0).
    Если установлен лимит динамических буферов приема, он должен быть как минимум равен количеству статических буферов приема.
  •     Type of WiFi TX buffers (Dynamic)Выберите тип буферов передачи WiFi:
    Если выбран параметр «Статический», буферы передачи WiFi выделяются при инициализации WiFi и освобождаются при деинициализации WiFi. Размер каждого статического буфера передачи фиксирован и составляет около 1,6 КБ.
    Если выбран параметр «Динамический», каждый буфер передачи WiFi выделяется по мере необходимости при передаче кадра данных драйверу WiFi из стека TCP/IP. Буфер освобождается после того, как кадр данных будет отправлен драйвером WiFi. Размер каждого динамического буфера передачи зависит от длины каждого кадра данных, отправленного уровнем TCP/IP.
    Если включена PSRAM, следует выбрать «Статический», чтобы гарантировать достаточное количество буферов передачи WiFi.
    Если PSRAM отключена, следует выбрать «Динамический», чтобы улучшить использование оперативной памяти.
  • (16)Max number of WiFi static TX buffersУстановите количество статических буферов передачи (TX) для WiFi. Каждый буфер занимает приблизительно 1,6 КБ ОЗУ. Статические буферы приема (RX) выделяются при вызове функции esp_wifi_init() и не освобождаются до вызова функции esp_wifi_deinit().
    Для каждого переданного кадра данных из стека TCP/IP верхнего уровня драйвер WiFi создает его копию в буфере передачи. Для некоторых приложений, особенно для приложений UDP, верхний уровень может передавать кадры быстрее, чем уровень WiFi. В таких случаях может закончиться количество буферов передачи.
  • (32)Max number of WiFi cache TX buffersУстановите количество буферов TX в кэше Wi-Fi.
    Для каждого пакета TX от вышестоящего сервера, например, LWIP и т. д., драйвер Wi-Fi должен выделить статический буфер TX и создать копию пакета от вышестоящего сервера. Если драйвер Wi-Fi не может выделить статический буфер TX, он кэширует пакеты от вышестоящего сервера в выделенную очередь буфера; эта опция используется для настройки размера кэшированной очереди TX.
  • (32)Max number of WiFi dynamic TX buffersЗадайте количество динамических буферов передачи WiFi. Размер каждого динамического буфера передачи не фиксирован, он зависит от размера каждого передаваемого кадра данных.
    Для каждого передаваемого кадра из стека TCP/IP верхнего уровня драйвер WiFi создает его копию в буфере передачи. Для некоторых приложений, особенно приложений UDP, верхний уровень может передавать кадры быстрее, чем уровень WiFi. В таких случаях может закончиться количество буферов передачи.
  •     Type of WiFi RX MGMT buffers – Выберите тип буферов управления приемом WiFi:
    Если выбран тип «Статический», буферы управления приемом WiFi выделяются при инициализации WiFi и освобождаются при деинициализации WiFi. Размер каждого статического буфера управления приемом фиксирован и составляет около 500 байт.
    Если выбран тип «Динамический», каждый буфер управления приемом WiFi выделяется по мере необходимости при получении кадра данных управления. Буфер управления освобождается после обработки кадра данных управления драйвером WiFi.
  • (5)Max number of WiFi RX MGMT buffers – Установите количество буферов WiFi RX_MGMT. Для буферов управления количество динамических и статических буферов управления одинаково. Во избежание фрагментации памяти тип буфера управления следует сначала установить на статический.
  • [ ]WiFi CSI(Channel State Information) – Выберите этот параметр, чтобы включить функцию CSI  (информация о состоянии канала). CSI занимает около
    CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM КБ ОЗУ. Если CSI не используется, лучше отключить для экономии памяти.
    эту функцию, чтобы сэкономить память.
  • [*]WiFi AMPDU TX – Выберите этот параметр, чтобы включить функцию AMPDU TX.
    • (6)WiFi AMPDU TX BA window size – Установите размер окна WiFi Block Ack TX. Как правило, большее значение означает более высокую пропускную способность, но
      расходует больше памяти. В большинстве случаев не следует менять значение по умолчанию, за исключением особых случаев, например, проверки максимальной пропускной способности UDP TX с помощью iperf и т. д. Для тестирования iperf в Shieldbox рекомендуемое значение составляет 9–12.
  • [*]WiFi AMPDU RX – Выберите этот параметр, чтобы включить функцию AMPDU RX.
    • (6)WiFi AMPDU RX BA window size – Установите размер окна подтверждения приема (RX) блока WiFi. Как правило, большее значение означает более высокую пропускную способность и лучшую совместимость, но требует больше памяти. В большинстве случаев не следует менять значение по умолчанию, если нет особых причин, например, для проверки максимальной пропускной способности UDP RX с помощью iperf и т. д. Для теста iperf в Shieldbox рекомендуемое значение составляет 9–12.
      Если используется PSRAM и предпочтительно сначала выделять память WiFi в PSRAM, то значение по умолчанию и минимальное значение должно быть 16 для достижения лучшей пропускной способности и совместимости как со станциями, так и с точками доступа.
  • [*]WiFi NVS flash  Выберите этот параметр для сохранения параметров Wi-Fi в NVS.
  •     WiFi Task Core ID (Core 0) – Закрепление задачи WiFi на ядре 0 или ядре 1.
  • (752)Max length of WiFi SoftAP Beacon – ESP-MESH использует кадры маяка для обнаружения и разрешения конфликтов корневых узлов (см. документацию). Однако длина кадра маяка по умолчанию может одновременно содержать только пять структур идентификаторов корневых узлов, что означает, что одновременно может быть обнаружен конфликт корневых узлов, включающий до пяти узлов. В случае возникновения большего количества конфликтов корневых узлов, затрагивающих более пяти узлов, процесс разрешения конфликтов обнаружит пять корневых узлов, разрешит конфликт и повторно обнаружит больше корневых узлов. Этот процесс будет повторяться до тех пор, пока не будут разрешены все конфликты корневых узлов. Однако этот процесс, как правило, может занять очень много времени.
    Чтобы избежать этой ситуации, длину кадра маяка можно увеличить, чтобы можно было одновременно обнаружить больше корневых узлов. Каждый дополнительный корневой узел потребует 36 байт и должен быть добавлен к длине кадра маяка по умолчанию, составляющей 752 байта. Например, если вы хотите одновременно обнаружить 10 корневых узлов, вам необходимо установить длину кадра маяка как 932 (752+36*5).
    Увеличение длины маяка также помогает в отладке, поскольку можно быстрее идентифицировать конфликтующие корневые узлы.
  • (32)WiFi mgmt short buffer number – Установите максимальное количество коротких буферов управления Wi-Fi. Эти буферы выделяются динамически, при этом их размер определяется длиной отправляемого пакета управления. Если размер пакета управления меньше 64 байт, драйвер Wi-Fi классифицирует его как короткий пакет управления и
    назначает его одному из этих буферов.
  • [*]WiFi IRAM speed optimization – Выберите этот параметр, чтобы разместить часто используемые функции библиотеки Wi-Fi в оперативной памяти IRAM. Если этот параметр отключен, будет сэкономлено более 10 Кбайт памяти IRAM, но пропускная способность Wi-Fi снизится.
  • [ ]WiFi EXTRA IRAM speed optimization – Выберите этот параметр, чтобы разместить дополнительные часто используемые функции библиотеки Wi-Fi в оперативной памяти IRAM. Если этот параметр отключен, будет сэкономлено более 5 Кбайт памяти IRAM, но пропускная способность Wi-Fi снизится.
  • [*]WiFi RX IRAM speed optimization – Выберите этот параметр, чтобы разместить часто используемые функции приема библиотеки Wi-Fi в оперативной памяти (IRAM). Если этот параметр отключен, будет сэкономлено более 17 Кбайт памяти IRAM, но производительность Wi-Fi снизится.
  • [*]Enable WPA3-Personal – Выберите этот параметр, чтобы разрешить устройству устанавливать соединение WPA3-Personal с подходящими точками доступа.
    PMF (Protected Management Frames) — это необходимая функция для соединения WPA3, её необходимо явно настроить перед попыткой подключения. Для получения подробной информации см. руководство по API драйвера Wi-Fi.

    • [*]Enable SAE-PK – Выберите этот параметр, чтобы включить SAE-PK.
    • [*]Enable WPA3 Personal(SAE) SoftAP – Выберите этот параметр, чтобы включить поддержку SAE в режиме softAP.
  • [*]Enable OWE STA – Выберите этот параметр, чтобы разрешить устройству устанавливать соединение OWE с подходящими точками доступа. PMF (Protected Management Frames) — это необходимая функция для соединения WPA3, её необходимо явно настроить перед попыткой подключения. Для получения подробной информации см. руководство по API драйвера Wi-Fi.
  • [ ]WiFi SLP IRAM speed optimization – Выберите этот параметр, чтобы разместить функции обработки и приема маяков библиотеки Wi-Fi TBTT в оперативной памяти (IRAM). Некоторые функции могут быть размещены в IRAM либо с помощью ESP_WIFI_IRAM_OPT и ESP_WIFI_RX_IRAM_OPT, либо с помощью этого параметра.
    Если ESP_WIFI_IRAM_OPT уже включен, оставшиеся 7,3 КБ памяти IRAM будут заняты этим параметром.
    Если ESP_WIFI_RX_IRAM_OPT уже включен, оставшиеся 1,3 КБ памяти IRAM будут заняты этим параметром.
    Если ни один из них не включен, оставшиеся 7,4 КБ памяти IRAM будут заняты этим параметром.
    При включении этого параметра средний потребляемый ток в режиме энергосбережения Wi-Fi будет снижен.
  • (50)Minimum active time – Только для станций в режиме WIFI_PS_MIN_MODEM или WIFI_PS_MAX_MODEM. Когда станция переходит в активное состояние, она будет работать как минимум в течение ESP_WIFI_SLP_DEFAULT_MIN_ACTIVE_TIME. Если в течение этого периода получен или отправлен пакет данных, то время активности будет обновлено. Если время истекло, но у станции еще есть пакеты, которые нужно получить или отправить, время активности также будет обновлено. Единица измерения: миллисекунды.
  • (10)Maximum keep alive time – Только для станций в режимах WIFI_PS_MIN_MODEM или WIFI_PS_MAX_MODEM. Если в течение SP_WIFI_SLP_DEFAULT_MAX_ACTIVE_TIME не было отправлено ни одного пакета данных, будет отправлен нулевой пакет данных для поддержания соединения с точкой доступа. Единица измерения: секунды.
  • (15)Minimum wait broadcast data time – Только для станций в режимах WIFI_PS_MIN_MODEM или WIFI_PS_MAX_MODEM. Когда станция узнает через маяк, что точка доступа отправит широковещательный пакет, она будет ждать ESP_WIFI_SLP_DEFAULT_WAIT_BROADCAST_DATA_TIME прежде чем перейти в спящий режим. Если получен  широковещательный пакет с большим количеством битов данных, время активности будет обновлено. Единица измерения: миллисекунды.
  • [*]Power Management for station at disconnected – Выберите этот параметр, чтобы включить управление питанием для станции при отключении от AP. Чип переведет модем в спящий режим, когда радиочастотный модуль больше не используется.
  • [*]WiFi GMAC Support(GMAC128 and GMAC256) – Выберите этот параметр, чтобы включить поддержку GMAC. Поддержка GMAC обязательна для сертификации Wi-Fi 192 бит.
  • [*]WiFi SoftAP Support – Модуль WiFi можно скомпилировать без поддержки SoftAP для уменьшения размера кода.
  • [ ]Wifi sleep optimize when beacon lost – Включите оптимизацию спящего режима Wi-Fi при потере сигнала и немедленно переходите в спящий режим, когда модуль Wi-Fi обнаруживает потерю сигнала.
  • (7)Maximum espnow encrypt peers number – Максимальное количество зашифрованных узлов, поддерживаемых espnow. Количество аппаратных ключей для шифрования фиксировано. И espnow и SoftAP используют одни и те же аппаратные ключи. Поэтому эта конфигурация повлияет на максимальное количество подключений SoftAP.
    Максимальное количество зашифрованных узлов espnow + максимальное количество подключений SoftAP = Максимальное количество аппаратных ключей. При использовании ESP mesh это значение следует установить максимум на 6.
  • [ ]WiFi Aware – Включите функцию WiFi Aware (NAN).
  • -*-Use MbedTLS crypto APIs – Выберите этот параметр, чтобы включить использование криптографических API MbedTLS. Внутренняя криптографическая поддержка в клиенте ограничена, и может быть недостаточной для всех новых функций безопасности, включая WPA3. Рекомендуется всегда оставлять этот параметр включенным. Кроме того, обратите внимание, что MbedTLS может использовать аппаратное ускорение, если оно доступно, что приводит к значительному ускорению криптографических операций.
    • [*]Use MbedTLS TLS client for WiFi Enterprise connection – Выберите этот параметр, чтобы использовать TLS-клиент MbedTLS для подключения Enterprise WPA2. Обратите внимание, что начиная с MbedTLS-3.0, MbedTLS не поддерживает SSL-3.0, TLS-v1.0 и TLS-v1.1. Если ваш сервер использует одну из этих версий,
      рекомендуется обновить сервер. Для совместимости со старыми версиями TLS отключите этот параметр.
  • [ ]Enable WAPI PSK support – Выберите этот параметр, чтобы включить WAPI-PSK, который является китайским национальным стандартом шифрования для беспроводных локальных сетей (GB 15629.11-2003).
  • [*]Enable 802.11k, 802.11v APIs Support – Выберите этот параметр, чтобы включить API 802.11k и 802.11v (поддержка RRM и BTM).
  • [*]Enable 802.11k APIs Support (NEW) – Выберите этот параметр, чтобы включить API 802.11k (поддержка RRM). В настоящее время поддерживаются только API, полезные для роуминга с поддержкой сети. Включите этот параметр вместе с включенным RRM в конфигурации станции, чтобы подготовить устройство к роумингу с поддержкой сети. Измерения радиосвязи позволяют позволяют станциям наблюдать и собирать данные о производительности радиоканала и о радиосреде. Текущая реализация добавляет отчет о маяках, измерение канала связи, отчет о соседях.
  • [*]Enable 802.11v APIs Support (NEW) – Выберите этот параметр, чтобы включить API 802.11v (поддержка BTM). В настоящее время поддерживаются только те API, которые полезны для роуминга с поддержкой сети. Включите этот параметр, если в конфигурации станции включена поддержка BTM, чтобы подготовить устройство к роумингу с поддержкой сети. Управление переходом BSS позволяет точке доступа запрашивать у станции переход на определенную точку доступа или указывать станции набор предпочтительных точек доступа.
  • [ ]Keep scan results in cache (NEW) – Сохранять результаты сканирования в кэше; если эта функция отключена, результаты будут немедленно удалены.
  • [ ]Enable Multi Band Operation Certification Support – Выберите этот параметр, чтобы включить поддержку сертификации работы в многодиапазонном режиме Wi-Fi.
  • [ ]Enable DPP support – Выберите этот параметр, чтобы включить поддержку WiFi Easy Connect.
  • [ ]Enable 802.11R (Fast Transition) Support – Выберите этот параметр, чтобы включить поддержку быстрого перехода по Wi-Fi.
  • [ ]Add WPS Registrar support in SoftAP mode – Выберите этот параметр, чтобы включить поддержку регистратора WPS в режиме SoftAP.
  • [ ]WPS Configuration Options – Выберите этот параметр, чтобы включить тщательную проверку каждого атрибута WPS. Отключение этой функции приведет к проблемам с совместимостью с различными точками доступа. Включение этой функции может вызвать проблемы совместимости с некоторыми точками доступа.
  • [ ]Print debug messages from WPA Supplicant – Выберите этот параметр, чтобы вывести информацию из журнала WPA-запросчика, включая информацию о рукопожатии и дампы шестнадцатеричных ключей в зависимости от уровня логирования проекта. Включение этой опции может увеличить размер сборки примерно на 60 КБ в зависимости от уровня логирования проекта.
  • [ ]Add DPP testing code – Выберите этот пункт, чтобы включить модульный тест для DPP.
  • [*]Enable enterprise option – Выберите этот параметр, чтобы включить/отключить поддержку enterprise подключений. Отключение этого параметра уменьшит размер бинарного файла. Отключение этого параметра отключит использование любых esp_wifi_sta_wpa2_ent_* (поскольку API станут бессмысленными).
    Обратите внимание, что при использовании больших сертификатов на маломощных чипах без аппаратного ускорения криптографии рекомендуется настроить сторожевой таймер задачи (TWDT), если он включен. Для получения точной информации о требованиях к времени выполнения вы можете проверить показатели производительности по адресу https://github.com/espressif/mbedtls/wiki/Performance-Numbers.

    • [ ]Free dynamic buffers during WiFi enterprise connection – Выберите эту конфигурацию, чтобы освободить динамические буферы во время подключения к корпоративной сети Wi-Fi. Это позволит чипу уменьшить потребление памяти в куче во время подключения к корпоративной сети Wi-Fi.

 


FAQ: вопросы и ответы

Данный раздел представляет собой перевод некоторых записей из ESP-FAQ » Software framework » Wi-Fi

 

Как настроить имя хоста для ESP32?

В качестве примера рассмотрим ESP-IDF V4.2. Вы можете перейти в menuconfig > Component Config > LWIP > Local netif hostname и ввести заданное вами имя хоста.

В разных версиях могут быть небольшие различия в названиях.

 

Что такое канал Wi-Fi? Могу ли я выбрать любой канал?

Канал Wi-Fi — это диапазон частот, используемый для беспроводной связи. В разных странах и регионах действуют различные ограничения на доступные каналы Wi-Fi. Например, в Северной Америке диапазон каналов Wi-Fi составляет от 1 до 11, а в Европе — от 1 до 13. Подробнее см. в руководстве по выбору каналов Wi-Fi для ESP8266.

 

[LWIP] Как настроить статический IP-адрес ESP32, когда он находится в режиме станции STA?

Поскольку ESP-IDF v4.2 и более поздние версии больше не поддерживают интерфейсы TCP/IP, рекомендуется использовать интерфейс ESP-NETIF.

Пример кода:

esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
if (sta_netif)
{
    esp_netif_ip_info_t info_t = {0};
    esp_netif_dhcpc_stop(sta_netif);

    info_t.ip.addr = ESP_IP4TOADDR(192, 168, 3, 23);
    info_t.gw.addr = ESP_IP4TOADDR(192, 168, 3, 1);
    info_t.netmask.addr = ESP_IP4TOADDR(255, 255, 255, 0);
    esp_netif_set_ip_info(sta_netif, &info_t);
}
esp_netif_dns_info_t dns_info = {0};

 

[LWIP] Как настроить содержимое параметров DHCP-сервера в ESP-IDF?

Поскольку ESP-IDF версии 4.1 и более поздние версии больше не поддерживают интерфейсы TCP/IP, рекомендуется использовать интерфейс ESP-NETIF.

Вы также можете обратиться к этому примеру при настройке DHCP-клиента. Пример кода выглядит следующим образом:

// Set up the handle for softap netif
esp_netif_t *ap_netif = esp_netif_create_default_wifi_ap();

// ESP_NETIF_IP_ADDRESS_LEASE_TIME, DHCP Option 51, Set the lease time for distributed IP address
uint32_t dhcps_lease_time = 60; // The unit is min
ESP_ERROR_CHECK(esp_netif_dhcps_option(ap_netif,ESP_NETIF_OP_SET,ESP_NETIF_IP_ADDRESS_LEASE_TIME,&dhcps_lease_time,sizeof(dhcps_lease_time)));

// ESP_NETIF_DOMAIN_NAME_SERVER , DHCP Option 6, Set DNS SERVER
// Set the local domain DNS first
esp_netif_dns_info_t dns_info = {0};
dns_info.ip.u_addr.ip4.addr = ESP_IP4TOADDR(8,8,8,8);
ESP_ERROR_CHECK(esp_netif_set_dns_info(ap_netif,ESP_NETIF_DNS_MAIN,&dns_info));

uint8_t dns_offer = 1; // Pass 1 to make the modified DNS take effect, if it is 0, then it means the gw ip of softap is used as the DNS server (0 by default)
ESP_ERROR_CHECK(esp_netif_dhcps_option(ap_netif,ESP_NETIF_OP_SET,ESP_NETIF_DOMAIN_NAME_SERVER,&dns_offer,sizeof(dns_offer)));

// ESP_NETIF_ROUTER_SOLICITATION_ADDRESS, DHCP Option 3 Router, Pass 0 to make the DHCP Option 3(Router) un-shown (1 by default)
uint8_t router_enable = 0;
ESP_ERROR_CHECK(esp_netif_dhcps_option(ap_netif,ESP_NETIF_OP_SET,ESP_NETIF_ROUTER_SOLICITATION_ADDRESS,&router_enable, sizeof(router_enable)));

// ESP_NETIF_SUBNET_MASK, DHCP Option 1, Configure the subnet mask
// If it fails to configure the subnet mask via ESP_NETIF_SUBNET_MASK, please make modifications using esp_netif_set_ip_info

 

Каково максимальное значение мощности радиочастотного сигнала Wi-Fi ESP32?

Выходная мощность радиосигнала ESP32 может быть установлена на уровне 20 дБм. Обратите внимание, что максимальная выходная мощность может различаться в разных странах и регионах. Убедитесь, что вы соблюдаете местные правила и нормы при использовании ESP32. Кроме того, высокая выходная мощность также влияет на срок службы батареи и стабильность сигнала Wi-Fi. В связи с этим необходимо уточнить выходную мощность в зависимости от конкретной области применения и требований.

 

Как можно регулировать мощность передачи Wi-Fi на ESP32?

  • Настройте Component config > PHY > Max Wi-Fi TX power(dBm) через menuconfig, максимальное значение составляет 20 дБм.
  • Используйте API esp_wifi_set_max_tx_power(int8_t power);.

 

[Connect] Как устранить проблему с невозможностью подключения устройств серии ESP32 к маршрутизатору по Wi-Fi как на аппаратном, так и на программном уровне?

Для устранения проблемы выполните следующие действия:

  • Во-первых, используйте код ошибки Wi-Fi, чтобы определить возможную причину сбоя.
  • Затем попробуйте подключить к маршрутизатору другое устройство, например, телефон, чтобы определить, проблема в маршрутизаторе или в ESP32.
    • Если телефон также не может подключиться к роутеру, пожалуйста, проверьте, нет ли проблем с самим роутером.
    • Если это возможно, пожалуйста, проверьте, нет ли каких-либо проблем с ESP32.
  • Шаги по устранению неполадок маршрутизатора:
    • Проверьте, не находится ли маршрутизатор в режиме выключения и перезагрузки. В этом режиме маршрутизатор не может быть подключен. Пожалуйста, не подключайтесь к нему, пока он не будет инициализирован.
    • Проверьте, совпадают ли настроенные SSID и пароль с данными маршрутизатора.
    • Проверьте, можно ли подключиться к маршрутизатору после его настройки в режиме OPEN.
    • Проверьте, может ли маршрутизатор подключаться к другим маршрутизаторам.
  • Шаги по устранению неполадок ESP32:
    • Устранение неполадок оборудования ESP32:
      • Проверьте, возникает ли проблема только в конкретном устройстве ESP32. Если она возникает в небольшом количестве конкретных устройств ESP32, определите вероятность её возникновения и сравните аппаратные различия между ними и обычными устройствами ESP32.
    • Устранение неполадок в программном обеспечении ESP32:
      • Проверьте, работает ли Wi-Fi-соединение, используя пример станции из ESP-IDF. В примере по умолчанию предусмотрен механизм переподключения, поэтому следите за тем, может ли ESP32 подключиться к Wi-Fi во время попытки переподключения.
      • Проверьте, совпадают ли настроенные SSID и пароль с данными маршрутизатора.
      • Проверьте, может ли ESP32 подключиться к маршрутизатору, когда маршрутизатор настроен в режиме OPEN.
      • Перед выполнением кода для подключения к Wi-Fi проверьте, может ли ESP32 подключиться к Wi-Fi после вызова API esp_wifi_set_ps(WIFI_PS_NONE).
    • Если все вышеперечисленные шаги по-прежнему не помогают выявить проблему, пожалуйста, перехватите пакеты Wi-Fi для дальнейшего анализа, обратившись к руководству пользователя Espressif Wireshark.
 

 

[Connect] Как определить причину сбоя при подключении ESP32 к Wi-Fi по кодам ошибок?

Когда функция обратного вызова получает событие WIFI_EVENT_STA_DISCONNECTED, причину ошибки можно получить через переменную reason структуры wifi_event_sta_disconnected_t.

if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
  wifi_event_sta_disconnected_t *sta_disconnect_evt = (wifi_event_sta_disconnected_t*)event_data;
  ESP_LOGI(TAG, "wifi disconnect reason: %d", sta_disconnect_evt->reason);
  esp_wifi_connect();
  xEventGroupClearBits(s_wifi_event_group, CONNECTED_BIT);
}

Для ESP-IDF версии 4.0 и более поздних версий обратитесь к следующим кодам, чтобы узнать причину:

  • WIFI_REASON_AUTH_EXPIRE: Этот код возвращается на этапе аутентификации, когда STA отправляет запрос аутентификации, но не получает ответа от точки доступа в течение указанного времени. Вероятность возникновения этого кода низкая.
  • WIFI_REASON_AUTH_LEAVE: Этот код отправляется точкой доступа, как правило, из-за того, что точка доступа по какой-либо причине отключилась от STA.
  • WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT или WIFI_REASON_HANDSHAKE_TIMEOUT: Неверный пароль. WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT — это стандартный обобщенный код ошибки, а WIFI_REASON_HANDSHAKE_TIMEOUT — это настраиваемый код ошибки. Основное отличие заключается в следующем: WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT возникает, когда маршрутизатор сообщает устройству о неверном пароле; WIFI_REASON_HANDSHAKE_TIMEOUT возникает, когда устройство само выполняет функцию тайм-аута, не получая от маршрутизатора уведомления о неверном пароле.
  • WIFI_REASON_CONNECTION_FAIL: Этот код возвращается во время фазы сканирования, когда STA сканирует соответствующую точку доступа, находящуюся в чёрном списке. Вероятно, это связано с тем, что точка доступа в прошлый раз была отключена от STA или произошла какая-то ошибка при подключении STA к точке доступа.

 

[Connect] Что означает число после переключения конечного автомата в журнале Wi-Fi?

Например: run -> init (fc0), здесь fc0 означает, что STA получила кадр деаутентификации, причина этого — ошибка пароля.

  • c0 указывает тип полученного кадра (00 указывает на тайм-аут).
  • f указывает причину.

Тип кадра: [a0 disassoc], [b0 auth], [c0 deauth].

 

[Connect] Когда ESP32 отключается от точки доступа SoftAP в режиме станции?

По умолчанию ESP32 отключается от точки доступа, если не получает сигнал маяка в течение 6 секунд. Это время можно изменить с помощью параметра esp_wifi_set_inactive_time.

 

[Scan] Почему STA иногда не может найти точку доступа во время сканирования?

Существует множество возможных причин, по которым ESP32 и ESP8266 не могут сканировать точки доступа. Ниже перечислены некоторые распространённые причины и способы их устранения.

  • Точка доступа расположена слишком далеко или сигнал слишком слабый, а Wi-Fi ESP32 и ESP8266 работает только в определённом диапазоне. Если точка доступа расположена слишком далеко или сигнал Wi-Fi слишком слабый, ESP32 и ESP8266 могут не сканировать точку доступа. Вы можете переместить ESP32 или ESP8266 ближе к точке доступа или использовать усилитель сигнала для усиления сигнала.
  • SSID точки доступа скрыт. SSID некоторых точек доступа может быть скрыт, поэтому они не будут транслироваться на близлежащие устройства. В этом случае ESP32 и ESP8266 не смогут сканировать эти точки доступа. Вы можете подключиться к этим точкам доступа, введя их SSID и пароли вручную.
  • Точка доступа перегружена или работает со сбоями. Если точка доступа перегружена или работает со сбоями, она может не обрабатывать новые запросы на подключение, и, следовательно, ESP32 и ESP8266 не смогут подключиться к точке доступа. Попробуйте подождать некоторое время, а затем снова просканировать точку доступа.
  • У ESP32 или ESP8266 есть некоторые проблемы с программным обеспечением. Иногда проблемы с программным обеспечением ESP32 или ESP8266 могут вызывать проблемы со сканированием точек доступа. В этом случае можно попробовать сбросить настройки ESP32 или ESP8266 и перезапустить Wi-Fi. Если этот метод не работает, может потребоваться обновление прошивки ESP32 или ESP8266.
  • Другие причины включают помехи в беспроводной сети, настройки безопасности и конфигурацию сети. Эти причины также могут влиять на работу Wi-Fi ESP32 или ESP8266. В этом случае необходимо тщательно проверить Wi-Fi-среду и настроить соответствующие параметры.

 

[Scan] Какое максимальное количество точек доступа можно просканировать?

Максимальное количество точек доступа, которые можно просканировать, не ограничено. Количество зависит от количества точек доступа и настроек параметров сканирования, таких как время, проведенное на каждом канале. Чем больше время, проведенное на каждом канале, тем выше вероятность обнаружения всех точек доступа.

 

Поддерживает ли ESP32 Wi-Fi переключение между различными точками доступа с одним и тем же SSID?

Да, в настоящее время поддерживаются протоколы 802.11k и 802.11v. См. пример роуминга.

 

Как настроить максимальную скорость и стабильность передачи данных по Wi-Fi с помощью ESP32, не учитывая потребление памяти и энергии?

Для настройки максимальной скорости и стабильности передачи данных по Wi-Fi, пожалуйста, обратитесь к руководству по программированию ESP-IDF «Как улучшить производительность Wi-Fi» и установите соответствующие параметры конфигурации menuconfig. Путь к параметрам можно найти, выполнив поиск «/» в menuconfig интерфейсе. Оптимальные параметры конфигурации необходимо протестировать в соответствии с реальными условиями эксплуатации.

 

В режиме AP + STA, после подключения ESP32 к Wi-Fi, повлияет ли включение или выключение режима AP на качество Wi-Fi-соединения?

После подключения ESP32 к Wi-Fi в двойном режиме AP + STA режим AP можно включать или выключать по желанию, не влияя на соединение Wi-Fi STA.

 

Как оптимизировать медленное получение IP-адреса на ESP32 после подключения к Wi-Fi в условиях слабой сети или помех?

  • Вы можете отключить режим сна модема, используя функцию esp_wifi_set_ps(WIFI_PS_NONE); после запуска Wi-Fi, и включить режим сна модема после получения события IP_EVENT_STA_GOT_IP.
  • В случае повторного подключения после разрыва соединения можно вручную отключить режим сна модема до подключения и включить его после получения события IP_EVENT_STA_GOT_IP.

Примечание: Данная оптимизация не применима для сценариев сосуществования Wi-Fi и Bluetooth.

 

Как получить уровень сигнала Wi-Fi RSSI при использовании ESP32?

Структура wifi_ap_record_t содержит информацию о подключенной точке доступа, включая SSID, BSSID, канал и тип шифрования. Поле RSSI представляет значение RSSI точки доступа. Вызовите функцию esp_wifi_sta_get_ap_info() для получения информации из этой структуры. 

При использовании ESP32 в качестве станции в версии ESP-IDF 4.1 вы можете использовать следующий пример кода для получения уровня сигнала RSSI подключенной точки доступа:

wifi_ap_record_t ap_info;
if (esp_wifi_sta_get_ap_info(&ap_info) == ESP_OK) {
  int rssi = ap_info.rssi;
  // handle rssi
}

 

Как полностью отключить настройки Wi-Fi на ESP32 для оптимизации размера прошивки, когда функции Wi-Fi не требуются?

Что касается программного обеспечения, просто избегайте вызова функции esp_wifi_init() . Это гарантирует отсутствие кода и конфигураций Wi-Fi, что не повлияет на размер прошивки.

 

 


Ссылки

  1. Документация Espressif: Обзор WiFi драйвера
  2. Документация Espressif: WiFi  API
  3. Документация Espressif: WiFi Driver

Пожалуйста, оцените статью:
[ 4.7 из 5, всего 7 оценок ]

-= Каталог статей (список по разделам) =-   -= Архив статей (плитки, все подряд) =-

Небольшой анонимный опрос: какие статьи вы предпочитаете?

2 комментария для “Подключение к сети WiFi в режиме STA на ESP-IDF”

  1. Дмитрий

    1) Заметил в обработке события WIFI_EVENT_STA_DISCONNECTED: при получении этого сигнала сразу запрос на подключение.
    Проблема в том, что если так делать, то будет нестабильно работать SoftAP, так как при подключении ESP32 сканирует сеть перед подключением, что приводит к отвалам клиентов.
    Лучше отложить переподключение при получении такого сигнала. Ну и нет смысла переподключать, если причиной отключения служил неправильно введённый пароль, а также нет смысла переподключать, если отключение от сети инициировано логикой. Иначе WiFi может заблокироваться.
    2) И ещё задание страны подключения. У меня схавал RU, при этом вручную прописать каналы:
    {
    wifi_country_t country = {
    .cc = “RU”,
    .schan = 1,
    .nchan = 13,
    .policy = WIFI_COUNTRY_POLICY_AUTO,
    };
    esp_wifi_set_country(&country);
    }
    3) Во время сканирования WiFi сетей могут быть отвалы клиентов SoftAP, поэтому сканировал один канал за проход и через интервал переключал канал для сканирования, кэшировал сети и в ответе выдавал данные из кэша. А при подключении при первом запросе по SoftAP запускается реальное подключение, а при повторных – последний статус подключения. При этом, запрет на реальное переподключение действует, если с момента последнего запроса на подключение меньше определенного интервала, или если реквизиты сети поменялись

    1. По поводу 1 и 3 пункта вы абсолютно правы, но…
      В данной статье я писал пока исключительно про STA.
      Да, при STA+AP неизбежно вылезут вот такие вот косяки с отвалом клиентов, про которые вы написали. Но кто-то их вообще никогда и не заметит, если SoftAP используется только для веб-морды.
      Про SoftAP и смешанный режим по хорошему нужна отдельная статья, чтобы не мешать все в кучу и не сбивать с толку новичков. А в ней можно и разобрать все эти конфликты. Потому что смешанный режим нужен далеко не всем и не всегда.
      Да и примеры логики я приводил самые простые – для тех, кто только осваивает технологию.

      По поводу страны. Я подозревал, что поддержка RU должна быть. Не может быть чтобы китайцы проигнорировали вчистую. Занимательно, что справочная система Espressif об этом ничего не знает. Спасибо

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

68  −  65  =