System Architecture
The system is organized around two crutches connected through the MADS broker. The master crutch hosts the control and logging services, while both crutches run the sensing agents that publish data to the network.
Overview
Each crutch is based on a Raspberry Pi Zero 2 W and runs a small set of MADS agents. The master side coordinates the acquisition workflow, stores data, and exposes the web interface. The slave side focuses on local sensing and publishes the same acquisition streams so the master can merge them into a single recording.
Role Split
- Master-only services: Web Server, Coordinator, Status Handler, HDF5 Writer, Eye Tracker.
- Services on both crutches: Tip Loadcell, Handle Loadcell, PPG, UPS.
System Diagram

Open diagram in a new tab
Data Flow
The Web Server issues commands to the Coordinator through the MADS broker. The Coordinator drives the sensing agents, the Status Handler reports the live health of the system, and the HDF5 Writer persists the data streams that are later consumed by the visualization and download tools.
Agent Details
Each agent is shown in its own transposed table for readability.
Master-only agents
| web_server | |
| Agent type | Filter |
| Role | FastAPI backend and UI host. Manages acquisitions, test metadata, comments, conditions, plotting, and CSV download. |
| Sub topics | status |
| Pub topics | ws_command |
| JSON output format | {"command": "...", ...} with allowed values for command: start, stop, set_offset, get_agents_status, datetime_update, condition, pupil_neon_connect, pupil_neon_disconnect |
| coordinator | |
| Agent type | Filter |
| Role | Command router and state gate. Validates transitions and forwards normalized commands to all agents. |
| Sub topics | ws_command |
| Accepted command | start, stop, condition, datetime_updateget_agents_status, set_offsetpupil_neon_connect, pupil_neon_disconnect
|
| Pub topics | coordinator |
| JSON output format | - Command:
{"command":..., "id"?:n, "label"?:s, "subject_id"?:n, "session_id"?:n} - Heartbeat:
{"agent_status":"idle|recording"}
|
| status_handler | |
| Agent type | Filter |
| Role | Status aggregator and normalizer. It merges agent events and health messages into a single stream and marks agents unreachable after a timeout. |
| Sub topics | agent_eventcoordinatorupshdf5_writertip_loadcellhandle_loadcellppgpupil_neon
|
| Accepted command | get_agents_status |
| Pub topics | status |
| JSON output format | {"status":{"source","level","status","message","side"?}} |
| hdf5_writer | |
| Agent type | Filter |
| Role | HDF5 logger. During recording, appends configured keypaths to topic groups and finalizes files on stop. |
| Sub topics | coordinatortip_loadcellhandle_loadcellppgpupil_neonups
|
| Accepted command | |
| Pub topics | hdf5_writer |
| JSON output format | Heartbeat: {"agent_status":"idle|recording"} |
| pupil_neon | |
| Agent type | Filter |
| Role | Pupil Neon integration: device discovery/connection, recording control, condition events, and time-sync metrics. |
| Sub topics | coordinator |
| Accepted command | pupil_neon_connectstartconditionstoppupil_neon_disconnect
|
| Pub topics | pupil_neon |
| JSON output format | - Heartbeat:
{"agent_status":...,"error"?:...} - Sync:
time_offset_ms_*, roundtrip_duration_ms_*, (*mean, median, and std)
|
Agents deployed on both crutches
| tip_loadcell | |
| Agent type | Filter |
| Role | HX711-based tip force acquisition with side-specific scale and offset handling. |
| Sub topics | coordinator |
| Accepted command | |
| Pub topics | tip_loadcell |
| JSON output format | - Recording:
{"force":n,"side":"left|right"} - Heartbeat:
{"agent_status":"idle|recording","side":"left|right"} - Offset:
{"info":{"offset":{"value":n,"test":n}},"side":"left|right"}
|
| handle_loadcell | |
| Agent type | Filter |
| Role | 8-channel ADS1263 handle-force acquisition with channel map, range conversion, and offset calibration. |
| Sub topics | coordinator |
| Accepted command | |
| Pub topics | handle_loadcell |
| JSON output format | - Recording:
{"force":{"sensor_label":...},"side":"left|right"} - Offset:
{"info":{"offset":{"value":{"sensor_label":...},"test":{"sensor_label":...}}},"side":"left|right"} - Heartbeat:
{"agent_status":"idle|recording","side":"left|right"}
Sensor labels are up_front, up_back, right_front, right_back, left_front, left_back, down_front, down_back.
|
| ppg | |
| Agent type | Filter |
| Role | MAX30100-based PPG acquisition; publishes IR/RED samples while recording plus status heartbeat. |
| Sub topics | coordinator |
| Accepted command | |
| Pub topics | ppg |
| JSON output format | - Heartbeat:
{"agent_status":"idle|recording","side":"left|right"} - Samples:
{"ir":n,"red":n,"side":"left|right"}
|
| ups | |
| Agent type | Source |
| Role | INA219 power monitor; computes battery percentage and estimated remaining time and publishes periodic telemetry. |
| Pub topics | ups |
| JSON output format | {"agent_status":"idle","side":"left|right","info":{"voltage","current","power","percent","remaining_battery_time"}}
|