Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
60e5573
Add ESP32 prepared send integration scaffold
NikolayChirkov Jun 29, 2026
c9a3cf8
Fetch aether-client-cpp via CPM on prepared-packet-v0 branch.
NikolayChirkov Jun 30, 2026
4ac8b9c
Cursor work.
dmitriyk-aethernet Jul 1, 2026
dd9ef03
Merge branch 'thermometer-prepared-send-v0' of https://github.com/aet…
dmitriyk-aethernet Jul 2, 2026
9623f33
Updating PreparedSend functionality.
dmitriyk-aethernet Jul 2, 2026
2bd8214
Test.
dmitriyk-aethernet Jul 3, 2026
3680297
Removing Test.
dmitriyk-aethernet Jul 3, 2026
951d56f
Changed temperature format from std:uint16_t to std::string and added…
dmitriyk-aethernet Jul 3, 2026
1bcd505
Fixing WiFi connection.
dmitriyk-aethernet Jul 9, 2026
3a82d18
Removing UAP.
dmitriyk-aethernet Jul 10, 2026
31617dc
Add optimizations.
dmitriyk-aethernet Jul 20, 2026
a095e4e
Some Wi-Fi optimization has been added.
dmitriyk-aethernet Jul 21, 2026
56eefb8
Fixing restoring BSSID + Channel.
dmitriyk-aethernet Jul 22, 2026
0eda112
Fixing mikrotik association.
dmitriyk-aethernet Jul 23, 2026
85bd81d
Changing the initialization method of the adress_is_valid and bs_is_v…
dmitriyk-aethernet Jul 24, 2026
865b068
update to adopt/prepared-packet-v0
BartolomeyKant Aug 26, 2026
af35277
Add Wi-Fi lifecycle bench and ignore-BSSID control experiment.
NikolayChirkov Aug 28, 2026
bfb86c0
Pin Aether BSSID-cache removal and record ESP32-C6 verify runs.
NikolayChirkov Aug 28, 2026
8b887ce
Align desktop CPM aether pin with BSSID-removal SHA.
NikolayChirkov Aug 28, 2026
cf33566
Add no-sleep prepared-message E2E bench and hardware results.
NikolayChirkov Aug 28, 2026
669ccdc
Fix prepared Wi-Fi cache and add silent 5x20 E2E experiment.
NikolayChirkov Aug 28, 2026
f2045c8
Add gateway MAC cache and static ARP on prepared UDP path.
NikolayChirkov Aug 28, 2026
631bc2f
Add keep-Wi-Fi-up prepared 5x20 A/B and report 96/100 delivery.
NikolayChirkov Aug 28, 2026
14d1bb7
Add silent Wi-Fi single-factor prepared bisect and 13x20 results.
NikolayChirkov Aug 29, 2026
adf5619
Add silent fastest-path prepared Wi-Fi campaign and 710ms winner report.
NikolayChirkov Aug 29, 2026
2c50b1f
Add late TX-done callback prepared path and WPA2 430ms VAL200 report.
NikolayChirkov Aug 29, 2026
40a0e9b
Validate short PRE=25 callback path: N50 screen and 230ms VAL200.
NikolayChirkov Aug 29, 2026
5b878e1
Add deep-sleep 5x50 prepared E2E with RTC cache and 250ms HOT median.
NikolayChirkov Aug 29, 2026
724a173
Fix TX-done FULL loop: enable early entry and report FullReason.
NikolayChirkov Aug 29, 2026
e7f4804
Record pushed SHA in TXD3 FULL-loop diagnostic report.
NikolayChirkov Aug 29, 2026
5f318b9
Add MAC retry-limit diagnostic campaign and report.
NikolayChirkov Aug 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
sdkconfig*
!sdkconfig.defaults

!sdkconfig.defaults.*

Empty file removed .gitmodules
Empty file.
65 changes: 65 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,71 @@ if (ESP_PLATFORM OR IDF_TARGET)
set(CM_PLATFORM "ESP32")
endif()

