Pilot Bio-Data — Pulse Oximeter Logger
Receives real-time heart rate (BPM) and blood-oxygen (SpO₂) readings from the Controlytics AI pulse oximeter over a wired Ethernet connection and logs them to a timestamped CSV file.
Hardware
| Item | Detail |
|---|---|
| Device | Controlytics AI Pulse Oximeter v1.0.0 |
| Interface | Ethernet (UDP) |
| Device Ethernet IP | 192.168.1.2 |
| Device WiFi AP | PULSE_OXI_WIFI / password controlytics |
| Device config page | http://192.168.4.1 (Admin / Admin) |
Network Setup
The device sends UDP packets to a configured destination address. The one-time setup below is already done; repeat only if hardware changes.
Computer (receiver)
Assign a static IP to the Ethernet interface connected to the device:
sudo ip addr add 192.168.1.100/24 dev <eth-interface>
On this machine the interface is enp1s0:
sudo ip addr add 192.168.1.100/24 dev enp1s0
Device configuration
Connect to PULSE_OXI_WIFI, open http://192.168.4.1, log in, and set:
| Field | Value |
|---|---|
| IP | 192.168.1.2 |
| Gateway | 192.168.1.1 |
| Subnet | 255.255.255.0 |
| Destination address | 192.168.1.100 |
| Destination port | 5005 |
| Send interval (ms) | 500 |
Submit and restart the device (Log Out button).
Requirements
Python 3.10 or later. No third-party packages — only the standard library is used.
python3 --version # 3.10+
Running
python3 receive.py
Place the pilot's index finger on the sensor with light, steady pressure. Hold still for a few seconds for the reading to stabilise.
Example output
Listening on UDP 0.0.0.0:5005 | Logging to readings.csv
TIME BPM SpO2 % STATUS
----------------------------------------------------
11:34:00.464 92 97 OK
11:34:00.965 92 97 OK
11:34:01.466 74 97 OK
11:34:22.034 87 98 OK
11:35:14.207 --- --- No finger
Press Ctrl+C to stop.
Output — readings.csv
Every packet is appended to readings.csv in the same directory.
Rows where no finger is detected have empty bpm and spo2 fields.
| Column | Description |
|---|---|
timestamp |
ISO-8601 local timestamp |
bpm |
Heart rate in beats per minute (empty if no finger) |
spo2 |
Blood-oxygen saturation % (empty if no finger) |
raw |
Raw JSON packet from device |
Example rows
timestamp,bpm,spo2,raw
2026-06-12T11:34:00.464000,92,97,"{""bpm"":92,""spo2"":97}"
2026-06-12T11:34:01.466000,74,97,"{""bpm"":74,""spo2"":97}"
2026-06-12T11:35:14.207000,,,"{""bpm"":-1,""spo2"":-1}"
Packet Format
The device sends a UDP JSON payload every 500 ms:
{"bpm": 92, "spo2": 97}
Values of -1 indicate no finger is on the sensor.
The parser also accepts key=value and bare CSV formats for forward compatibility.
Project Structure
pilot_bio_data/
├── receive.py # UDP listener and CSV logger
├── readings.csv # Auto-generated log (appended on each run)
└── README.md
Axial Aero — Internal tooling