Files
ruvnet--RuView/firmware/esp32-csi-node/main/c6_twt.h
T
ruv f23e34ee5c feat(firmware): ESP32-C6 target — Wi-Fi 6 / 802.15.4 / TWT / LP-core (ADR-110)
`firmware/esp32-csi-node` now builds for both `esp32s3` (existing
production) and `esp32c6` (new research / battery-seed target) from
the same source tree. ESP-IDF auto-applies `sdkconfig.defaults.esp32c6`
when the target is set to esp32c6; every C6 module is gated on
CONFIG_IDF_TARGET_ESP32C6 (or the SOC_WIFI_HE_SUPPORT capability) so
the S3 build path is byte-identical to today.

New modules (all #ifdef-gated, no-op stubs on S3):
- c6_twt.{h,c}      — iTWT wrapper, graceful AP-NACK fallback
- c6_timesync.{h,c} — 802.15.4 beacon-based mesh time-sync, EUI-64
                      leader election, c6_timesync_get_epoch_us()
- c6_lp_core.{h,c}  — wake-on-motion deep-sleep helper (ext1 path
                      this cut; real LP-core polling deferred)

ADR-018 frame extension:
- byte 18: PPDU type (0=HT/legacy, 1=HE-SU, 2=HE-MU, 3=HE-TB)
- byte 19: bandwidth + STBC + 802.15.4-sync-valid flags
- Magic 0xC5110001 unchanged — backwards compatible
- Dual-branch encoding handles both struct variants of
  wifi_pkt_rx_ctrl_t (legacy S3 / HE C6) per CONFIG_SOC_WIFI_HE_SUPPORT

Critical bug fixed during live witness collection (verified across 3
boards on COM6/COM9/COM12):
- c6_timesync.c read MAC into a 6-byte buffer and ran MAC-48->EUI-64
  conversion. But esp_read_mac(ESP_MAC_IEEE802154) returns 8 bytes
  already in EUI-64 form on C6 — code was double-inserting FFFE.
  Boot log was 206ef1fffefffe17, fix yields 206ef1fffe17278c which
  matches esptool's eFuse reading exactly.

Tooling:
- CI workflow (firmware-ci.yml) extended with c6-4mb matrix row +
  ADR-110 host-unit-test step
- Host unit tests for pure functions (mac48_to_eui64,
  eui64_bytes_to_u64, PPDU encoding both branches) — runs on Ubuntu CI
- Multi-board live-capture harness (test/capture-3board-experiment.py)
- Witness bundle script records SHA-256s for s3-adr110, c6-adr110, and
  s3-fair-adr110 (apples-to-apples) binary archives

Honest empirical findings (full report in docs/WITNESS-LOG-110.md):
- Verified live on 3 C6 boards: boot, 802.15.4 init w/ correct EUIs,
  WiFi STA reaching assoc->run on ruv.net, TWT setup attempted +
  gracefully NACKed (AP is 11n-only, TWT Responder:0), HE-MAC firmware
  loaded
- NOT verified (need 11ax AP / second-channel exp / INA meter):
  HE-LTF subcarrier expansion, TWT cadence determinism, ±100 µs sync
  alignment, 5 µA hibernation
- Bug found: leader election doesn't step down under live WiFi load —
  likely 2.4 GHz radio coex preemption (WiFi ch 5 vs 15.4 ch 15);
  follow-up task #30
- Apples-to-apples size: S3-no-display = 886 KB, C6 = 1003 KB
  (C6 is 13% LARGER for equivalent CSI features; the extra is the
  802.15.4 + OpenThread stack that S3 lacks)

Tracking: ruvnet/RuView#762

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-22 20:10:30 -04:00

76 lines
2.3 KiB
C

/**
* @file c6_twt.h
* @brief ESP32-C6 TWT (Target Wake Time) helper — ADR-110 Phase 3.
*
* Wraps esp_wifi_sta_itwt_setup() to negotiate a deterministic wake slot
* with the AP, replacing today's opportunistic CSI capture cadence with
* a scheduler-bounded one.
*
* Only built when CONFIG_IDF_TARGET_ESP32C6 is set — the S3 radio is
* 802.11n only and cannot speak iTWT.
*
* Usage from main.c (after WiFi STA is connected):
* c6_twt_setup_default(); // honors CONFIG_C6_TWT_WAKE_INTERVAL_US
*
* Graceful failure: if the AP rejects (no 11ax support, doesn't allow
* iTWT, or returns a NACK), the helper logs and returns ESP_OK — the
* device keeps doing opportunistic CSI just like the S3.
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "soc/soc_caps.h"
#if defined(CONFIG_IDF_TARGET_ESP32C6) && SOC_WIFI_HE_SUPPORT
#include "esp_err.h"
#include <stdint.h>
#include <stdbool.h>
/**
* Set up an individual TWT agreement using the Kconfig defaults
* (CONFIG_C6_TWT_WAKE_INTERVAL_US, CONFIG_C6_TWT_MIN_WAKE_DURA_US).
*
* @return ESP_OK whether or not the AP accepted — the helper never
* propagates a TWT NACK as an error to the caller.
*/
esp_err_t c6_twt_setup_default(void);
/**
* Set up an individual TWT agreement with explicit parameters.
*
* @param wake_interval_us Period between wake events.
* @param min_wake_dura_us Minimum awake duration per wake (≥256 µs).
* @return ESP_OK on success or graceful NACK; ESP_FAIL on local error.
*/
esp_err_t c6_twt_setup(uint32_t wake_interval_us, uint32_t min_wake_dura_us);
/**
* Tear down any active TWT agreement. Safe to call when none is active.
* Should be invoked on WIFI_EVENT_STA_DISCONNECTED so the AP scheduler
* doesn't keep a dead slot reserved.
*/
void c6_twt_teardown(void);
/**
* Returns true if a TWT agreement is currently active.
*/
bool c6_twt_is_active(void);
#else /* not C6 with iTWT support — provide stubs so call sites compile */
static inline esp_err_t c6_twt_setup_default(void) { return ESP_OK; }
static inline esp_err_t c6_twt_setup(uint32_t a, uint32_t b) { (void)a; (void)b; return ESP_OK; }
static inline void c6_twt_teardown(void) { }
static inline bool c6_twt_is_active(void) { return false; }
#endif /* CONFIG_IDF_TARGET_ESP32C6 && SOC_WIFI_HE_SUPPORT */
#ifdef __cplusplus
}
#endif