# Bench / silent measurement sdkconfig. Must be set before project().
set(AE_EXP_PREPARED_MESSAGE_E2E "" CACHE STRING
"No-sleep prepared-message E2E bench (set to 1)")
set(AE_EXP_PREPARED_WIFI_CACHE_5X20 "" CACHE STRING
"Silent 5x20 prepared Wi-Fi cache bench (set to 1)")
set(AE_EXP_PREPARED_KEEP_WIFI_UP_5X20 "" CACHE STRING
"Silent 5x20 keep-Wi-Fi-up prepared bench (set to 1)")
set(AE_EXP_PREPARED_WIFI_BISECT "" CACHE STRING
"Silent single-factor prepared Wi-Fi bisect (set to 1)")
set(AE_EXP_PREPARED_WIFI_FASTEST "" CACHE STRING
"Silent fastest-path prepared Wi-Fi campaign (set to 1)")
set(AE_EXP_PREPARED_DEEPSLEEP_5X50 "" CACHE STRING
"Silent deep-sleep 5x50 prepared E2E (set to 1)")
set(AE_EXP_PREPARED_TX_DONE_DIAG "" CACHE STRING
"Silent TX-done callback diagnostic 1x50 (set to 1)")
set(AE_EXP_PREPARED_MAC_RETRY_DIAG "" CACHE STRING
"Silent MAC retry-limit diagnostic 7x50 (set to 1)")
set(AE_EXP_TX_DIAG_MODE "" CACHE STRING
"TX-done diag wait mode: 0=FIRST_ANY 1=FIRST_SUCCESS")
set(AE_EXP_FAST_DISABLE_WPA3 "" CACHE STRING
"Benchmark-only: disable CONFIG_ESP_WIFI_ENABLE_WPA3_SAE (set to 1)")
set(AE_EXP_BISECT_CONSOLE "" CACHE STRING
"Bisect USB stage markers / console (set to 1; disables silent)")
set(AE_EXP_BISECT_SMOKE "" CACHE STRING
"Bisect smoke: B1 only, 2 prepared (set to 1)")
if(AE_EXP_BISECT_CONSOLE STREQUAL "1" AND
AE_EXP_PREPARED_WIFI_BISECT STREQUAL "1")
list(APPEND SDKCONFIG_DEFAULTS
"${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults.bench")
elseif(AE_EXP_PREPARED_DEEPSLEEP_5X50 STREQUAL "1")
list(APPEND SDKCONFIG_DEFAULTS
"${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults.silent"
"${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults.fastest"
"${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults.wpa2only"
"${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults.deepsleep_5x50")
elseif(AE_EXP_PREPARED_TX_DONE_DIAG STREQUAL "1")
list(APPEND SDKCONFIG_DEFAULTS
"${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults.silent"
"${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults.fastest"
"${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults.wpa2only"
"${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults.deepsleep_5x50")
elseif(AE_EXP_PREPARED_MAC_RETRY_DIAG STREQUAL "1")
list(APPEND SDKCONFIG_DEFAULTS
"${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults.silent"
"${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults.fastest"
"${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults.wpa2only"
"${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults.deepsleep_5x50")
elseif(AE_EXP_PREPARED_WIFI_FASTEST STREQUAL "1")
list(APPEND SDKCONFIG_DEFAULTS
"${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults.silent"
"${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults.fastest")
if(AE_EXP_FAST_DISABLE_WPA3 STREQUAL "1")
list(APPEND SDKCONFIG_DEFAULTS
"${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults.wpa2only")
endif()
elseif(AE_EXP_PREPARED_WIFI_CACHE_5X20 STREQUAL "1" OR
AE_EXP_PREPARED_KEEP_WIFI_UP_5X20 STREQUAL "1" OR
AE_EXP_PREPARED_WIFI_BISECT STREQUAL "1")
list(APPEND SDKCONFIG_DEFAULTS
"${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults.silent")
elseif(AE_EXP_PREPARED_MESSAGE_E2E STREQUAL "1")
list(APPEND SDKCONFIG_DEFAULTS
"${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults.bench")
endif()

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

