Skip to main content

Data output

The data schema can be obtained with the sifibridge schema subcommand.

Data packet format​

SiFi Bridge outputs sensor data as JSON packets (use -p flag for pretty-printed output). Each packet follows a standard structure defined by the DataPacket schema.

DataPacket structure​

Each packet contains data from a single sensor at most.

Each data channel has the same number of samples, and the timestamps always has the same number of values too. Data values and timestamps are matched 1-to-1: (timestamps[0], data['ecg'][0]) form a pair.

{
"device": "BioPoint",
"id": "BP_7A1F",
"name": "my_bp",
"mac": "C2:EC:EF:30:0D:10",
"packet_type": "ecg",
"status": "ok",
"received_at": 1782573963.169,
"timestamps": [
17.826,
17.828,
17.83,
],
"data": {
"ecg": [
null,
0.0,
4.378609035326087e-7,
]
},
"sample_rate": 500.28058548806575,
"samples_lost": 1,
"download_progress": 90
}

Field descriptions​

FieldTypeRequiredDescription
devicestringYesDevice type, i.e. BioPoint or SiFiBand
idstringYesDevice unique identifier
namestringYesDevice name
macstringYesDevice MAC address
packet_typestringYesSee Packet types
statusstringYesSee Packet Status
received_atdoubleYesPacket time of arrival (seconds since Unix epoch)
dataHashMap[string, list[float]] | nullNoSee Data channels
timestampslist[float] | nullNoData timestamps (seconds since the start of the acquisition)
sample_ratedouble | nullNoSampling rate (Hz). See Sampling rate
samples_lostuint | nullNoSee Lost samples
download_progressuint8 | nullNoMemory download progress (percent)

Packet types​

The packet_type field indicates what kind of data is in the packet:

TypeDescriptionData channels
ecgElectrocardiographyecg
emgElectromyographyemg
edaElectrodermal Activityeda
imuInertial Measurement Unitax, ay, az, qw, qx, qy, qz
ppgPhotoplethysmographyir, r, g, b
emg_armbandEMG Armband (8 channels)emg0-emg7
temperatureTemperature sensortemperature
statusDevice status updatetemperature, battery_%, memory_used_kbytes
start_timeAcquisition start timeyear, month, day, hour, minute, second
eventSoftware event markerevent (a counter, incrementing per event)
memoryMemory event-
device_infoDevice information notice, emitted on connect- (no data field)
invalidInvalid/unrecognised packet-

The status packet also carries a top-level device_state field, and start_time a top-level start_time field, alongside the channels above.

Two further values

The packet-type schema also declares low_latency ("extended" low latency) and start_packet. Both are internal to the BLE framing and are not normally surfaced as standalone packets on the data stream, so you are unlikely to see them — but treat packet_type as an open set rather than matching exhaustively. Export the authoritative list at any time with:

sifibridge schema -o ./schemas

Packet Status​

The status field indicates the state of the packet:

StatusDescription
okData received successfully
recordingDevice is recording to memory
memory_download_completedMemory download finished
memory_erasedDevice memory was erased
invalid_datetimeInvalid timestamp
invalidInvalid/corrupted packet

Channel names​

Available channel names that can appear in the data object:

Biosignal channels​

  • ecg - Electrocardiography (volts)
  • emg (BioPoint) / emg0-emg7 (SiFiBand) - Electromyography (volts)
  • eda - Electrodermal activity (siemens)
  • temperature - Skin temperature (°C)

IMU channels​

  • ax, ay, az - Accelerometer (m/s²)
  • qw, qx, qy, qz - Normalized quaternions

PPG channels​

PPG does not have a standardized unit since it mesures the intensity of the light reflected in the skin. Typical values are in the ~1000-10000 absolute units (AU) range.

  • ir - Infrared LED
  • r - Red LED
  • g - Green LED
  • b - Blue LED

Status channels​

  • temperature - Temperature (°C). Redundant with dedicated temperature packets.
  • battery_% - Battery level (percentage)

Device types​

The device field can be:

  • BioPoint - BioPoint device
  • SiFiBand - SiFiBand device
  • Invalid - Unknown/invalid device

Sampling rate​

The sample_rate field reports the effective sampling rate in Hz of the sensor as measured by SiFi Bridge. SiFi Bridge continually measures this value as each packet is received. Normally, it converges toward and perhaps slightly oscillates around the configured rate due to the jitter in Bluetooth communications.

The field is omitted from the packet entirely when there is no rate to report — it is never serialized as null. Consumers must treat it as optional (packet.get("sample_rate") in Python) rather than indexing it directly. It is absent:

  • on packet types that do not carry periodically sampled data, such as status, start_time, and memory;
  • on the first packet of each sensor in an acquisition, since the rate is measured from the interval between packets and there is nothing to measure against yet;
  • during a memory download, since packets are streamed as fast as possible, making the measured sampling rate meaningless.

