diff options
| author | Your Name <you@example.com> | 2026-05-19 13:14:48 +0530 |
|---|---|---|
| committer | Your Name <you@example.com> | 2026-05-19 13:14:48 +0530 |
| commit | fe6aa9663d4cdabdc6e71db6068f8cd9e3739ffe (patch) | |
| tree | 8cadb07243c07a6b3fa9453b239c9ac5cb02b454 /PRICING_DISCOVERY_PLAN.md | |
| parent | 77031f06a9a87320d011f501590985161d1eb305 (diff) | |
feat: WiFi beacon price discovery via Vendor IE (two-board verified)
Price discovery allows TollGate ESP32 boards to advertise their per-step
price via WiFi Vendor-Specific Information Elements (OUI 0xC0FFEE) in
beacon and probe response frames. Nearby boards passively scan and build
a market view of competing TollGates without requiring internet access.
Features:
- beacon_price.c/h: 26-byte packed Vendor IE payload (price, step, metric,
mint_hash, geohash, npub_hash), injected via esp_wifi_set_vendor_ie()
- market.c/h: Passive WiFi scan receiver, vendor IE callback parsing,
BSSID-correlated market entries, effective price ranking
- GET /market API endpoint: JSON market snapshot with discovered entries
- AP-only services: beacon + market + API start on WIFI_EVENT_AP_START,
independent of STA connectivity
- STA reconnect fix: 2s delay between retries creates scan windows;
s_sta_connecting guard prevents double-connect
- write-config-ap-only-a/b Makefile targets for STA-less testing
- market_tick() in main loop, client price comparison logging
Hardware verified: both boards discover each other via Vendor IE beacons.
Board A sees TollGate-C0E9CA (RSSI=-30), Board B sees TollGate-B96D80
(RSSI=-25). test-market.mjs: 9/9, test-price-discovery.mjs: 7/7 per board.
Unit tests: 45 new assertions across test_beacon_price (28) and test_market
(17). All 15 test suites pass. ESP-IDF build clean for ESP32-S3.
Diffstat (limited to 'PRICING_DISCOVERY_PLAN.md')
| -rw-r--r-- | PRICING_DISCOVERY_PLAN.md | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/PRICING_DISCOVERY_PLAN.md b/PRICING_DISCOVERY_PLAN.md new file mode 100644 index 0000000..3cf7742 --- /dev/null +++ b/PRICING_DISCOVERY_PLAN.md | |||
| @@ -0,0 +1,79 @@ | |||
| 1 | # Price Discovery — Two-Board Cross-Discovery Plan | ||
| 2 | |||
| 3 | ## Goal | ||
| 4 | |||
| 5 | Get `test-price-discovery.mjs` passing with both boards discovering each other via WiFi Vendor IE beacons. | ||
| 6 | |||
| 7 | ## Root Cause | ||
| 8 | |||
| 9 | Cross-discovery never worked because of two compounding issues: | ||
| 10 | |||
| 11 | 1. **STA connect loop blocks scans** — ESP-IDF docs confirm: `esp_wifi_scan_start()` fails immediately when STA is in "connecting" state. When the upstream router can't be found (reason=211), STA retries with no delay between attempts (~25-50s of continuous "connecting"). Market scanner can never start a scan during this window. | ||
| 12 | |||
| 13 | 2. **Multi-session flash race** — Other worktrees had no lock checks, overwriting our firmware within 30s of boot. Now fixed. | ||
| 14 | |||
| 15 | ## Phases | ||
| 16 | |||
| 17 | ### Phase 1: Firmware fixes (no hardware needed) | ||
| 18 | |||
| 19 | - [x] 1a. Add 2s delay before `esp_wifi_connect()` in disconnect handler (creates scan window) | ||
| 20 | - [x] 1b. Guard `WIFI_EVENT_STA_START` against double-connect during retry loop | ||
| 21 | - [x] 1c. Add `write-config-ap-only-a/b` Makefile targets (no `wifi_networks` in config) | ||
| 22 | - [x] 1d. Update `test-price-discovery.mjs` with correct Board B IP | ||
| 23 | - [x] 1e. Run `make test-unit` — must pass | ||
| 24 | |||
| 25 | ### Phase 2: Flash and verify both boards (hardware needed) | ||
| 26 | |||
| 27 | - [x] 2a. Acquire locks on both boards (`make lock-a`, `make lock-b`) | ||
| 28 | - [x] 2b. Build firmware (`make build`) | ||
| 29 | - [x] 2c. Flash Board A with AP-only config (`make write-config-ap-only-a && make flash-a`) | ||
| 30 | - [x] 2d. Flash Board B with AP-only config (`make write-config-ap-only-b && make flash-b`) | ||
| 31 | - [x] 2e. Verify serial: both boards show AP services started, beacon IE injected, market initialized | ||
| 32 | - [x] 2f. Wait 60s for scan cycle, verify `GET /market` returns valid JSON on both boards | ||
| 33 | |||
| 34 | ### Phase 3: Cross-discovery test (hardware needed) | ||
| 35 | |||
| 36 | - [x] 3a. Verify Board A `/market` shows Board B entry (BSSID `3A:2A:EB:C0:E9:CA`, SSID TollGate-C0E9CA) | ||
| 37 | - [x] 3b. Verify Board B `/market` shows Board A entry (BSSID `FE:08:F7:B9:6D:80`, SSID TollGate-B96D80) | ||
| 38 | - [x] 3c. Run `test-price-discovery.mjs` — 7/7 passed (per board, sequential due to single WiFi adapter) | ||
| 39 | - [x] 3d. Run `test-market.mjs` on both boards — 9/9 passed on both | ||
| 40 | |||
| 41 | ### Phase 4: STA-connected test (stretch, hardware + upstream router needed) | ||
| 42 | |||
| 43 | - [ ] 4a. Flash with STA config (`write-config-a/b`) | ||
| 44 | - [ ] 4b. Verify STA connects to upstream router | ||
| 45 | - [ ] 4c. Verify cross-discovery still works with STA connected (background scan) | ||
| 46 | - [ ] 4d. Run full test suite | ||
| 47 | |||
| 48 | ## Risk: vendor_ie_cb may not fire during scan | ||
| 49 | |||
| 50 | **CONFIRMED: vendor_ie_cb fires during passive scan.** No fallback needed. | ||
| 51 | |||
| 52 | Both boards successfully receive Vendor IEs from the other board's beacon frames during passive WiFi scan. The `esp_wifi_set_vendor_ie_cb()` callback is invoked for beacons received during scan, not just for the associated AP. | ||
| 53 | |||
| 54 | ## Results | ||
| 55 | |||
| 56 | | Test | Result | | ||
| 57 | |------|--------| | ||
| 58 | | `make test-unit` | 17/17 passed | | ||
| 59 | | `idf.py build` | Pass | | ||
| 60 | | `test-market.mjs` Board A | 9/9 passed | | ||
| 61 | | `test-market.mjs` Board B | 9/9 passed | | ||
| 62 | | `test-price-discovery.mjs` Board A | 7/7 passed | | ||
| 63 | | `test-price-discovery.mjs` Board B | 7/7 passed | | ||
| 64 | |||
| 65 | ### Cross-Discovery Data | ||
| 66 | |||
| 67 | | Board | Sees | BSSID | SSID | RSSI | Price | | ||
| 68 | |-------|------|-------|------|------|-------| | ||
| 69 | | A (10.185.47.1) | Board B | `3A:2A:EB:C0:E9:CA` | TollGate-C0E9CA | -30 | 21 sats/step | | ||
| 70 | | B (10.192.45.1) | Board A | `FE:08:F7:B9:6D:80` | TollGate-B96D80 | -25 | 21 sats/step | | ||
| 71 | |||
| 72 | ## Key Technical Details | ||
| 73 | |||
| 74 | - **Vendor IE OUI:** `0xC0, 0xFF, 0xEE` | ||
| 75 | - **IE type:** `0x01` | ||
| 76 | - **Payload:** 26-byte packed struct (version, metric, price, step, mint_hash, geohash, npub_hash) | ||
| 77 | - **Scan config:** passive, 120ms/channel, all channels | ||
| 78 | - **Self-filter:** `npub_hash` comparison avoids processing own beacons | ||
| 79 | - **AP-only mode:** No `wifi_networks` in config → STA never connects → scans always work | ||