Expand Down
5 changes: 1 addition & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
## Build Instructions
### Init dependencies
It uses git submodules to manage dependencies.
```
git submodule update --init --remote ./aether-client-cpp
```
`aether-client-cpp` is fetched automatically by [CPM.cmake](https://github.com/cpm-cmake/CPM.cmake) during the CMake configure step (branch `prepared-packet-v0`).

### For desktop
For desktop a regular cmake project is used.
Expand Down
139 changes: 139 additions & 0 deletions experiments/PREPARED_DEEPSLEEP_5X50_REPORT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# Prepared deep-sleep 5×50 E2E report

Experiment only. Production `SendPreparedOnce` was **not** switched.

## Pins

| Repo | Branch | SHA |
|------|--------|-----|
| temperature-sensor | `thermometer-prepared-send-v0` | *(this commit)* |
| aether-client-cpp | `exp/esp32c6-wifi-lifecycle-diag` | `157aadbec8e7b852d0f89274307ff7cb8103e5f7` **unchanged** |

## Effective sdkconfig (verified before flash)

```
CONFIG_RTC_CLK_SRC_EXT_CRYS=y
# CONFIG_RTC_CLK_SRC_INT_RC is not set
CONFIG_RTC_CLK_CAL_CYCLES=1024
CONFIG_ESP_BROWNOUT_DET=y
CONFIG_ESP_BROWNOUT_DET_LVL_SEL_7=y (from base defaults)
# CONFIG_ESP_WIFI_ENABLE_WPA3_SAE is not set
CONFIG_ESP_CONSOLE_NONE=y
CONFIG_LOG_DEFAULT_LEVEL_NONE=y
CONFIG_BOOTLOADER_LOG_LEVEL_NONE=y
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160=y
# CONFIG_PM_ENABLE is not set
CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP=y
```

## Fast Wi-Fi / HOT path

- WPA2-PSK, negotiated authmode=3 on all recovered HOT samples
- Cached channel; **no** BSSID reconnect
- Static IPv4 / netmask / gateway + cached GW MAC + static ARP (RTC `PreparedWifiRtcCache`)
- Wi-Fi 4 (b/g/n), auto PHY, `WIFI_PS_NONE`, max TX, retry=10
- PRE=25 ms, POST=0, late TX-done callback immediately before `sendto`, socket held until callback
- Safety callback wait ≤100 ms; no fingerprint matching

## RTC state

- `RtcState` (magic `DS3P`) + CRC: phase, outer 1..5, hot 1..50, attempts, pending measurement, brownout/unexpected counters, sleep arm timestamp
- `PreparedWifiRtcCache` (magic `WCF1`) + CRC: channel, IP, netmask, gateway, gw_mac (BSSID diagnostic only)
- Prepared nonce block remains `RTC_NOINIT` `PreparedSendMessageBlock` (existing aether layout)
- Early `app_main` hook: `ExperimentEarlyAppEntry()` before WDT/`setup`

## Campaign shape

- Client id: `prepared_deepsleep_5x50_v1`
- 5 measured FULL Æther cycles
- Per FULL: `PrepareSendMessageBlock(50)` → 50 deep-sleep HOT wakes (3 s) → next FULL
- Target: 250 prepared `sendto`; FINAL Aether report after last HOT

## Delivery / lifecycle

| Item | Result |
|------|--------|
| FULL messages recovered | **5/5** |
| HOT measurement records | **249/250** (outer 5 missing last pending = HOT#50 flush via FINAL) |
| FINAL message | **not received** (FINAL Aether phase stuck/retried; campaign timed out) |
| callback_seen (HOT) | **246** |
| callback_timeout (HOT) | **3** |
| authmode=3 | **249/249** HOT |
| brownout boots | **0** |
| unexpected resets (payload) | **0** |

Prepared `sendto` lifecycle on device is independent of UDP delivery; UDP losses are expected.

## FULL USER CYCLE (n=5)

Raw (ms): 12670, 3640, 4310, 3600, 3620

| | ms |
|--|-----|
| min | 3600 |
| **median** | **3640** |
| p90 | 12670 |
| max | 12670 |

(First FULL includes colder registration-adjacent work; later FULLs ~3.6–4.3 s.)

## HOT USER CYCLE (n=249)

| | ms |
|--|-----|
| min | ~(from samples) |
| **median** | **250** |
| p90 | 320 |
| p99 | 1090 |
| max | 1370 |

## HOT WIFI CYCLE (n=249)

| | ms |
|--|-----|
| **median** | **239** |
| p90 | 309 |
| p99 | 1079 |
| max | 1359 |

## CONNECT / TX-DONE / TEARDOWN (HOT)

| | med ms | p90 ms | max ms |
|--|--------|--------|--------|
| connect | 133 | 188 | 1293 |
| tx-done wait | 11 | 23 | 100 |
| teardown | 77 | 97 | 967 |

## SLEEP → APP overhead

`sleep_elapsed_to_app_us` median ≈ **3040.7 ms** (requested 3000 ms)

| | med ms | p90 | p99 | max |
|--|--------|------|-----|-----|
| sleep_to_app_overhead | **40.7** | 40.8 | 40.8 | 40.9 |
| app_entry_esp_timer_us | **5424** | 5424 | — | 5425 |

## Brownout / reset

| | count |
|--|-------|
| ESP_RST_BROWNOUT boots (payload flag) | 0 |
| unexpected_reset_count in payloads | 0 |
| recovery FULL (not observed in TSV) | 0 |

## Confirmation

- Five FULL blocks: **yes** (records for outer 1..5)
- 50 prepared nonces per block: **yes** (HOT counts 50/50/50/50/49 recovered; last pending needs FINAL)
- Total prepared sends targeted 250: device completed through HOT#46–50 of outer 5; **249** timing records recovered via pending chain

## Artifacts

- `experiments/prepared_deepsleep_5x50.tsv`
- `experiments/run_deepsleep_5x50.py`
- `main/prepared_deepsleep_5x50_bench.cpp`
- `sdkconfig.defaults.deepsleep_5x50`

## Note on FINAL

FINAL Æther write after HOT#50 outer 5 did not reach the receiver before the orchestrator timeout. A fail-counter fallback to DONE was added for subsequent runs. Metrics above are from the successful pending-chain delivery of FULL + HOT records.
103 changes: 103 additions & 0 deletions experiments/PREPARED_KEEP_WIFI_UP_REPORT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Prepared KEEP-WIFI-UP 5×20 (ESP32-C6, silent)

Hardware: ESP32-C6, ESP-IDF v6.0.2, COM7
Aether: `exp/esp32c6-wifi-lifecycle-diag` @ `157aadbec8e7b852d0f89274307ff7cb8103e5f7` (**unchanged**)
Firmware: `AE_EXP_PREPARED_KEEP_WIFI_UP_5X20=1`, silent console/log NONE
Client: `prepared_keep_wifi_up_5x20_v1`
Receiver UID: `5aade50f-00d9-4624-b097-e203cdcf1e38`
Block reserve: **20** (unchanged prepared packet path)

## Hypothesis

Losses on prepared sends may be caused by **full Wi-Fi teardown + reassociation after every prepared message**.

## A/B change (bench-only)

Production `SendPreparedOnce()` unchanged.

New session API:

- `BeginPreparedWifiSession()` — init, associate, static IP/ARP once; leave Wi-Fi up
- `SendPreparedPacketOnActiveWifi()` — EncodePacket → socket → sendto → close (no stop/deinit/reconnect)
- `EndPreparedWifiSession()` — stop/deinit/cleanup after message #20

Per outer cycle: FULL → PrepareSendMessageBlock(20) → capture Wi-Fi cache → release FULL Æther → Begin session → 20 prepared sends (1 s gap, **no** post-send hold) → End session.

Socket is still create/close **per message** (extra socket A/B not required).

## previous

| Experiment | Delivery |
|---|---|
| static ARP + teardown each send | **29/100** |
| hold 300→600 ms (control) | **6/20 ≈ 30%** |

## new

| Experiment | Delivery |
|---|---|
| keep Wi-Fi up (5×20) | **96/100** |

### DELIVERY detail

```
full=6/5
prepared=96/100
final=1/1
missing=4
duplicates=1
out_of_order=0
```

Cache flags on recovered prepared: BSSID/IP/DHCP-skip/static-ARP hits=99 (payload flag path active).

## wifi_session_start

Measured once per outer cycle (init → association → static IP/ARP ready). Carried on PREPARED#1 as `previous_full_us`. Only 2/5 first-of-burst messages recovered, so n=2:

```
raw=[254249, 279549]
min=254249
median=279549
max=279549
n=2
```

(~0.25–0.28 s)

## prepared send (encode + socket + sendto + close only)

No 300 ms post-send hold in this mode.

### FIRST PREPARED (send-only)

```
raw=[2467, 2415, 2418, 3785, 2413]
min=2413
median=2418
max=3785
n=5
```

### ALL PREPARED (send-only)

```
n=99
min=2180
median=2600
p90=2710
p99=3785
max=3785
```

(~2.6 ms median)

## Verdict

```
LOSS LOCATION = WIFI RECONNECT/TEARDOWN
```

KEEP-WIFI-UP reaches **96/100** (criterion ~95–100/100). Packet encoding / server / nonce / UDP path is adequate when Wi-Fi stays associated; prior ~30% delivery tracks full teardown/reassociation between each prepared send.

Do **not** chase further Wi-Fi micro-optimizations (ARP, hold) for this loss class — next product work is avoiding per-message Wi-Fi lifecycle, not changing prepared packets.
Loading