Lost samples​

The samples_lost field reports how many samples were lost since the last packet. This can happen if the BLE link is unstable, causing the effective bandwidth of the BLE link to be lower than what the device requires to deliver all data in real-time.

To preserve the 1-to-1 alignment between timestamps and data values, each lost sample is kept as a null entry in the affected data channel(s), with its corresponding timestamp still present. In the DataPacket example above, samples_lost is 1 and the first ecg value is null. Its associated timestamp is 17.826 s.

If the key is missing, it means no samples were lost.

Downloading memory with lost samples

Lost samples only affect streamed data. The on-device data is not lost!

For instance, if an acquisition is started with memory mode of both, and some samples are lost, you can still obtain the real data by downloading the data from the device's onboard memory afterward.


Example packets​

ECG packet​

{
"device": "SiFiBand",
"id": "SB_EFFD",
"name": "my_sb",
"mac": "CD:92:AD:DA:B7:71",
"packet_type": "ecg",
"status": "ok",
"received_at": 1782598065.104,
"sample_rate": 996.8285003209548,
"timestamps": [
8.184
],
"data": {
"ecg": [
0.0
]
}
}

EMG packet​

This only applies to BioPoint.

{
"device": "BioPoint",
"id": "BP_7A1F",
"name": "my_bp",
"mac": "C2:EC:EF:30:0D:10",
"packet_type": "emg",
"status": "ok",
"received_at": 1783259483.825,
"sample_rate": 1998.5506970149786,
"timestamps": [
3.448,
3.499
],
"data": {
"emg": [
-0.000020798392917798913,
-0.000021455184273097826
]
}
}

8-channel EMG packet​

This only applies to SiFiBand.

{
"device": "SiFiBand",
"id": "SB_EFFD",
"name": "my_sb",
"mac": "CD:92:AD:DA:B7:71",
"packet_type": "emg_armband",
"status": "ok",
"received_at": 1783476224.947,
"sample_rate": 1597.1503517986785,
"timestamps": [
1.541875,
],
"data": {
"emg0": [
6.56791355298913e-7,
],
"emg1": [
2.1893045176630434e-7,
],
"emg2": [
-8.757218070652174e-7,
],
"emg3": [
-4.378609035326087e-6,
],
"emg4": [
2.1893045176630434e-7,
],
"emg5": [
5.254330842391304e-6,
],
"emg6": [
1.970374065896739e-6,
],
"emg7": [
1.0946522588315218e-6,
]
}
}

EDA packet​

{
"device": "BioPoint",
"id": "BP_7A1F",
"name": "my_bp",
"mac": "C2:EC:EF:30:0D:10",
"packet_type": "eda",
"status": "ok",
"received_at": 1730370226.150,
"timestamps": [1730370225.123, 1730370225.163, 1730370225.203],
"data": {
"eda": [1.6323e-6, 1.64873e-6 , 1.72176e-6]
},
"sample_rate": 50.0
}

IMU packet​

{
"device": "SiFiBand",
"id": "SB_EFFD",
"name": "my_sb",
"mac": "CD:92:AD:DA:B7:71",
"packet_type": "imu",
"status": "ok",
"received_at": 1782598065.101,
"sample_rate": 198.82754760640623,
"timestamps": [
8.15,
8.16
],
"data": {
"ax": [
9.889045000076294,
9.698618143200875
],
"ay": [
-0.09820778757333756,
-0.13653277784585954
],
"az": [
-0.17845073595643043,
-0.14012574568390848
],
"qw": [
0.6869235096042648,
0.6868557952294714
],
"qx": [
0.03461920498869666,
0.034735847377314714
],
"qy": [
-0.7243495047894222,
-0.7243973636306342
],
"qz": [
0.047491025575671385,
0.0476549790856847
]
}
}

PPG packet​

{
"device": "BioPoint",
"id": "BP_7A1F",
"name": "my_bp",
"mac": "C2:EC:EF:30:0D:10",
"packet_type": "ppg",
"status": "ok",
"received_at": 1730370225.150,
"timestamps": [1730370225.123, 1730370225.133, 1730370225.143],
"data": {
"ir": [2738.0, 2753.0, 2765.0],
"r": [3141.0, 3154.0, 3168.0],
"g": [3465.0, 3474.0, 3482.0],
"b": [2890.0, 2903.0, 2915.0]
},
"sample_rate": 100.0
}

Status packet​

{
"device": "BioPoint",
"id": "BP_7A1F",
"name": "my_bp",
"mac": "C2:EC:EF:30:0D:10",
"packet_type": "status",
"status": "ok",
"received_at": 1782574161.811,
"timestamps": [
1782574161.811
],
"data": {
"temperature": [
27.607770919799805
],
"battery_%": [
92.0
],
"memory_used_kbytes": [
370808.490234375
]
}
}

Processing Data Packets​

For Python, we recommend using sifi-bridge-py.