# Seebscribe ECG Streaming Service

This is the newer browser-first ECG service. It leaves the earlier monitor
files intact and adds a live WebSocket stream for real ECG samples.

## Test Without Hardware

```bash
cd "/Users/yaro/Library/CloudStorage/OneDrive-Personal/!FabAcademy 26/Notes/Final Project/Files/ECG"
python3 ecg_stream_service.py --simulate
```

Open:

```text
http://127.0.0.1:8766/ecg_live_dashboard.html?ws=8765
```

## Run With The XIAO + AD8232

1. Upload the current Arduino sketch in `../2HB_SerialPlotter`.
2. Close Arduino Serial Plotter so Python can use the serial port.
3. Run:

```bash
cd "/Users/yaro/Library/CloudStorage/OneDrive-Personal/!FabAcademy 26/Notes/Final Project/Files/ECG"
python3 ecg_stream_service.py
```

The service auto-detects a likely USB serial port. If it chooses the wrong one:

```bash
python3 ecg_stream_service.py --port /dev/cu.usbmodem1101
```

## If BPM / RR / RMSSD Stay Blank

The dashboard now shows diagnostics under the metric cards:

- `Polarity`: should usually become `inverted` for the AD8232 signal from the current sketch.
- `Signal span`: should be clearly above `35`; if it is lower, the ECG is too flat.
- `Noise`: lower is better; high noise means movement, poor skin contact, or loose pads.
- `Hint`: explains what the detector is waiting for.

Practical tuning steps:

1. Keep the Arduino sketch at `250 Hz` and run Python at the default `250 Hz`.
2. Close Arduino Serial Plotter before running Python.
3. Sit still for the first 3-5 seconds while auto-polarity and threshold settle.
4. If the signal is visible but no beats count, try:

```bash
python3 ecg_stream_service.py --polarity inverted
```

or:

```bash
python3 ecg_stream_service.py --polarity upright
```

The default is:

```bash
python3 ecg_stream_service.py --polarity auto
```

## HR / HRV Pipeline

The current service follows an Edge Impulse-style structure:

```text
raw ECG -> filtered/detector signal -> R peaks -> RR intervals -> HR/HRV features
```

Timing gates:

- BPM appears after at least 3 clean RR intervals.
- RMSSD appears after at least 10 seconds of clean RR intervals.
- SDNN, AVNN, pNN50, SD1, and SD2 appear after at least 30 seconds of clean RR intervals.
- Frequency-domain HRV is marked ready after 90 seconds, but frequency features are not calculated yet.

The dashboard shows:

- Raw ECG trace.
- Filtered/detector trace.
- Accepted R-peak markers.
- Confidence and rejected peak count.
- HRV warm-up status.

## Record A Session

To save a CSV for comparison with a smartwatch or pulse oximeter:

```bash
python3 ecg_stream_service.py --record-csv
```

This creates a file in `recordings/`.

To choose the output path:

```bash
python3 ecg_stream_service.py --record-csv my_ecg_session.csv
```

## MQTT Local Streaming

The gateway can also publish ECG JSON packets to MQTT. This keeps the local
dashboard working and adds an IoT-style stream for later cloud or BLE work.

Dry-run the MQTT packet structure without a broker:

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

Publish to a local MQTT broker:

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

If Python reports `Missing dependency: pip3 install paho-mqtt`, install the
MQTT client library first:

```bash
pip3 install paho-mqtt
```

Default topics:

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

See `MQTT_SCHEMA.md` for the JSON payload structure.

## BLE Wireless Gateway

The first wireless version keeps the same Python/dashboard/MQTT pipeline, but
replaces USB serial with BLE notifications from the XIAO ESP32-C3.

Upload:

```text
XIAO_AD8232_BLE/XIAO_AD8232_BLE.ino
```

The BLE firmware now mirrors the stable serial diagnostic sketch: 250 Hz
sampling, 4-sample moving average, and safe 20-byte BLE packets. It always sends
the ECG value and sends AD8232 LO+/LO- only as metadata, so noisy lead-off pins
cannot flatten the waveform.

If LO+/LO- is flickering while the ECG waveform is still useful, run the gateway
with `--ignore-leads` while tuning.

Install the Python BLE dependency:

```bash
pip3 install bleak
```

Run the BLE gateway:

```bash
python3 ble_ecg_gateway.py
```

Run BLE plus MQTT:

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

See `BLE_SCHEMA.md` for the binary BLE packet format.

## Wi-Fi UDP Gateway

The Wi-Fi version keeps the same Python/dashboard/MQTT pipeline, but replaces
BLE notifications with UDP packets over your local Wi-Fi network.

Upload:

```text
XIAO_AD8232_WIFI_UDP/XIAO_AD8232_WIFI_UDP.ino
```

Before upload, edit the Wi-Fi name/password at the top of the Arduino file:

```cpp
const char* WIFI_SSID = "YOUR_WIFI_NAME";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
```

The firmware sends compact binary UDP packets to port `5005`. It uses 25 samples
per packet, so only about 10 UDP packets/sec are sent. This is much less
chatty than the BLE notification stream and is useful for comparing whether BLE
radio timing was causing artifacts.

Run Wi-Fi dashboard:

```bash
python3 wifi_ecg_gateway.py --public-server --ignore-leads
```

Run Wi-Fi plus MQTT:

```bash
python3 wifi_ecg_gateway.py --mqtt --mqtt-host 127.0.0.1 --mqtt-port 1883 --public-server --ignore-leads
```

Open:

```text
http://127.0.0.1:8780/ecg_live_dashboard.html?ws=/ws
```

If the Python gateway does not receive packets, set `DESTINATION_IP` in the
Arduino sketch to your Mac's Wi-Fi IP instead of `255.255.255.255`.

See `WIFI_SCHEMA.md` for the binary UDP packet format.

## Public Demo Link

For internet streaming, use the one-port public server mode. This serves the
dashboard and the live WebSocket stream from the same local address, which makes
it much easier to expose with Cloudflare Tunnel or ngrok.

BLE public mode:

```bash
python3 ble_ecg_gateway.py --mqtt --mqtt-host 127.0.0.1 --mqtt-port 1883 --public-server
```

If Mosquitto is not running, the dashboard still starts and MQTT is skipped.
For a dashboard-only public demo, you can also omit MQTT:

```bash
python3 ble_ecg_gateway.py --public-server
```

USB serial public mode:

```bash
python3 ecg_stream_service.py --mqtt --mqtt-host 127.0.0.1 --mqtt-port 1883 --public-server
```

Open locally:

```text
http://127.0.0.1:8780/ecg_live_dashboard.html?ws=/ws
```

If `ngrok` is not installed:

```bash
brew install ngrok
ngrok config add-authtoken <your_ngrok_token>
ngrok http 8780
```

If you prefer Cloudflare Tunnel:

```bash
brew install cloudflared
cloudflared tunnel --url http://localhost:8780
```

Use the public `https://...` address printed by the tunnel tool. Keep this as a
short demo link, because ECG is personal biometric data even in an art/prototype
project.

## What Streams To The Browser

Sample batches:

```json
{"type":"samples","sample_rate":250,"threshold":2188,"samples":[2201,2198,2196]}
```

Beat events:

```json
{"type":"beat","rr":832,"bpm":72,"beat_count":12}
```

Metrics:

```json
{"type":"metrics","bpm":72,"rr":832,"rmssd":18.4,"rhythm":"Normal sinus","quality":"Good"}
```

The default sample rate is 250 Hz because the current Arduino sketch samples
every 4000 microseconds.
