|
| 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