Skip to content

Commit 2b8a7cc

Browse files
authored
feat: happiness scoring pipeline + ESP32 swarm with Cognitum Seed (#285)
* feat: happiness scoring pipeline with ESP32 swarm + Cognitum Seed coordinator ADR-065: Hotel guest happiness scoring from WiFi CSI physiological proxies. ADR-066: ESP32 swarm with Cognitum Seed as coordinator for multi-zone analytics. Firmware: - swarm_bridge.c/h: FreeRTOS task on Core 0, HTTP client with Bearer auth, registers with Seed, sends heartbeats (30s) and happiness vectors (5s) - nvs_config: seed_url, seed_token, zone_name, swarm intervals - provision.py: --seed-url, --seed-token, --zone CLI args - esp32-hello-world: capability discovery firmware for 4MB ESP32-S3 variant WASM edge modules: - exo_happiness_score.rs: 8-dim happiness vector from gait speed, stride regularity, movement fluidity, breathing calm, posture, dwell time (events 690-694, 11 tests, ESP32-optimized buffers + event decimation) - ghost_hunter.rs standalone binary: 5.7 KB WASM, feature-gated default pipeline RuView Live: - --mode happiness dashboard with bar visualization - --seed flag for Cognitum Seed bridge (urllib, background POST) - HappinessScorer + SeedBridge classes (stdlib only, no deps) Examples: - seed_query.py: CLI tool (status, search, witness, monitor, report) - provision_swarm.sh: batch provisioning for multi-node deployment - happiness_vector_schema.json: 8-dim vector format documentation Verified live: ESP32 on COM5 (4MB flash) registered with Seed at 10.1.10.236, vectors flowing, witness chain growing (epoch 455, chain 1108). Co-Authored-By: claude-flow <ruv@ruv.net> * ci: raise firmware binary size gate to 1100 KB for HTTP client stack The swarm bridge (ADR-066) adds esp_http_client for Seed communication, which pulls in the HTTP/TLS stack (~150 KB). Binary grew from ~978 KB to ~1077 KB. Raise the gate from 950 KB to 1100 KB. Still fits comfortably in both 4MB (1856 KB OTA slot, 43% free) and 8MB flash variants. Co-Authored-By: claude-flow <ruv@ruv.net>
1 parent 8a84748 commit 2b8a7cc

22 files changed

Lines changed: 3072 additions & 40 deletions

File tree

.github/workflows/firmware-ci.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,16 @@ jobs:
2727
idf.py set-target esp32s3
2828
idf.py build
2929
30-
- name: Verify binary size (< 950 KB gate)
30+
- name: Verify binary size (< 1100 KB gate)
3131
working-directory: firmware/esp32-csi-node
3232
run: |
3333
BIN=build/esp32-csi-node.bin
3434
SIZE=$(stat -c%s "$BIN")
35-
MAX=$((950 * 1024))
35+
MAX=$((1100 * 1024))
3636
echo "Binary size: $SIZE bytes ($(( SIZE / 1024 )) KB)"
37-
echo "Size limit: $MAX bytes (950 KB — includes Tier 3 WASM runtime)"
37+
echo "Size limit: $MAX bytes (1100 KB — includes WASM runtime + HTTP client for Seed swarm bridge)"
3838
if [ "$SIZE" -gt "$MAX" ]; then
39-
echo "::error::Firmware binary exceeds 950 KB size gate ($SIZE > $MAX)"
39+
echo "::error::Firmware binary exceeds 1100 KB size gate ($SIZE > $MAX)"
4040
exit 1
4141
fi
4242
echo "Binary size OK: $SIZE <= $MAX"
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
# ADR-065: Hotel Guest Happiness Scoring -- WiFi CSI + Cognitum Seed Bridge
2+
3+
**Status:** Proposed
4+
**Date:** 2026-03-20
5+
**Deciders:** @ruvnet
6+
**Related:** ADR-040 (WASM edge modules), ADR-039 (edge intelligence), ADR-042 (CHCI), ADR-064 (multimodal ambient intelligence), ADR-060 (multi-node aggregation)
7+
8+
## Context
9+
10+
Hotels lack objective, privacy-preserving methods to measure guest satisfaction in real time. Current approaches (post-stay surveys, NPS scores) are delayed, biased toward extremes, and capture less than 10% of guests. Meanwhile, ambient RF sensing can infer behavioral cues that correlate with comfort and well-being -- without cameras, wearables, or any guest interaction.
11+
12+
### Hardware
13+
14+
Two ESP32-S3 variants are deployed:
15+
16+
| Device | Flash | PSRAM | MAC | Port | Notes |
17+
|--------|-------|-------|-----|------|-------|
18+
| ESP32-S3 (QFN56 rev 0.2) | 4 MB | 2 MB | 1C:DB:D4:83:D2:40 | COM5 | Budget node, uses `sdkconfig.defaults.4mb` + `partitions_4mb.csv` |
19+
| ESP32-S3 | 8 MB | 8 MB | -- | COM7 | Full-featured node, existing deployment |
20+
21+
Both run the Tier 2 DSP firmware with presence detection, vitals extraction, fall detection, and gait analysis.
22+
23+
### Cognitum Seed Device
24+
25+
A Cognitum Seed unit is deployed on the same network segment:
26+
27+
- **Address:** 169.254.42.1 (link-local)
28+
- **Hardware:** Raspberry Pi Zero 2 W
29+
- **Firmware:** 0.7.0
30+
- **Vector store:** 398 vectors, dim=8
31+
- **API endpoints:** 98 (REST, fully documented)
32+
- **Sensors:** PIR, reed switch (door), vibration, ADS1115 ADC (4-ch analog), BME280 (temp/humidity/pressure)
33+
- **Security:** Ed25519 custody chain with tamper-evident witness log
34+
35+
The Seed's 8-dimensional vector store and drift detection engine make it a natural aggregation point for behavioral feature vectors extracted from CSI data.
36+
37+
### Existing WASM Edge Modules
38+
39+
The following modules already run on-device and produce features relevant to happiness scoring:
40+
41+
| Module | Event IDs | Outputs |
42+
|--------|-----------|---------|
43+
| `exo_emotion_detect.rs` | 610-613 | Arousal level, stress index |
44+
| `med_gait_analysis.rs` | 130-134 | Cadence, stride length, regularity |
45+
| `ret_customer_flow.rs` | 410-413 | Entry/exit count, direction |
46+
| `ret_dwell_heatmap.rs` | 420-423 | Dwell time per zone |
47+
48+
## Decision
49+
50+
### 1. New WASM Module: `exo_happiness_score.rs`
51+
52+
Create a new WASM edge module that fuses outputs from existing modules into an 8-dimensional happiness vector, matching the Seed's vector dimensionality (dim=8).
53+
54+
**Event ID registry (690-694):**
55+
56+
| Event ID | Name | Description |
57+
|----------|------|-------------|
58+
| 690 | `HAPPINESS_VECTOR` | Full 8-dim happiness vector emitted per scoring window |
59+
| 691 | `HAPPINESS_TREND` | Windowed trend (rising/falling/stable) over last N vectors |
60+
| 692 | `HAPPINESS_ALERT` | Score crossed a configured threshold (low satisfaction) |
61+
| 693 | `HAPPINESS_GROUP` | Aggregate score for multi-person zone |
62+
| 694 | `HAPPINESS_CALIBRATION` | Baseline recalibration event (new guest check-in) |
63+
64+
### 2. Happiness Vector Schema (8 Dimensions)
65+
66+
Each dimension is normalized to [0.0, 1.0] where 1.0 = maximal positive signal:
67+
68+
| Dim | Name | Source | Derivation |
69+
|-----|------|--------|------------|
70+
| 0 | `gait_speed` | `med_gait_analysis` (130) | Normalized walking velocity. Brisk = positive. |
71+
| 1 | `stride_regularity` | `med_gait_analysis` (131) | Low stride-to-stride variance = relaxed gait. |
72+
| 2 | `movement_fluidity` | CSI phase jerk (d3/dt3) | Low jerk = smooth, unhurried movement. |
73+
| 3 | `breathing_calm` | Vitals BR extraction | BR 12-18 at rest = calm. Deviation penalized. |
74+
| 4 | `posture_openness` | CSI subcarrier spread | Wide phase spread across subcarriers = open posture. |
75+
| 5 | `dwell_comfort` | `ret_dwell_heatmap` (420) | Moderate dwell in amenity zones = engagement. |
76+
| 6 | `direction_entropy` | `ret_customer_flow` (410) | Low entropy = purposeful movement. Wandering penalized. |
77+
| 7 | `group_energy` | Multi-target CSI clustering | Synchronized movement of 2+ people = social engagement. |
78+
79+
The composite scalar happiness score is the weighted L2 norm:
80+
81+
```
82+
score = sum(w[i] * v[i] for i in 0..7) / sum(w[i])
83+
```
84+
85+
Default weights are uniform (all 1.0), configurable via NVS or Seed API.
86+
87+
### 3. ESP32 to Seed Bridge
88+
89+
```
90+
ESP32-S3 (CSI) Cognitum Seed (169.254.42.1)
91+
+------------------+ +----------------------------+
92+
| Tier 2 DSP | | |
93+
| + WASM modules | UDP 5555 | /api/v1/store/ingest |
94+
| exo_happiness |──────────────| (POST, 8-dim vector) |
95+
| _score.rs | | |
96+
| | | /api/v1/drift/check |
97+
| |◄─────────────| (drift alerts via webhook) |
98+
| | | |
99+
| | | /api/v1/witness/append |
100+
| | | (Ed25519 audit trail) |
101+
+------------------+ +----------------------------+
102+
```
103+
104+
**Data flow:**
105+
106+
1. ESP32 runs CSI capture at 20+ Hz and feeds subcarrier data through existing WASM modules.
107+
2. `exo_happiness_score.rs` collects outputs from emotion, gait, flow, and dwell modules every scoring window (default: 30 seconds).
108+
3. The 8-dim happiness vector is packed as a 32-byte payload (8x float32) and sent via UDP to port 5555 on 169.254.42.1.
109+
4. A lightweight bridge task on the Seed receives the UDP packet and POSTs it to `/api/v1/store/ingest` with metadata (room ID, timestamp, MAC).
110+
5. The Seed's drift detection engine monitors the happiness vector stream and flags anomalies (sudden drops, sustained low scores).
111+
6. Every ingested vector is appended to the Seed's Ed25519 witness chain, providing a tamper-proof audit trail.
112+
113+
### 4. Seed Drift Detection for Happiness Trends
114+
115+
The Seed's built-in drift detection compares incoming vectors against a rolling baseline:
116+
117+
- **Check-in calibration:** When a new guest checks in, event 694 resets the baseline.
118+
- **Drift threshold:** Configurable (default: cosine distance > 0.3 from baseline triggers alert).
119+
- **Trend window:** Last 20 vectors (~10 minutes at 30s intervals).
120+
- **Alert routing:** Seed webhook notifies hotel management system when happiness trend is declining.
121+
122+
### 5. RuView Live Dashboard Update
123+
124+
`ruview_live.py` gains a `--seed` flag:
125+
126+
```bash
127+
python ruview_live.py --port COM5 --seed 169.254.42.1 --mode happiness
128+
```
129+
130+
This mode displays:
131+
- Real-time 8-dim radar chart of the happiness vector
132+
- Scalar happiness score (0-100) with color coding (red/yellow/green)
133+
- Trend sparkline over the last hour
134+
- Seed witness chain status (last hash, chain length)
135+
- Room-level aggregate when multiple ESP32 nodes report
136+
137+
### 6. Architecture
138+
139+
```
140+
+------------------------------------------+
141+
| Hotel Room |
142+
| |
143+
| [ESP32-S3] [Cognitum Seed] |
144+
| COM5 or COM7 169.254.42.1 |
145+
| 4MB or 8MB flash Pi Zero 2 W |
146+
| | | |
147+
| | WiFi CSI | PIR, reed, |
148+
| | 20+ Hz | BME280, |
149+
| v | vibration |
150+
| +-----------+ | |
151+
| | Tier 2 DSP| v |
152+
| | presence | +-------------+ |
153+
| | vitals | | Seed API | |
154+
| | gait | | 98 endpoints| |
155+
| | fall det | | 398 vectors | |
156+
| +-----------+ | dim=8 | |
157+
| | +-------------+ |
158+
| v ^ |
159+
| +-----------+ UDP 5555 | |
160+
| | WASM edge |─────────────┘ |
161+
| | happiness | |
162+
| | score | Drift alerts |
163+
| | (690-694) |◄────────────── |
164+
| +-----------+ /api/v1/drift/check |
165+
| |
166+
+------------------------------------------+
167+
|
168+
| MQTT / HTTP
169+
v
170+
+------------------+
171+
| Hotel Management |
172+
| System / RuView |
173+
| Live Dashboard |
174+
+------------------+
175+
```
176+
177+
### 7. 4MB Flash Support
178+
179+
The 4MB ESP32-S3 variant (COM5) is officially supported for happiness scoring. The existing `partitions_4mb.csv` and `sdkconfig.defaults.4mb` from ADR-265 provide dual OTA slots (1.856 MB each), sufficient for the full Tier 2 DSP firmware plus `exo_happiness_score.wasm` (estimated < 40 KB).
180+
181+
Build for 4MB variant:
182+
183+
```bash
184+
cp sdkconfig.defaults.4mb sdkconfig.defaults
185+
idf.py build
186+
```
187+
188+
The WASM module loader selects which modules to instantiate based on available heap. On the 4MB/2MB PSRAM variant, happiness scoring runs with a reduced scoring window (60s instead of 30s) to conserve memory.
189+
190+
### 8. Privacy Considerations
191+
192+
- **No cameras.** All sensing is RF-based (WiFi subcarrier amplitude/phase).
193+
- **No facial recognition.** Happiness is inferred from movement patterns, not expressions.
194+
- **No audio capture.** Breathing rate is extracted from chest wall displacement via RF, not microphone.
195+
- **No PII stored on device.** Vectors are anonymous; room-to-guest mapping lives only in the hotel PMS.
196+
- **Seed witness chain** provides auditable proof of what data was collected and when, satisfying GDPR Article 30 record-keeping requirements.
197+
- **Guest opt-out:** A physical switch on the ESP32 node (GPIO connected to a toggle) disables CSI capture entirely. The Seed's reed switch can also serve as a "privacy mode" trigger (door-mounted magnet removed = sensing paused).
198+
- **Data retention:** Vectors are retained on the Seed for the duration of the stay plus 24 hours, then purged. The witness chain retains hashes (not vectors) indefinitely for audit.
199+
200+
### 9. API Integration
201+
202+
Key Cognitum Seed endpoints used:
203+
204+
| Endpoint | Method | Purpose |
205+
|----------|--------|---------|
206+
| `/api/v1/store/ingest` | POST | Ingest 8-dim happiness vector |
207+
| `/api/v1/store/query` | POST | Retrieve vectors by room/time range |
208+
| `/api/v1/drift/check` | GET | Check if current vector drifts from baseline |
209+
| `/api/v1/drift/configure` | PUT | Set drift threshold and window size |
210+
| `/api/v1/witness/append` | POST | Append event to Ed25519 custody chain |
211+
| `/api/v1/witness/verify` | GET | Verify chain integrity |
212+
| `/api/v1/sensors/bme280` | GET | Room temperature/humidity (comfort correlation) |
213+
| `/api/v1/sensors/pir` | GET | PIR presence (cross-validate with CSI) |
214+
215+
## Consequences
216+
217+
### Positive
218+
219+
- Provides real-time, objective guest satisfaction measurement without surveys or wearables.
220+
- Reuses four existing WASM modules -- the happiness module is a fusion layer, not a rewrite.
221+
- The Seed's 8-dim vector store is a natural fit; no schema changes needed.
222+
- Ed25519 witness chain satisfies hospitality industry audit requirements and GDPR record-keeping.
223+
- Both 4MB and 8MB ESP32-S3 variants are supported, enabling low-cost deployment at scale (~$8 per room for the 4MB node).
224+
- Seed's environmental sensors (BME280, PIR) provide complementary context (room temperature, humidity) that can be correlated with happiness scores.
225+
- No cloud dependency -- all processing is local (ESP32 edge + Seed link-local network).
226+
227+
### Negative
228+
229+
- Happiness inference from movement patterns is a proxy, not a direct measurement. Correlation with actual guest satisfaction must be validated empirically.
230+
- The 4MB variant has reduced scoring frequency (60s vs 30s) due to memory constraints.
231+
- UDP transport between ESP32 and Seed is unreliable; packets may be lost. Mitigation: sequence numbers and a small retry buffer on the ESP32 side.
232+
- Link-local addressing (169.254.x.x) limits the Seed to the same network segment as the ESP32. Multi-room deployments need one Seed per subnet or a routed bridge.
233+
- Drift detection thresholds require per-property tuning; a luxury resort has different movement patterns than a budget hotel.
234+
- The system cannot distinguish between guests in a multi-occupancy room without additional multi-target CSI clustering, which is experimental (ADR-064, Tier 3).

0 commit comments

Comments
 (0)