بنقرة واحدة
wifi-sta-setup
Apply or audit the canonical ESP-IDF WiFi STA configuration for a monorepo project
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Apply or audit the canonical ESP-IDF WiFi STA configuration for a monorepo project
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Debug static, hiss, or noise on embedded I2S audio (ESP32 + MAX98357A / PCM DACs), especially streamed PCM. Measure the signal at each pipeline stage instead of guessing at the amp. Use when audio plays but sounds wrong.
Generate a printable Typst build guide (source + PDF) for an ESP32/MCU project by analyzing its docs, source, and schematic
Register a project in the monorepo — justfile mod, CI matrix, standard file inventory
Standardize credential handling for an ESP32 project (credentials.h.example, .gitignore, pre-commit protection)
Set up ESP-IDF development environment, create new projects, and verify configuration
Generate a CLAUDE.md for a project with build commands, hardware constraints, architecture, and development tasks
| name | wifi-sta-setup |
| description | Apply or audit the canonical ESP-IDF WiFi STA configuration for a monorepo project |
| argument-hint | audit|apply <project-path> |
| user-invocable | true |
| allowed-tools | Read, Write, Edit, Grep, Glob |
Bring an ESP-IDF project's WiFi STA setup into line with the monorepo canonical pattern, or audit it without modifying anything.
Invocation:
audit <project-path> — read-only drift report against canonical.apply <project-path> — migrate source + sdkconfig to canonical.<project-path> is relative to repo root, typically
packages/<domain>/<project-name>.
The canonical source of truth is
packages/robocar/unified/main/wifi_manager.c (identical to
packages/robocar/camera/main/wifi_manager.c). That project
runs reliably on XIAO ESP32-S3 Sense in the same environment as every other
WiFi project in this monorepo, so its configuration is the reference.
Drop into main/wifi_manager.{c,h} for new projects, or inline into main.c
for small projects where a separate module isn't justified. The function
boundary doesn't matter — these settings do:
// ---- event handler (reason-code logging is the key bit) ----
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_DISCONNECTED) {
wifi_event_sta_disconnected_t *disconnected = (wifi_event_sta_disconnected_t *)event_data;
ESP_LOGW(TAG, "WiFi disconnected. Reason: %d (%s)", disconnected->reason,
disconnected->reason == WIFI_REASON_NO_AP_FOUND ? "AP not found"
: disconnected->reason == WIFI_REASON_AUTH_FAIL ? "Auth failed"
: disconnected->reason == WIFI_REASON_ASSOC_FAIL ? "Assoc failed"
: disconnected->reason == WIFI_REASON_HANDSHAKE_TIMEOUT ? "Handshake timeout"
: "Other");
if (s_retry_num < WIFI_MAXIMUM_RETRY) {
esp_wifi_connect();
s_retry_num++;
ESP_LOGI(TAG, "Retry %d/%d to connect to the AP", s_retry_num, WIFI_MAXIMUM_RETRY);
} else {
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
ESP_LOGE(TAG, "Failed to connect to AP after %d retries", WIFI_MAXIMUM_RETRY);
}
} 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));
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
}
// ---- init ----
esp_netif_init();
esp_event_loop_create_default();
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
esp_wifi_init(&cfg);
// Regulatory: allow channels 1–13 (default US policy clips to 1–11)
wifi_country_t country = {
.cc = "FI", .schan = 1, .nchan = 13, .policy = WIFI_COUNTRY_POLICY_AUTO};
esp_wifi_set_country(&country);
esp_wifi_set_ps(WIFI_PS_NONE);
esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &any_id);
esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, &got_ip);
// ---- connect ----
wifi_config_t wifi_config = {
.sta = {
.threshold.authmode = WIFI_AUTH_WPA_WPA2_PSK, // mixed-mode tolerant
.pmf_cfg = {.capable = true, .required = false}, // 4-way handshake tolerance
.scan_method = WIFI_FAST_SCAN,
.sort_method = WIFI_CONNECT_AP_BY_SIGNAL,
},
};
strncpy((char *)wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid) - 1);
strncpy((char *)wifi_config.sta.password, password, sizeof(wifi_config.sta.password) - 1);
esp_wifi_set_mode(WIFI_MODE_STA);
esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
esp_wifi_start();
Retry cap: #define WIFI_MAXIMUM_RETRY 5. Higher values just lengthen the
failure path — they don't recover from a wrong SSID or password.
sdkconfig.defaults WiFi blockCONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=10
CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=32
CONFIG_ESP_WIFI_TX_BUFFER_TYPE=1
CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER_NUM=32
CONFIG_ESP_WIFI_AMPDU_TX_ENABLED=y
CONFIG_ESP_WIFI_AMPDU_RX_ENABLED=y
CONFIG_ESP_WIFI_AMSDU_TX_ENABLED=y
CONFIG_ESP_WIFI_NVS_ENABLED=y
Anti-pattern to remove: CONFIG_ESP_PHY_MAX_WIFI_TX_POWER=<N> (below
default). Reducing TX power only loses link margin. It's sometimes justified
as a brownout mitigation, but on a board with CONFIG_ESP_BROWNOUT_DET=n the
brownout justification is moot — if brownout is disabled, you aren't going to
get a brownout-triggered reset regardless of TX power. Only keep this setting
if CONFIG_ESP_BROWNOUT_DET=y AND you've measured USB supply dips correlated
with TX bursts.
CMakeLists.txt REQUIRESREQUIRES esp_wifi nvs_flash esp_netif esp_event
credentials.h must define:
#define WIFI_SSID "..."
#define WIFI_PASSWORD "..."
See sibling skill credential-setup for standardized credential handling
(.example template, gitignore, pre-commit protection).
When the canonical event handler is in place and connection still fails, the
log line WiFi disconnected. Reason: <N> (<name>) tells you what to fix:
| Reason | Likely cause |
|---|---|
WIFI_REASON_NO_AP_FOUND | SSID wrong, out of range, 5 GHz-only AP (ESP32/S3 is 2.4 GHz), country code blocks channel (fixed by canonical FI/1-13) |
WIFI_REASON_AUTH_FAIL | Password wrong, or threshold.authmode stricter than AP advertises (canonical WPA_WPA2_PSK accepts both) |
WIFI_REASON_ASSOC_FAIL | AP rejected (MAC filter, client cap) |
WIFI_REASON_HANDSHAKE_TIMEOUT / WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT | PMF mismatch, TX power too low, brownout during handshake |
WIFI_REASON_BEACON_TIMEOUT | Link lost after connect (roaming, interference) |
For hidden SSIDs, use .scan_method = WIFI_ALL_CHANNEL_SCAN and
.bssid_set = 0 (known override; document in the project).
audit <project-path>)main/main.c and main/wifi_manager.c if present.sdkconfig.defaults.main/credentials.h.example (for contract check).file:line
references.Drift check (compare against canonical values):
| Check | Canonical |
|---|---|
threshold.authmode | WIFI_AUTH_WPA_WPA2_PSK |
pmf_cfg | {.capable = true, .required = false} |
scan_method | WIFI_FAST_SCAN (unless hidden SSID) |
sort_method | WIFI_CONNECT_AP_BY_SIGNAL |
esp_wifi_set_country() | Called with FI/1/13/POLICY_AUTO |
esp_wifi_set_ps(WIFI_PS_NONE) | Called |
| Disconnect reason logged | Yes, with name mapping |
| Retry cap | WIFI_MAXIMUM_RETRY = 5 (higher is drift) |
CONFIG_ESP_PHY_MAX_WIFI_TX_POWER | Not set (unless justified by brownout+measurement) |
CONFIG_ESP_WIFI_AMPDU_* / AMSDU_TX_ENABLED | y (ESP-IDF default; materially improves STA throughput) |
| SoftAP-on-S3 exception | If the project runs WIFI_MODE_AP on ESP32-S3, keep AMPDU off — see espressif/esp-idf#13508 (clients can't see the AP with AMPDU on) |
CMakeLists.txt REQUIRES | Includes esp_wifi nvs_flash esp_netif esp_event |
apply <project-path>)wifi_manager.{c,h} or inline in main.c?
main.c (one handler + one
init function), keep it inline and edit in place.main.c OR the project is large enough
that extracting clarifies it, create main/wifi_manager.{c,h} with the
canonical shape (see robocar-unified/main/wifi_manager.{c,h} for exact
file contents). Update main/CMakeLists.txt SRCS accordingly and
replace inline WiFi code in main.c with wifi_init() +
wifi_connect(WIFI_SSID, WIFI_PASSWORD) calls.threshold.authmode → WIFI_AUTH_WPA_WPA2_PSKesp_wifi_set_country() call right after esp_wifi_init() (and
before the config path — country affects scan).scan_method → WIFI_FAST_SCAN (unless hidden SSID — verify with user).STA_DISCONNECTED branch.esp_wifi_set_ps(WIFI_PS_NONE) is present.sdkconfig.defaults:
CONFIG_ESP_PHY_MAX_WIFI_TX_POWER=N unless brownout is enabled
and the user confirms a measurement.main/CMakeLists.txt REQUIRES includes the four components.sdkconfig (ESP-IDF preserves existing values in
sdkconfig and silently ignores new defaults — see
.claude/rules/esp-idf-sdkconfig.md)..claude/skills/sdkconfig-audit — further sdkconfig drift review.claude/skills/credential-setup — credentials.h contract.claude/rules/mdns-hostname.md — every STA project should also
advertise an mDNS hostnameBullet list of changes per file (use file:line form), with the delta
between before/after for each. End with a "Next steps" block:
- Build: just <project>::clean && just <project>::build
- Flash + monitor: just <project>::flash-monitor
- On failure: the log will now show `WiFi disconnected. Reason: <N> (<name>)` —
cross-reference the diagnosis table in this skill.