Skip to content

Response Reference

This page documents the response packet formats received from the S120 device.

Responses arrive via BLE notifications on two characteristics:

CharacteristicPurposeType
0xAE02Measurement data, device infoNotify
0xAE05Errors, status updatesIndicate
PrefixCategoryDescription
F1 01 01ACKContinuous measure started
F1 02 01DataSingle measurement data
F1 02 02DataAngle data
F1 02 04DataContinuous measurement data
F1 03 00InfoMAC address
F1 03 01InfoHistory record
F1 03 02InfoDevice version (JSON)
F1 04 01ResultMeasurement result
F1 04 02StatusStatus update
F1 04 04HistoryHistory data
F1 04 05ErrorError notification
F1 04 06WarningWarning notification
F1 05 01ErrorDevice error event

Command: GET_DEVICE_MAC (F1 03 02 05)

Response format:

f1 03 00 LL DD DD DD DD DD DD DD DD DD DD DD DD CS CS
│ │ │ │ └──────────────────────────────────┴──┴── MAC (ASCII) + checksum
│ │ │ └────────────────────────────────────────── Length (0x0C = 12 bytes)
│ │ └───────────────────────────────────────────── Response code: 00 (MAC)
│ └──────────────────────────────────────────────── Type: 03 (info)
└─────────────────────────────────────────────────── Frame: F1

Live capture example:

f1 03 00 0c 35 62 61 36 38 36 33 38 66 61 63 61 03 a1
│ │ │ │ │ │ │ │ │ │ │ │
5 b a 6 8 6 3 8 f a c a (ASCII)

Decoded: MAC = 5b:a6:86:38:fa:ca

Command: GET_DEVICE_VERSION (F1 03 03 06)

Response format: JSON string (may span multiple BLE packets)

Live capture example:

{"bv":"V1.2.2","fv":"1.0.07","m":"s120"}
FieldValueDescription
bv”V1.2.2”Bootloader version
fv”1.0.07”Firmware version
m”s120”Model identifier

Command: SYNC_HISTORY_DATA (F1 03 01 14 18)

Response format (per record):

f1 03 01 SS MM UU FF FF FF VH VL SH SL CS
│ │ │ │ │ │ │ │ │ └──┴──┴──┴──┴── Value + checksum
│ │ │ │ │ │ └──┴──┴─────────────── Constant: 01 31 50
│ │ │ │ │ └──────────────────────── Unit code
│ │ │ │ └─────────────────────────── Mode code
│ │ │ └────────────────────────────── Status (00 = valid)
│ │ └───────────────────────────────── Response: 01 (history)
│ └──────────────────────────────────── Type: 03 (info)
└─────────────────────────────────────── Frame: F1

Value decoding:

value_raw = (VH << 8) | VL # Big-endian 16-bit
distance_mm = value_raw # In millimeters
distance_m = value_raw / 1000 # In meters

Live capture examples:

HexVHVLRawDistance
02 790x020x796330.633 m
04 810x040x8111531.153 m
0a 180x0A0x1825842.584 m

Full packet structure (23 bytes):

Byte: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
┌───────────────┬───────────────┬───────────────┬───────────────┬───────────────┬────────────┐
│ RecDataH3 │ RecDataH2 │ RecDataH1 │ RecDataMain │ Attributes │ Para │
│ (4 bytes) │ (4 bytes) │ (4 bytes) │ (4 bytes) │ (4 bytes) │ (3 bytes) │
└───────────────┴───────────────┴───────────────┴───────────────┴───────────────┴────────────┘
FieldBytesFormatDescription
RecDataH30-3uint32 BEThird dimension (height)
RecDataH24-7uint32 BESecond dimension (width)
RecDataH18-11uint32 BEFirst dimension (length)
RecDataMain12-15uint32 BEPrimary result

Field Definitions (Imperial - when unit_Len == 5)

Section titled “Field Definitions (Imperial - when unit_Len == 5)”
FieldByteFormatDescription
*.Fra0uint8Fraction (1/32”)
*.In1uint8Inches
*.FT2-3uint16 BEFeet
ByteBitsFieldDescription
200-4modeMeasurement mode (0x00-0x13)
206-7continuemodeContinuous flag
210unit_angleAngle unit (0=degrees)
211-3AddDecModeDecimal mode
214-7standbotDatum point
220-3modestepMode step
224msignSign (0=+, 1=-)
225-7unit_LenLength unit
Data TypeDivisorExample
Length÷ 1,00081600 → 81.6 m
Area÷ 1,000,0005000000 → 5.0 m²
Volume÷ 1,000,000,0001000000000 → 1.0 m³
Angle÷ 10450 → 45.0°
ModeRecDataMainH1H2H3
Single (0x01)Distance---
Continuous (0x02)Distance---
Area (0x03)AreaLengthWidth-
Volume (0x04)VolumeLengthWidthHeight
Angle+Height (0x07)HeightBase distAngle-
Pythagorean (0x08-0x0A)ResultSide ASide BSide C

Errors are sent on 0xAE05 with prefix F1 05 01:

f1 05 01 XX XX XX XX ...
└──────────── Error data (from byte 8 onwards)

Common errors (displayed on device):

ErrorMeaning
Err 1Target too far
Err 2Target too close
Err 3Excessive ambient light
Err 4Out of measurement range
def parse_mac_response(data: bytes) -> str:
"""Parse GET_DEVICE_MAC response."""
if data[:3] != bytes([0xF1, 0x03, 0x00]):
raise ValueError("Invalid MAC response prefix")
length = data[3]
mac_ascii = data[4:4+length].decode('ascii')
# Insert colons: "5ba68638faca" -> "5b:a6:86:38:fa:ca"
mac = ':'.join(mac_ascii[i:i+2] for i in range(0, len(mac_ascii), 2))
return mac
def parse_distance_response(data: bytes) -> float:
"""Parse single measurement response."""
# Assuming metric, distance is in RecDataMain (bytes 12-15)
raw = int.from_bytes(data[12:16], 'big')
return raw / 1000 # Convert to meters
def parse_history_record(data: bytes) -> dict:
"""Parse history record response."""
if data[:3] != bytes([0xF1, 0x03, 0x01]):
raise ValueError("Invalid history response prefix")
return {
'status': data[3],
'mode': data[4],
'unit': data[5],
'value_raw': (data[9] << 8) | data[10],
'distance_m': ((data[9] << 8) | data[10]) / 1000
}