[ESP32S3 Undocumented] Скрытые нотификации WIFI стека. Ну вдруг нужно кому

Привет,

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

Скрытый механизм уведомлений WiFi. Мигать светодиодиками можно!

ESP32-S3 (и другие ESP32 с встроенным WiFi) имеют скрытый механизм, который Espressif использует для отладки своей RF-подсистемы.

Помимо отладки, его можно использовать для создания светодиодных индикаторов состояния устройства - примерно как светодиоды на домашнем WiFi-роутере.

WiFi-стек ESP32 позволяет установить обработчик (hook), который вызывается драйвером WiFi при определённых событиях:

static int wifi_hook(int x, int y); // --> ваша функция-обработчик

extern void wifi_set_gpio_debug_cb(void *handler); // скрытый API ESP-IDF для установки обработчика

После установки обработчик начинает вызываться довольно часто во время нормальной работы WiFi. Если выполнять в нём что-то медленное, это замедлит весь WiFi-стек.

Функция получает два параметра - небольшие целые числа, определяющие тип события.

Судя по именам функций и диапазонам аргументов, я предполагаю, что параметр x когда-то использовался как номер GPIO, а y содержал данные, которые нужно было вывести на этот GPIO.

Вместо этого мы можем подключить светодиоды к произвольным GPIO и включать/выключать их, получая наглядную индикацию состояния устройства без всяких printf :slight_smile:

Теперь о параметрах x и y, которые передаются в наш обработчик. Не запутайтесь в описаниях ниже - ориентируйтесь прежде всего на раздел Интерпретация.

Low MAC: события передачи кадров

X Y Описание
8 2 Вызывается каждый раз, когда программное обеспечение пытается передать кадр через esp_wifi_internal_tx() или esp_wifi_internal_tx_by_ref(), непосредственно перед помещением кадра в очередь передачи. Интерпретация: программное событие отправки WiFi кадра. Драйвер отправил ваш пакет в железо.
9 2 Вызывается из lmacTxFrame() непосредственно перед hal_mac_txq_enable(). Интерпретация: аппаратная передача WiFi началась
10 4 Вызывается функцией ppTask() сразу после lmacProcessCollisions_task(). Интерпретация: обнаружена коллизия WiFi
10 2 Вызывается из lmacProcessTxComplete() перед началом обработки. Интерпретация: аппаратная передача завершена
10 3 Вызывается из lmacProcessTxComplete() перед началом обработки. Интерпретация: то же самое - аппаратная передача завершена

Low MAC: приём кадров

X Y Описание
11 >1 Вызывается из IndicateFrame(). Буфер памяти успешно выделен. Указывает, что получен корректный WiFi-кадр и он передан в механизмы LMAC. Также означает, что кадр предназначен нам. Интерпретация: приём данных или получен корректный кадр
12 ?? Вызывается из IndicateFrame() перед DiscardFrame(), указывая на проблему с выделением памяти. Это событие возникает только при нехватке памяти и не является общим индикатором отброшенных пакетов. Интерпретация: кадр отброшен из-за нехватки памяти (критическая ситуация)

IEEE80211: высокоуровневые события

X Y Описание
13 2 Вызывается из ieee80211_hostap_send_beacon_process() непосредственно перед ic_tx_pkt() для передачи заранее подготовленного маяка (beacon). Происходит часто. Только в режиме AP или AP+STA. Интерпретация: передача маяка SoftAP, точка доступа работает
15 0 Начато сканирование
14 2 Сканирование выполняется, периодические вызовы
15 1 Сканирование завершено

Учитывая количество и плотность вызовов из модуля PowerManager, я начинаю подозревать, что весь этот механизм создавался прежде всего для отладки энергосбережения :slight_smile:

PowerManager

Процедуры пробуждения и сна

X Y Описание
17 0 Вызывается после pm_wake_up(). Интерпретация: начат процесс пробуждения
17 1 Вызывается перед pm_wake_done(). Интерпретация: процесс пробуждения завершён

Здесь Espressif немного усложняет картину.

Используется последовательность из трёх последовательных вызовов.

Гарантируется, что эта последовательность не будет перемешана с другой, поскольку все вызовы выполняются из контекста WiFi-задачи, которая сериализует доступ к WiFi API. (Один большой глобальный лок на всё. Напоминает сетевые стеки BSD-происхождения в RTEMS образца примерно 2005 года.)

Последовательность №1

X Y
3 1
4 1
5 0

Наблюдается в конце следующих функций:

  • pm_start()
  • pm_disconnected_start()
  • pm_disconnected_wake()

Интерпретация: какой-то процесс пробуждения (точное назначение неясно)

Последовательность №2

X Y
3 0
4 1
5 1

Наблюдается в:

  • pm_disconnected_sleep()
  • pm_disconnected_stop()

Интерпретация: какой-то процесс перехода в сон (точное назначение неясно)

Отслеживание изменений состояния внутреннего конечного автомата

Последовательность из двух вызовов выполняется каждый раз, когда модуль PM переходит из состояния old_state в состояние new_state.

Состояния нумеруются в диапазоне [0..5].

Знак «минус» означает обычное вычитание, поэтому значение аргумента X также лежит в диапазоне [0..5].

Гарантируется, что эта последовательность не будет прервана другой аналогичной последовательностью.

X Y
5 - old_state 1
5 - new_state 0

Интерпретация: PowerManager выполняет работу

Активность, связанная с маяками

X Y Описание
0 2 TBTT (Target Beacon Transmission Time). Интерпретация: аппаратное пробуждение для передачи или приёма маяков
1 2 Интерпретация: начало обработки маяка
2 2 Интерпретация: маяк не был получен в ожидаемое время

Понятно не до конца

X Y Описание
6 >1 Пока нет никаких идей
16 2 Возникает одновременно с документированным WiFi-событием 0x16 (см. документацию ESP-IDF по WiFi)

События подсистемы Coexist (RF-арбитр / планировщик BT, BLE и WiFi)

Эти события можно использовать для поиска проблем, связанных с совместной работой радиоподсистем.

X Y Описание
7 0 RF-арбитр работает и распределяет временные интервалы между BT и WiFi. Имеет смысл только когда обе подсистемы активны. Интерпретация: тик планировщика совместного использования радио
7 1 Что-то связанное с процедурой временного разделения радиоэфира. Может использоваться как индикатор того, что coexist_schm функционирует.

Минимальный пример для Arduino

#include <Arduino.h>

extern "C" {
  void wifi_set_gpio_debug_cb(void *handler);
};

static int wifi_hook(int x, int y) {
  esp_rom_printf("\r\nWIFI-GPIO-DEBUG: X=%d, Y=%d\r\n", x, y);
  return 0;
}

void setup() {

  Serial.begin(115200);

  wifi_set_gpio_debug_cb((void *)wifi_hook);

  // Инициализируйте WiFi здесь, иначе ваш обработчик никогда не будет вызван :)
}

void loop() {
  delay(1000);
}
2 лайка