# Seebscribe ECG MQTT Schema

The Python gateway can publish ECG data as JSON over MQTT while still serving
the local browser dashboard.

## Architecture

```text
XIAO ESP32-C3 + AD8232
  -> USB serial diagnostic packets
  -> Python gateway
  -> local browser dashboard
  -> MQTT broker
  -> subscribers: remote dashboard, recorder, cloud processor, visualizer
```

Later, the USB serial input can be replaced with BLE without changing the MQTT
topic and JSON structure.

## Topics

Default base topic:

```text
seebscribe/ecg
```

Default device ID:

```text
device01
```

Published topics:

```text
seebscribe/ecg/device01/status
seebscribe/ecg/device01/samples
seebscribe/ecg/device01/beats
seebscribe/ecg/device01/metrics
seebscribe/ecg/device01/hrv
```

## Sample Batch Payload

Published around 10 times per second by default.

```json
{
  "type": "samples",
  "device_id": "device01",
  "session_id": "20260502-120000",
  "timestamp": 1777690000.123,
  "sample_rate": 250,
  "start_index": 12000,
  "samples": [2210, 2214, 2209],
  "filtered_samples": [2208, 2212, 2211],
  "threshold": 2450,
  "lead_plus": 0,
  "lead_minus": 0,
  "lead_off": false
}
```

## Beat Payload

Published when a clean R-peak is accepted.

```json
{
  "type": "beat",
  "device_id": "device01",
  "session_id": "20260502-120000",
  "timestamp": 1777690001.456,
  "sample_index": 12340,
  "value": 2601,
  "rr_ms": 688,
  "bpm": 87,
  "confidence": 82,
  "stable": true
}
```

## Metrics Payload

Published once per second and after accepted beats.

```json
{
  "type": "metrics",
  "device_id": "device01",
  "session_id": "20260502-120000",
  "timestamp": 1777690002.000,
  "bpm": 87,
  "mean_hr": 86,
  "rr_ms": 688,
  "confidence": 82,
  "stable": true,
  "quality": "Good",
  "rhythm": "Normal sinus",
  "clean_seconds": 12.4
}
```

## HRV Payload

Published when HRV windows are available.

```json
{
  "type": "hrv",
  "device_id": "device01",
  "session_id": "20260502-120000",
  "timestamp": 1777690030.000,
  "window_sec": 30,
  "rmssd": 24.8,
  "avnn": 714.2,
  "sdnn": 38.5,
  "pnn50": 12.5,
  "sd1": 17.4,
  "sd2": 51.2,
  "rmssd_ready": true,
  "time_domain_ready": true,
  "frequency_ready": false,
  "hrv_wait": "Frequency HRV needs 90s clean RR",
  "confidence": 78
}
```

## Status Payload

Published every 2 seconds and retained on the broker.

```json
{
  "type": "status",
  "device_id": "device01",
  "session_id": "20260502-120000",
  "timestamp": 1777690000.000,
  "source": "/dev/cu.usbmodem1101",
  "sample_rate": 250,
  "connected": true,
  "lead_plus": 0,
  "lead_minus": 0,
  "lead_off": false,
  "leads": "attached",
  "quality": "Good",
  "confidence": 90,
  "firmware": "xiao-ad8232-diagnostic-v1"
}
```

## Local Test Commands

Dry-run packet structure without a broker:

```bash
python3 ecg_stream_service.py --simulate --mqtt-dry-run
```

Publish to a local broker:

```bash
python3 ecg_stream_service.py --mqtt --mqtt-host 127.0.0.1
```

Subscribe with Mosquitto tools:

```bash
mosquitto_sub -h 127.0.0.1 -t 'seebscribe/ecg/device01/#' -v
```
