NAV
c

Introduction

Document describes communication protocol between PC and AIRTLS devices. Protocol is used to transfer data over USB and Bluetooth Low Energy.

Hardware ID

typedef enum Descriptor_BoardType_e
{
    /* --- Anchors --- */

    /** Field anchor, mobile, LED */
    Descriptor_BoardType_AIRTLS_FA01 = 0x04,
    /** Field anchor nRF5340, mobile, LED */
    Descriptor_BoardType_AIRTLS_FA02 = 0x07,
    /** Anchor ETH/WiFi ESP32 nRF53 */
    Descriptor_BoardType_AIRTLS_WA02 = 0x06,
    /** Anchor Horn nRF53 */
    Descriptor_BoardType_AIRTLS_HA02 = 0x24,
    /** Anchor Socket Line-In Neutral-Out nRF53 */
    Descriptor_BoardType_AIRTLS_LINO = 0x27,

    /* --- Tags --- */

    /** Tag Ball nRF52 */
    Descriptor_BoardType_AIRTLS_TB0X = 0x3F,
    /** Tag Display nRF52 */
    Descriptor_BoardType_AIRTLS_TA01 = 0x3E,
    /** Tag Display nRF53 */
    Descriptor_BoardType_AIRTLS_TA02 = 0x37,
    /** Tag Field Pylon nRF53*/
    Descriptor_BoardType_AIRTLS_PT02 = 0x35,
    /** Tag Industrial Ruggedized nRF53 */
    Descriptor_BoardType_AIRTLS_CT02 = 0x33,

} Descriptor_BoardType_t;

typedef enum Descriptor_MCU_e
{
    Descriptor_MCU_nRF52 = 0, /**< nRF52 */
    Descriptor_MCU_nRF53 = 1, /**< nRF53 */

} Descriptor_MCU_t;

Hardware ID consists of Board Type and MCU Type. Parameter is used in protocol (field hwid) and formatted using following bit assignment with a single byte storage:

Bit Field
0-5 boardType
6-7 mcuType

Board Type defines type of the board and its place in the system. It is based on hardware pin configuration and takes following values:

Value Board type Description
0x04 FA01 Field Anchor
0x07 FA02 Field Anchor (nRF53)
0x06 WA02 Anchor with Ethernet/WiFi interface
0x24 HA02 Horn Anchor
0x27 LINO Line-In Neutral-Out 230 VAC
0x3F TB0X Ball Tag
0x3E TA01 Display Tag (nRF52)
0x37 TA02 Display Tag (nRF53)
0x35 PT02 Pylon Tag
0x33 CT02 Industrial Tag

MCU Type depends on the processor used by the device and could have following values:

Value MCU type
0 nRF52
1 nRF53

TLV

Type-Length-Value is format of encoding well known data.

General frame structure

struct Protocol_Frame_t
{
    uint8_t type;
    uint16_t length;
    uint8_t value[];
};

Example

Example structures and raw binary data

typedef enum FrameType_e
{
    FrameType_A = 0x00,
    FrameType_B = 0x01,
};

struct A_t
{
    uint8_t a1;
    uint16_t a2;
};

struct B_t
{
    uint16_t b1;
    char b2[];
};

uint8_t rawDataStream = {
    0x00, 0x03, 0x00, 0x00, 0x01,
    0x02, 0x01, 0x08, 0x00, 0x07,
    0x00, 0x41, 0x49, 0x52, 0x54,
    0x4c, 0x53
};

Let's consider some example:

There are two types of frames A and B. Frame type A is represented by type value 0x00, frame type B is represented by type value 0x01.

To start unpacking data we need to parse first three bytes of raw data stram. First byte 0x00 represents type of parsed message, so

Type = 0x00 - FrameType_A means that data in value field contains structure A_t. Structure A_t is well known, and has constant size, which is 3B. To confirm that we can read length field.

Second and third byte 0x03, 0x00 represents value length. It must be casted to uint16 value, so

Next 3 bytes 0x00, 0x01, 0x02 can be interpreted as A_t structure, so

But there is more data! We need to repeat the whole process. Take next byte 0x01, and interpret it as message type.

Type is equal to FrameType_B, so value field contains structure B_t, which size varies. To get actual B_t size we need to read length field, so we need to process next two bytes 0x08, 0x00,.

We know that b1 field is two bytes long, so to get length of b2 field we need to subtract 2B from read value length.

8 - 2 = 6

We now that in case of this message b2 field will be 6B long. Now we have all information to decode value field.

Dictionary

Supported frame types

Type Direction Description
0x01 Master Anchor ⟶ PC Time of Flight (ToF) data
0x02 Master Anchor ⟶ PC Serial data
0x03 Master Anchor ⟶ PC Admin distribution data
0x04 Master Anchor ⟶ PC Compressed Sensor data - XORDS
0x05 Master Anchor ⟶ PC Accelerometer data - bit-stitched
0x06 Master Anchor ⟶ PC Gyroscope data - bit-stitched
0x07 Master Anchor ⟶ PC Magnetometer data - bit-stitched
0x08 Master Anchor ⟶ PC Impact data
0x80 (128) PC ⟶ Master Anchor [⟶ UWB] Command set serial number
0x81 (129) PC ⟶ Master Anchor [⟶ UWB] Command set group
0x82 (130) PC ⟶ Master Anchor [⟶ UWB] Command set name
0x83 (131) PC ⟶ Master Anchor [⟶ UWB] Command set IMU calibration
0x84 (132) PC ⟶ Master Anchor [⟶ UWB] Command set message
0x85 (133) PC ⟶ Master Anchor [⟶ UWB] Command update license
0x86 (134) PC ⟶ Master Anchor [⟶ UWB] Command set Magnetometer calibration
0x87 (135) PC ⟶ Master Anchor [⟶ UWB] Command set Heart Rate MAC
0x88 (136) PC ⟶ Master Anchor [⟶ UWB] Command set BLE config
0x8B (139) PC ⟶ Master Anchor [⟶ UWB] Command set UWB config
0x8C (140) PC ⟶ Master Anchor [⟶ UWB] Command shutdown (kill)
0x8D (141) PC ⟶ Master Anchor [⟶ UWB] Command set board config
0x8E (142) PC ⟶ Master Anchor [⟶ UWB] Command set network config
0x8F (143) PC ⟶ Master Anchor [⟶ UWB] Command set random period
0x90 (144) PC ⟶ Master Anchor [⟶ UWB] Command set network start delay
0x93 (147) PC ⟶ Master Anchor [⟶ UWB] Command display overlay command
0x94 (148) PC ⟶ Master Anchor [⟶ UWB] Command display overlay update
0x95 (149) PC ⟶ Master Anchor [⟶ UWB] Command display overlay upload
0x96 (150) PC ⟶ Master Anchor [⟶ UWB] Command MOx setup
0xB4 (180) PC ⟶ Master Anchor [⟶ UWB] Command set ESP config 1
0xB5 (181) PC ⟶ Master Anchor [⟶ UWB] Command set ESP config 2
0xB6 (182) PC ⟶ Master Anchor [⟶ UWB] Command set ESP WiFi password 1
0xB7 (183) PC ⟶ Master Anchor [⟶ UWB] Command set ESP WiFi password 2
0xC4 (196) PC ⟶ Master Anchor [⟶ UWB] Command commit seat plan
0xC5 (197) PC ⟶ Master Anchor [⟶ UWB] Command set seat plan
0xC6 (198) PC ⟶ Master Anchor [⟶ UWB] Command factory reset
0xC7 (199) PC ⟶ Master Anchor [⟶ UWB] Command blink pylon
0xC8 (200) PC ⟶ Master Anchor [⟶ UWB] Command blink immediately
0xC9 (201) PC ⟶ Master Anchor [⟶ UWB] Command blink

0x01 - Time of Flight (ToF) data

C Structure format

typedef struct ToF_TagImpactReport_s
{
    uint8_t did[3];
    uint8_t hitCycleOffset;
    uint8_t motionByte;

} ToF_TagImpactReport_t;

typedef struct ToF_Tag_s
{
    uint8_t did[3];
    struct
    {
        uint8_t receivedByTag : 4;    /** bits 0:3 */
        uint8_t receivedByAnchor : 4; /** bits 4:7 */
    } rssi;

    uint16_t tof[];
} ToF_Tag_t;

typedef struct ToF_TagList_s
{
    struct
    {
        uint8_t numberOfToFs : 6; /** bits 0:5 */
        uint8_t precision : 2;    /** bits 6:7 */
    };

    uint8_t tagCount;
    uint8_t tagData[];

} ToF_TagList_t;

typedef struct Protocol_ToFReport_s
{
    uint8_t did[3];
    uint16_t cycleId;

    struct
    {
        uint8_t did[3];
        struct
        {
            uint8_t receivedByResponderAnchor : 4;   /** bits 0:3 */
            uint8_t receivedByTransmitterAnchor : 4; /** bits 4:7 */
        } rssi;

        uint16_t tof[4];
    } a2a;

    struct
    {
        uint8_t hitCycleOffset;
        uint8_t motionByte;
        uint8_t tagImpactReportCount;
    } imu;

    uint8_t tagListsCount;
    uint8_t tagListsData[];

} Protocol_ToFReport_t;

Frame example:

- One list of tags
- Two tags in list
- Each tag has 3 ToF reports, and ToF precision without shift (lsb = 0.5 cm)
- Two extra Tag impact reports
uint8_t rawData[] =
{
    /* TLV header: type = 1, length = 53 */
    0x01, 0x32, 0x00, 

    /* TLV value buffer (Protocol_ToFReport_t) */
    0x05, 0x0a, 0x0f, 0x88, 0x13, 

    /* a2a:
        did = [20, 40, 60],
        rssi.receivedByResponderAnchor = 5,
        rssi.receivedByTransmitterAnchor = 12,
        tof[0] = 300,
        tof[1] = 302,
        tof[2] = 301,
        tof[3] = 303
    */
    0x14, 0x28, 0x3c, 0x5c, 0x2c, 0x01, 0x2e, 0x01, 0x2d, 0x01, 0x2f, 0x01, 

    /* imu:
        hitCycleOffset = 40;
        motionByte = 0x80 | 10; - Anchor faced up | motion '10'
        tagImpactReportCount = 2;
    */
    0x28, 0x8a, 0x02, 

    /* tagListsCount = 1 */
    0x01,

    /* Tag lists data (ToF_TagList_t [])*/

    /* List 0 data:
        precision    = 0,
        numberOfToFs = 3,
        tagCount     = 2,
    */
    0x03, 0x02,

    /* Tag data (ToF_Tag_t []) */

    /* Tag 0 data:
        did = [33, 34, 35],
        rssi.receivedByAnchor = 10,
        rssi.receivedByTag = 12,
        tof[0] = 200,
        tof[1] = 201,
        tof[2] = 202
    */
    0x21, 0x22, 0x23, 0xac, 0xc8, 0x00, 0xc9, 0x00,
    0xca, 0x00,

    /* Tag 1 data:
        did = [33, 34, 36],
        rssi.receivedByAnchor = 11,
        rssi.receivedByTag = 13,
        tof[0] = 204,
        tof[1] = 205,
        tof[2] = 206
    */
    0x21, 0x22, 0x24, 0xbd, 0xcc, 0x00, 0xcd, 0x00,
    0xce, 0x00,

    /* Impact report 1:
        did = [33, 34, 35],
        hitCycleOffset = 17;
        motionByte     = 27;
    */
    0x21, 0x22, 0x23, 0x11, 0x1b, 

    /* Impact report 2:
        did = [33, 34, 36],
        hitCycleOffset = 22;
        motionByte     = 50;
    */
    0x21, 0x22, 0x24, 0x16, 0x32, 
}

Frame transmitted from the device (Master Anchor) to the PC after reception of ToF report from other Anchor (in OFFLOAD slot), and after OFFLOAD, when we want to transmit ToF report from MasterAnchor.

Frame format changes dynamically depending on type of tags (number of ToFs), and tag count. Frame consists of three well defined nested structures (3 levels):

Structure nesting example

ToF Report contains two tag lists (two tag types). First list contains three tags of given type, second list contains single tag of other type (not related to example in code).

RSSI encoding

RSSI value in described packet is stored as 4-bit packed value (range 0-15) specifying value from -78dB (packed as 0) to -93dB (packed as 15) with 1dB resolution.

Protocol_ToFReport_t fields

Byte Storage Field Description
0-2 3 bytes (array uint8) did Three bytes long Anchor (or Master Anchor) ID
3-4 2 bytes (uint16) cycleId ID of the cycle in which the data was sent
5-7 3 bytes (array uint8) a2a.did Three bytes long Target Anchor ID (Target Anchor that performed ranging)
8 [bits 0-3] 4 bits (uint8) a2a.rssi.receivedByResponderAnchor 4bit packed RSS value of frame received by responder Anchor
8 [bits 4-7] 4 bits (uint8) a2a.rssi.receivedByTransmitterAnchor 4bit packed RSS value of frame received by transmitter Anchor (initiator)
9-10 2 bytes (uint16) a2a.tof[0] ToF value #0
11-12 2 bytes (uint16) a2a.tof[1] ToF value #1
13-14 2 bytes (uint16) a2a.tof[2] ToF value #2
15-16 2 bytes (uint16) a2a.tof[3] ToF value #3
17 1 byte (uint8) imu.hitCycleOffset Impact hit detection in miliseconds relative to the start of the cycle
18 1 byte (uint8) imu.motionByte Anchor motion byte characterizing device movement
19 1 byte (uint8) imu.tagImpactReportCount Number of Tag impact reports at the end of the ToF report frame
20 1 byte (uint8) tagListsCount Defines number of ToF_TagList_t structures in tagListsData buffer (number of different tag configurations)
21+ n bytes (array uint8) tagListsData Contains list of ToF_TagList_t structures

ToF_TagList_t fields

Byte Storage Field Description
0 [bits 0-5] 6 bits (uint8) numberOfToFs 6bit value that defines number of ToF values (uint16) in each ToF_Tag_t structure
0 [bits 6-7] 2 bits (uint8) precision 2bit value that defines ToF value shift. (0 = unchanged (lsb = 0.5cm), 1 = divided by 2 (lsb = 1cm), 2 = divided by 4 (lsb = 2cm), 3 = divided by 8 (lsb = 4cm)).
1 1 bytes (uint8) tagCount Defines how many ToF_Tag_t structures are in tagData buffer
2+ n bytes (array uint8) tagData Contains array of ToF_Tag_t structures

ToF_Tag_t fields

Byte Storage Field Description
0-2 3 bytes (array uint8) did Three bytes long Tag ID
3 [bits 0-3] 4 bits (uint8) rssi.receivedByTag 4bit packed RSS value of frame received by Tag
3 [bits 4-7] 4 bits (uint8) rssi.receivedByAnchor 4bit packed RSS value of frame received by Anchor
4+ n * 2 bytes (array uint16) tof Array ToF values (uint16). Array length is defined in parent ToF_TagList_t structure

ToF_TagImpactReport_t fields

Byte Storage Field Description
0-2 3 bytes (array uint8) did Three bytes long Tag ID
3 1 byte (uint8) hitCycleOffset Impact hit detection in miliseconds relative to the start of the cycle
4 1 byte (uint8) motionByte Motion byte characterizing device movement

0x02 - Serial data

C Structure format

typedef struct Protocol_Sensors_s
{
    uint8_t did[3];
    uint16_t cycleId;
    uint8_t slotId;
    uint8_t data[];
} Protocol_Sensors_t;

Frame example c uint8_t rawData[] = { /* TLV header: type = 2, length = 12 */ 0x02, 0x0c, 0x00, /* Value buffer did = [10, 20, 30], cycleId = 10000, slotId = 50 data = 'AIRTLS' */ 0x0a, 0x14, 0x1e, 0x10, 0x27, 0x32, 0x41, 0x49, 0x52, 0x54, 0x4c, 0x53, }

Frame transmitted from the device (Master Anchor) to the PC after reception of TRESPONSE frame form device. Frame contains serial data form the device. Frame length depends on serial data length.

Byte Storage Field Description
0-2 3 bytes (array uint8) did Three bytes long Tag ID
3-4 2 bytes (uint16) cycleId ID of the cycle in which the data was sent
5 1 byte (uint8) slotId ID of the slot (seat) in which data was sent
6+ n bytes (array uint8) data Raw serial data transmitted from the device. Field has variable size

0x03 - Admin Distribution Info

C Structure format

typedef struct Protocol_AdminDistributionInfo_s
{
    uint8_t did[3];
    uint16_t cycleId;

    struct
    {
        /* Constants */
        uint8_t fwVersionMajor;
        uint8_t fwVersionMinor;
        uint8_t hwid;
        uint8_t mac[6];

        /* Configuration */
        struct
        {
            uint8_t deviceOffset;
            uint8_t frequency;
            uint8_t seat;
        } cycle;

        uint8_t serialNumber[4];
        char name[9];
        char group[3];
        uint16_t licenseConfig;

        struct 
        {
            uint8_t txPowerPreset;
        } bt;

        struct
        {
            uint8_t txPowerPreset;
        } uwb;

        uint8_t boardConfig;
        uint16_t licenseTime;

        uint8_t seatPlanVersion;

    } config;

    /* Calibration data */
    struct 
    {
        struct
        {
            int8_t ch3AntennaDelay;
            int8_t ch5AntennaDelay;
        } uwb;

        struct
        {
            uint8_t bias;

        } pressure;

        struct
        {
            struct
            {
                uint8_t x[2];
                uint8_t y[2];
                uint8_t z[2];
            } bias;

            struct
            {
                uint8_t x;
                uint8_t y;
                uint8_t z;
            } scalar;

        } magnetometer;

        struct
        {
            struct
            {
                uint8_t x;
                uint8_t y;
                uint8_t z;

            } accelerometer;

        } imu;

    } calibration;

    /* Dynamic sensor data */
    struct
    {
        uint8_t vbat;
        uint8_t motionByte;
        uint8_t hit;
        uint8_t pressure;
        uint8_t heartRate;
        uint8_t mox;
    } sensorsData;

    /* Indicates which data is valid in the above set */
    uint8_t valid[7];

    uint8_t slowTagsDetected;
    Protocol_AdminDistributionInfo_SlowTag_t slowTags[];

} Protocol_AdminDistributionInfo_t;

typedef struct Protocol_AdminDistributionInfo_SlowTag_s
{
     /** Short device ID */
    uint8_t did[3];

    uint8_t seat;       /**< Device seat */
    uint8_t vbat;       /**< Packed battery voltage */
    uint8_t hwid;       /**< Board type HWID */
    uint8_t motionByte; /**< Motion byte characterizing device movement */

} Protocol_AdminDistributionInfo_SlowTag_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 3, length = 69 */
    0x03, 0x45, 0x00,

    /* Value buffer (Protocol_AdminDistributionInfo_t):
        did = [0xaf, 0x31, 0x9e],
        cycleId = 46385,

        config.fwVersionMajor = 0,
        config.fwVersionMinor = 13,
        config.hwid = 124,
        config.mac = [0xfd, 0x5d, 0x4b, 0xaf, 0x31, 0x9e],
        config.cycle.deviceOffset = 0,
        config.cycle.frequency = 5,
        config.cycle.seat = 1,
        config.serialNumber = [0x40, 0xE2, 0x01, 0x00],
        config.name = "MY_TAG",
        config.group = "ABC",
        config.licenseConfig = 5,
        config.bt.txPowerPreset = 0,
        config.uwb.txPowerPreset = 22,
        config.boardConfig = 0x00,
        config.licenseTime = 0,
        config.seatPlanVersion = 32,

        calibration.uwb.ch3AntennaDelay = 0,
        calibration.uwb.ch5AntennaDelay = 0,
        calibration.pressure.bias = 255,
        calibration.magnetometer.bias.x = 1,
        calibration.magnetometer.bias.y = 2,
        calibration.magnetometer.bias.z = 3,
        calibration.magnetometer.scalar.x = 4,
        calibration.magnetometer.scalar.y = 5,
        calibration.magnetometer.scalar.z = 6,
        calibration.imu.accelerometer.x = 9,
        calibration.imu.accelerometer.y = 10,
        calibration.imu.accelerometer.z = 11,

        sensorsData.vbat = 255,
        sensorsData.motionByte = 1,
        sensorsData.hit = 255,
        sensorsData.pressure = 98,
        sensorsData.heartRate = 99,
        sensorsData.mox = 100,

        valid = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],

        slowTagsDetected = 0,
        */
    0xAF, 0x31, 0x9E, 0x31, 0xB5
    0x00, 0x0D, 0x7C, 0xFD, 0x5D, 0x4B, 0xAF, 0x31
    0x9E, 0x00, 0x05, 0x01, 0x40, 0xE2, 0x01, 0x00
    0x4D, 0x59, 0x5F, 0x54, 0x41, 0x47, 0x00, 0x00
    0x00, 0x41, 0x42, 0x43, 0x05, 0x00, 0x00, 0x16
    0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0xFF, 0x01
    0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06
    0x09, 0x0A, 0x0B, 0xFF, 0x01, 0xFF, 0x62, 0x63
    0x64, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
    0x00
 };

Frame with administration data transmitted from the connected anchor to the PC periodically - at most one frame per cycle, used to synchronize device database with the host. The database contains basic information about anchor and tags seen in the network.
Most of the time anchor provides information about devices that have their administration data changed. Periodically it enters full DDM synchronization mode in which it dumps all available record in interleaved mode with nominal priority entries. This method allows to eventually reconstruct anchor database in host application regardless of the moment that the ACP connection was created.
Slow update rate devices (those not active in every cycle) are not part of the core data structure, but instead there are appended at the end in form of a variable size list with basic device properties.

Protocol_AdminDistributionInfo_t fields

Byte Storage Field Description
0-2 3 bytes (array uint8) did Three bytes long Device (Tag, or Anchor) ID
3-4 2 bytes (uint16) cycleId ID of the cycle in which the data was sent
5 1 byte (uint8) config.fwVersionMajor Firmware version major number
6 1 byte (uint8) config.fwVersionMinor Firmware version minor number
7 1 byte (uint8) config.hwid Hardware ID with board type (6 bits) and chip type (highest 2 bits)
8-13 6 bytes (array uint8) config.mac 48b MAC Address
14 1 byte (uint8) config.cycle.deviceOffset Cycle offset for low speed tags
15 1 byte (uint8) config.cycle.frequency Device frequency correlating to number of ToF values provided:
- 0 - unknown
- 1 - 25Hz
- 2 - 50Hz
- 3 - 75Hz
- 4 - 100Hz
- 5 - 125Hz
- 6 - 150Hz
16 1 byte (uint8) config.cycle.seat Primary seat assigned to device, that is the first seat used by the device in a cycle.
17-20 4 bytes (array uint8) config.serialNumber 4 Byte Serial number
21-29 9 bytes (array char) config.name Device name string
30-32 3 bytes (array char) config.group Device group name string
33-34 2 bytes (uint16) config.licenseConfig License configuration, applicable to display tags only:
- bits 15:14 - failed activation attempts count (0-3)
- bits 13:0 - total successful license activation count
35 1 byte (uint8) config.bt.txPowerPreset Bluetooth TX power preset, see here
36 1 byte (uint8) config.uwb.txPowerPreset UWB TX power in 0.5dB step in 0-30.5dB range.
37 1 byte (uint8) config.boardConfig Board config
38-39 2 bytes (uint16) config.licenseTime Remaining device license time in ticks (1 tick = 5 minutes), applicable to display tags only
40 1 byte (uint8) config.seatPlanVersion Version number of currently used seat plan. 0 Indicates default seat plan hardcoded in device firmware. Other value is provided by user during seat plan modification.
41 1 byte (uint8) calibration.uwb.ch3AntennaDelay UWB Antenna delay for UWB channel 1-3 - offset from default value.
42 1 byte (uint8) calibration.uwb.ch5AntennaDelay UWB Antenna delay for UWB channel 5 - offset from default value.
43 1 byte (uint8) calibration.pressure.bias Sensor calibration value which is substracted from the measured pressure value.
44-45 2 byte (uint16) calibration.magnetometer.bias.x Sensor calibration constant
46-47 2 byte (uint16) calibration.magnetometer.bias.y Sensor calibration constant
48-49 2 byte (uint16) calibration.magnetometer.bias.z Sensor calibration constant
50 1 byte (uint8) calibration.magnetometer.scalar.x Sensor calibration constant
51 1 byte (uint8) calibration.magnetometer.scalar.y Sensor calibration constant
52 1 byte (uint8) calibration.magnetometer.scalar.z Sensor calibration constant
53 1 byte (uint8) calibration.imu.accelerometer.x Sensor calibration constant
54 1 byte (uint8) calibration.imu.accelerometer.y Sensor calibration constant
55 1 byte (uint8) calibration.imu.accelerometer.z Sensor calibration constant
56 1 byte (uint8) sensorsData.vbat Battery voltage value encoded using following format:
- minimum (binary 0) = 2754mV
- maximum (binary 255) = 4250mV
- resolution = ~5.84mV
57 1 byte (uint8) sensorsData.motionByte Motion byte characterizing device movement
58 1 byte (uint8) sensorsData.hit Impact hit detection in milliseconds relative to the start of the cycle. NOTE: It is "last known" impact value.
59-67 9 bytes (array uint8) sensorsData.platform Container for platform specific sensor measurements, see Platform specific Sensor Data table below for more information.
68-75 8 bytes (array uint8) valid Indicates which packet fields are valid. Each bit correlates to single single packet field starting from offset 5 (fwVersionMajor), i.e:
- valid[0]:bit0 -> field @ 5 -> fwVersionMajor
- valid[0]:bit1 -> field @ 6 -> fwVersionMinor
- valid[0]:bit7 -> field @ 12 -> mac[4]
- valid[1]:bit0 -> field @ 13 -> mac[5]
All fields without corresponding valid bit set should be treated as not available. Depending on the device type they may be updated at some later time when the anchor receives proper data over the internal system network.
76 1 byte (uint8) slowTagsDetected Number of detected slow update rate tags in cycle cycleId

Protocol_AdminDistributionInfo_SlowTag_t fields

Byte Storage Field Description
0-2 3 bytes (array uint8) did Three bytes long Device ID
3 1 byte (uint8) seat Primary seat assigned to device, that is the first seat used by the device in a cycle.
4 1 byte (uint8) vbat Battery voltage value encoded using following format:
- minimum (binary 0) = 2754mV
- maximum (binary 255) = 4250mV
- resolution = ~5.84mV
5 1 byte (uint8) hwid Hardware ID with board and chip types.
6 1 byte (uint8) motionByte Motion byte characterizing device movement

Platform specific Sensor Data

Each type of platform in the system provides some custom sensor readings that are stored in the same place inside Admin Distribution record.

Display Tag

Byte Storage Field Description
0 1 byte (uint8) heartRate Heart Rate sensor value - pulse in BPM.
1-3 3 bytes (struct) mox Muscle Oxygenation sensor value. First 12 bits of the structure refer to raw ADC reading from first sensor, last 12 bits - raw ADC reading from the second sensor.
ADC is configured to 12 bit resolution, Vref = 0.6 V, input gain = 1/6. Measured voltage can be calculated as:
V = (reading * Vref)/(2^resolution * gain)
Which, for given values, simplifies to:
V = (reading * 3.6) / 4096
3-8 - Reserved for future use -

Ball Tag

Byte Storage Field Description
0 1 byte (uint8) pressure Pressure sensor value encoded using following format:
- minimum (binary 0) = atmospheric pressure
- maximum (binary 255) = 1.1 Bar above atmospheric pressure
- resolution = ~4.3mBar
Presented value already takes into account pressureCalibration value, so no additional calculations are required.
1-8 - Reserved for future use -

Anchor

Byte Storage Field Description
0-8 - Reserved for future use -

0x04 - Compressed sensor data

C Structure format

typedef struct Protocol_Sensors_s
{
    uint8_t did[3];
    uint16_t cycleId;
    uint8_t slotId;
    uint8_t data[];
} Protocol_Sensors_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 4, length = 106 */
    0x04, 0x6a, 0x00, 
    /* Value buffer 
        did = [10, 20, 30],
        cycleId = 10000,
        slotId = 50
        data = <numbers form 0-99 - AKA dummy bytes>
    */
    0x0a, 0x14, 0x1e, 0x10, 0x27, 0x32, 0x00, 0x01, 
    0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 
    0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 
    0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 
    0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 
    0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 
    0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 
    0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 
    0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 
    0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 
    0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 
    0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 
    0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 
    0x62, 0x63, 
};

Frame transmitted from the device (Master Anchor) to the PC after reception of TRESPONSE frame form device. Frame contains compressed data form sensor. Compressed data format can be used to optimize communication interface capacity, and reduce CPU usage on anchor, because in this case anchor just copies data received from other devices - no extra processing needed.

Byte Storage Field Description
0-2 3 bytes (array uint8) did Three bytes long Tag ID
3-4 2 bytes (uint16) cycleId ID of the cycle in which the data was sent
5 1 byte (uint8) slotId ID of the slot (seat) in which data was sent
6+ n bytes (array uint8) data Compressed (raw) data transmitted from the device. Field has variable size

0x05 - Accelerometer sensor data

C Structure format

typedef struct Protocol_Sensors_s
{
    uint8_t did[3];
    uint16_t cycleId;
    uint8_t slotId;
    uint8_t data[];
} Protocol_Sensors_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 5, length = 114 */
    0x05, 0x72, 0x00, 

    /* Value buffer 
        did = [10, 20, 30],
        cycleId = 1234,
        slotId = 77
    */
    0x0a, 0x14, 0x1e, 0xd2, 0x04, 0x4d, 

    /* data = Bit-Stitched 18b accelerometer samples */
    0x1c, 0xf4, 
    0x67, 0x24, 0xe0, 0x57, 0x80, 0x04, 0xfd, 0x1f, 
    0x09, 0x4c, 0x16, 0x80, 0x41, 0x7f, 0x46, 0x02, 
    0x8c, 0x05, 0x18, 0xd0, 0xbf, 0x8e, 0xc0, 0x6e, 
    0x01, 0x0d, 0xf4, 0x5b, 0x23, 0xe0, 0x5a, 0xc0, 
    0xfc, 0xfc, 0xde, 0x08, 0xfc, 0x16, 0x00, 0xb0, 
    0x22, 0x02, 0x80, 0xa3, 0xff, 0xf9, 0xff, 0xd7, 
    0xff, 0x1f, 0x4e, 0x79, 0xc9, 0x90, 0xfb, 0x03, 
    0xe8, 0x9d, 0x0f, 0x07, 0xfd, 0x19, 0x09, 0xf8, 
    0x15, 0x20, 0x41, 0xff, 0x47, 0x02, 0x93, 0x05, 
    0x60, 0xd0, 0x9f, 0x91, 0x00, 0x63, 0x01, 0x06, 
    0xf4, 0xaf, 0x23, 0xb0, 0x5b, 0x40, 0x03, 0xfd, 
    0xd6, 0x08, 0xb8, 0x16, 0x30, 0x3f, 0xbf, 0x37, 
    0x02, 0xbf, 0x05, 0x34, 0xd0, 0x6f, 0x8d, 0x80, 
    0x6b, 0x01, 
};

/* Samples used in example above */
static const int32_t samples[] = {
    -3044,      2329,        1406,
    -3054,      2335,        1427,
    -3048,      2329,        1420,
    -3066,      2283,        1467,
    -3059,      2262,        1454,
    -3085,      2270,        1471,
    -120064,    -131064,     130979,
    131070,     131069,      124216,
    -28471,     -130818,     63966,
    -3044,      2329,        1406,
    -3054,      2335,        1427,
    -3048,      2329,        1420,
    -3066,      2283,        1467,
    -3059,      2262,        1454,
    -3085,      2270,        1471,
    -3059,      2262,        1454,
};

Message contains packed accelerometer samples collected by the source device in cycle previous to the one at which data is received. Frame transmitted from the device (Master Anchor) to the PC after reception of tag response frame form the source device.

Byte Storage Field Description
0-2 3 bytes (array uint8) did Three bytes long Tag ID
3-4 2 bytes (uint16) cycleId ID of the cycle in which the data was sent
5 1 byte (uint8) slotId ID of the slot (seat) in which data was sent
6+ n bytes (array uint8) data Bit-stitched accelerometer samples

Data array contains bit-stitched set of accelerometer samples. Each sample is 18 bit signed int. Bit-stitching was implemented to increase the capacity of communication interface. It reduces number of unused bits to minimum.

Accelerometer data format:

IMU Bit Stiching

16 Samples
7 Samples

0x06 - Gyroscope sensor data

C Structure format

typedef struct Protocol_Sensors_s
{
    uint8_t did[3];
    uint16_t cycleId;
    uint8_t slotId;
    uint8_t data[];
} Protocol_Sensors_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 6, length = 120 */
    0x06, 0x78, 0x00, 
    /* Value buffer 
        did = [10, 20, 30],
        cycleId = 4321,
        slotId = 77
    */
    0x0a, 0x14, 0x1e, 0xe1, 0x10, 0x4d, 
    /* Bit-stitched 19b gyroscope samples with 8b header */
    0x79, 0xfd, 
    0xdf, 0xf8, 0xff, 0x6a, 0x00, 0x66, 0xfd, 0xbf, 
    0xf1, 0x7f, 0x89, 0x00, 0x28, 0xfa, 0x7f, 0xe3, 
    0xff, 0xab, 0x01, 0x90, 0xf9, 0x7f, 0xd6, 0xff, 
    0x45, 0x02, 0xa0, 0xe7, 0x7f, 0xa2, 0xff, 0x8b, 
    0x04, 0x60, 0xe3, 0xff, 0x05, 0xff, 0xef, 0x0b, 
    0x80, 0xa7, 0xff, 0x37, 0xfe, 0xdf, 0x17, 0x00, 
    0x64, 0xff, 0x6f, 0xfc, 0x5f, 0x22, 0x00, 0x59, 
    0xff, 0x97, 0xf9, 0xbf, 0x48, 0x00, 0x0a, 0xfe, 
    0xbf, 0xf1, 0xff, 0xbe, 0x00, 0x6c, 0xfc, 0x7f, 
    0xd6, 0xff, 0x12, 0x01, 0xd8, 0xf8, 0xbf, 0xa2, 
    0xff, 0x57, 0x03, 0x40, 0xf4, 0x7f, 0x3d, 0xff, 
    0x8b, 0x04, 0x60, 0xe3, 0xff, 0x9e, 0xfe, 0xef, 
    0x0b, 0xc0, 0xc6, 0xff, 0xf3, 0xfa, 0xbf, 0x1a, 
    0x80, 0x8d, 0xff, 0x23, 0xfb, 0x5f, 0x22, 0x00, 
};

/* Samples used in example above */
static const int32_t samples[] = {
    -647, -229, 427,
    -333, -229, 274,
    -374, -229, 427,
    -206, -167, 290,
    -390, -188, 290,
    -229, -251, 381,
    -354, -229, 381,
    -312, -229, 274,
    -167, -206, 290,
    -251, -229, 381,
    -229, -333, 274,
    -229, -374, 427,
    -188, -390, 290,
    -229, -354, 381,
    -229, -647, 427,
    -229, -312, 274
};

Message contains packed gyroscope samples collected by the source device in cycle previous to the one at which data is received. Frame transmitted from the device (Master Anchor) to the PC after reception of tag response frame form the source device.

Byte Storage Field Description
0-2 3 bytes (array uint8) did Three bytes long Tag ID
3-4 2 bytes (uint16) cycleId ID of the cycle in which the data was sent
5 1 byte (uint8) slotId ID of the slot (seat) in which data was sent
6+ n bytes (array uint8) data Bit-stitched 19b gyroscope samples

Data array contains bit-stitched set of gyroscope samples. Each sample is 19 bit signed int. Bit-stitching was implemented to increase the capacity of communication interface. It reduces number of unused bits to minimum.

Gyroscope data format:

IMU Bit Stiching

16 Samples
7 Samples

0x07 - Magnetometer sensor data

C Structure format

typedef struct Protocol_Sensors_s
{
    uint8_t did[3];
    uint16_t cycleId;
    uint8_t slotId;
    uint8_t data[];
} Protocol_Sensors_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 7, length = 72 */
    0x07, 0x48, 0x00, 
    /* Value buffer 
        did = [10, 20, 30],
        cycleId = 1111,
        slotId = 33
    */
    0x0a, 0x14, 0x1e, 0x57, 0x04, 0x21, 
    /* Bit-stitched 11b magnetometer samples with 8b header */
    0x08, 0xef, 
    0xed, 0x95, 0xfb, 0xcd, 0x5b, 0x29, 0xfb, 0xfb, 
    0xb7, 0x53, 0xf6, 0xf7, 0x6f, 0xa1, 0xfc, 0xef, 
    0xde, 0x42, 0x19, 0xe1, 0xbe, 0x9d, 0x72, 0xbf, 
    0x79, 0x7b, 0xe5, 0x7f, 0xfb, 0x16, 0xca, 0xfd, 
    0xfe, 0xad, 0x94, 0xfd, 0xcd, 0xdb, 0x2b, 0xff, 
    0xfb, 0xb7, 0x50, 0x46, 0x78, 0x6f, 0xa7, 0xdc, 
    0x6f, 0xdf, 0x4a, 0xd9, 0xdf, 0xbf, 0xbd, 0x32, 
    0xc2, 0x79, 0x0b, 0x65, 0x7f, 0xfb, 0x76, 0xca,
};

/* Samples used in example above */
static const int32_t samples[] = {
    -248, -579, -425,
    -259, -580, -430,
    -258, -577, -429,
    -258, -577, -432,
    -257, -579, -432,
    -248, -578, -429,
    -259, -580, -425,
    -257, -578, -432,
    -259, -577, -430,
    -258, -580, -425,
    -257, -577, -432,
    -248, -579, -429,
    -259, -578, -430,
    -258, -577, -425,
    -248, -580, -432,
    -258, -578, -429
};

Message contains packed magnetometer samples collected by the source device in cycle previous to the one at which data is received. Frame transmitted from the device (Master Anchor) to the PC after reception of tag response frame form the source device.

Byte Storage Field Description
0-2 3 bytes (array uint8) did Three bytes long Tag ID
3-4 2 bytes (uint16) cycleId ID of the cycle in which the data was sent
5 1 byte (uint8) slotId ID of the slot (seat) in which data was sent
6+ n bytes (array uint8) data Bit-stitched 11b magnetometer samples

Data array contains bit-stitched set of magnetometer samples. Each sample is 11 bit signed int. Bit-stitching was implemented to increase the capacity of communication interface. It reduces number of unused bits to minimum.

Magnetometer data format:

IMU Bit Stiching

16 Samples
7 Samples

0x08 - Impact sensor data

typedef struct Protocol_Sensors_s
{
    uint8_t did[3];
    uint16_t cycleId;
    uint8_t slotId;
    uint8_t data[];
} Protocol_Sensors_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 7, length = 72 */
    0x08, 0x48, 0x00, 
    /* Value buffer 
        did = [10, 20, 30],
        cycleId = 1111,
        slotId = 33
    */
    0x0a, 0x14, 0x1e, 0x57, 0x04, 0x21, 
    /* Impact Data:
        ht - Hit Type = 1 (Full)
        hpos - Hit Position = 32 (ms)
        hval - Hit Vector = 0xBBAA -> 48042
    */
    0x41, 0xAA, 0xBB,
};

Message contains impact event description data determined by the source device in cycle previous to the one at which data is received. Frame transmitted from the device (Master Anchor) to the PC after reception of tag response frame form the source device.

Byte Storage Field Description
0-2 3 bytes (array uint8) did Three bytes long Tag ID
3-4 2 bytes (uint16) cycleId ID of the cycle in which the data was sent
5 1 byte (uint8) slotId ID of the slot (seat) in which data was sent
6 (bit 0) 1 bit ht Type of the hit descriptor: 1 = Full 16-bit hit vector value, 0 = Reduced 8-bit hit vector value
6 (bits 1:7) 7 bits hpos Hit position - offset of impact detection in milliseconds since the cycle beginning
7-(8) 1 or 2 bytes hval Hit vector value - accelerometer output vector length for the sample collected at the moment of impact. Full measurement resolution is 64G, unit value depends on underlying type width (8 or 16-bits).

0x80 - Command Set Serial

C Structure format

typedef struct SM_Protocol_CommandSetSerial_s
{
    uint8_t did[3];
    uint32_t serial;

    uint8_t padding[5];

} SM_Protocol_CommandSetSerial_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 128, length = 12 */
    0x80, 0x0c, 0x00, 
    /* Value buffer */

    /* Device 0:
    did = [1, 2, 3],
    serial = 0x3412CDAB
    */
    0x01, 0x02, 0x03, 0xAB, 0xCD, 0x12, 0x34, 

    /* Padding
    */
    0x00, 0x00, 0x00, 0x00, 0x00, 
};

Frame transmitted from the PC to the device (Master Anchor) to change device serial number.

Byte Storage Field Description
0-2 3 bytes (array uint8) did Three bytes long Node ID
3-6 4 bytes (uint32) serial Serial number
7-11 5 bytes (array uint8) padding Command padding, unused

0x81 - Command Set Group

C Structure format

typedef struct SM_Protocol_CommandSetGroup_s
{
    struct
    {
        uint8_t did[3];
        uint8_t group[3];

    } devices[2];

} SM_Protocol_CommandSetGroup_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 129, length = 12 */
    0x81, 0x0c, 0x00, 
    /* Value buffer */

    /* Device 0:
    did 0 = [1, 2, 3],
    group 0 = 'ABC'
    */
    0x01, 0x02, 0x03, 0x41, 0x42, 0x43, 
    /* Device 1:
    did 1 = [255, 254, 253],
    group 1 = 'DEF'
    */
    0xFF, 0xFE, 0xFD, 0x44, 0x45, 0x46,
};

Frame transmitted from the PC to the device (Master Anchor) to change device logical group name.

Byte Storage Field Description
0-2 3 bytes (array uint8) did 0 Three bytes long Node ID
3-5 3 bytes (array uint8) group 0 Device group name, UTF-8 encoded, NULL termination not required
6-8 3 bytes (array uint8) did 1 Three bytes long Node ID
9-11 3 bytes (array uint8) group 1 Device group name, UTF-8 encoded, NULL termination not required

0x82 - Command Set Name

C Structure format

typedef struct SM_Protocol_CommandSetName_s
{
    uint8_t did[3];
    uint8_t name[9];

} SM_Protocol_CommandSetName_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 130, length = 12 */
    0x82, 0x0c, 0x00, 
    /* Value buffer */

    /* Device 0:
    did = [1, 2, 3],
    name = 'ABCDEF'
    */
    0x01, 0x02, 0x03, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x00, 0x00, 0x00, 
};

Frame transmitted from the PC to the device (Master Anchor) to change device logical name.

Byte Storage Field Description
0-2 3 bytes (array uint8) did Three bytes long Node ID
3-11 9 bytes (array uint8) name Device logical name, UTF-8 encoded, NULL termination not required

0x83 - Command Set IMU Calibration

C Structure format

typedef struct SM_Protocol_CommandSetCalibrationIMU_s
{
    struct
    {
        uint8_t did[3];

        /** Accelerometer zero offset compensation */
        uint8_t accelerometerBias[3];

    } devices[2];

} SM_Protocol_CommandSetCalibrationIMU_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 131, length = 12 */
    0x83, 0x0c, 0x00, 
    /* Value buffer */

    /* Device 0:
    did1 = [1, 2, 3],
    accelerometerBias = [4, 5, 6]
    */
    0x01, 0x02, 0x03, 0x04, 0x05, 0x06,

    /* Device 1:
    did2 = [7, 8, 9],
    accelerometerBias = [10, 11, 12]
    */
    0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,
};

Frame transmitted from the PC to the device (Master Anchor) to upload IMU calibration constants. The coefficients are not used during signal acquisition. This is just a storage than can be used by software to store and retrieve calibration data unique for the device.

Byte Storage Field Description
0-2 3 bytes (array uint8) did1 Three bytes long Node ID
3-5 3 bytes (array uint8) accelerometer bias Accelerometer bias calibration constants per X, Y, Z axis
6-8 3 bytes (array uint8) did2 Three bytes long Node ID
9-11 3 bytes (array uint8) accelerometer bias Accelerometer bias calibration constants per X, Y, Z axis

0x84 - Command Set Message

C Structure format

typedef struct SM_Protocol_CommandSetMessage_s
{
    uint8_t did[3];
    uint8_t message[9];

} SM_Protocol_CommandSetMessage_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 132, length = 12 */
    0x84, 0x0c, 0x00, 
    /* Value buffer */

    /* Device 0:
    did = [1, 2, 3],
    message = 'ABCDEF'
    */
    0x01, 0x02, 0x03, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x00, 0x00, 0x00, 
};

Frame transmitted from the PC to the device (Master Anchor) to notify target display tag with custom short message.

Byte Storage Field Description
0-2 3 bytes (array uint8) did Three bytes long Node ID
3-11 9 bytes (array uint8) message Custom text message, UTF-8 encoded, NULL termination not required

0x85 - Command Update License

C Structure format

typedef struct SM_Protocol_CommandUpdateLicense_s
{
    SM_Protocol_DID_t did;

    uint8_t licenseKey[8];
    int8_t tickUpdate;

} SM_Protocol_CommandUpdateLicense_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 133, length = 12 */
    0x85, 0x0c, 0x00, 
    /* Value buffer */

    /* Device 0:
    did = [1, 2, 3],
    licenseKey = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x01, 0x02],
    tickUpdate = 10,
    */
    0x01, 0x02, 0x03, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x01, 0x02, 0x0A, 
};

Frame transmitted from the PC to the device (Master Anchor) to update high update frequency license period for selected display tag.

Byte Storage Field Description
0-2 3 bytes (array uint8) did Three bytes long Node ID
3-10 8 bytes (array uint8) licenseKey License key used to authorize update request
11 1 byte tickUpdate Number of license ticks add or removed from the target device

0x86 - Command Set Magnetometer Calibration

C Structure format

typedef struct SM_Protocol_CommandSetCalibrationMagnetometer_s
{
    uint8_t did[3];

    struct
    {
        /** Magnetometer zero offset compensation */
        int16_t magnetometerBias[3];
        /** Magnetometer gain skew compensation */
        uint8_t magnetometerScalar[3];
    } calibration;

} SM_Protocol_CommandSetCalibrationMagnetometer_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 134, length = 12 */
    0x86, 0x0c, 0x00, 
    /* Value buffer */

    /* Device 0:
    did = [1, 2, 3],
    magnetometer bias = [4, 5, 6]
    magnetometer scalar = [10, 11, 12]
    */
    0x01, 0x02, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x0A, 0x0B, 0x0C
};

Frame transmitted from the PC to the device (Master Anchor) to upload Magnetometer calibration constants. The coefficients are not used during signal acquisition. This is just a storage than can be used by software to store and retrieve calibration data unique for the device.

Byte Storage Field Description
0-2 3 bytes (array uint8) did Three bytes long Node ID
3-8 6 bytes (array uint16) magnetometer bias Magnetometer bias calibration constants per X, Y, Z axis
9-11 3 bytes (array uint8) magnetometer scalar Magnetometer scalar/gain calibration constants per X, Y, Z axis

0x87 - Command Set Heart Rate MAC

C Structure format

typedef struct SM_Protocol_CommandSetHeartRateMAC_s
{
    uint8_t did[3];

    /** MAC address for the paired HR belt, ordered from the MSB */
    uint8_t mac[6];

    /** Unused command padding */
    uint8_t padding[3];

} SM_Protocol_CommandSetHeartRateMAC_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 135, length = 12 */
    0x87, 0x0c, 0x00, 
    /* Value buffer */

    /* Device 0:
    did = [1, 2, 3],
    mac = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]
    padding = [0, 0, 0]
    */
    0x01, 0x02, 0x03, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x00, 0x00
};

Frame transmitted from the PC to the device (Master Anchor) to set Heart Rate belt MAC address that will be monitored for BPM by the target Tag Device. Settings will be synced (saved in non-volatile memory) 3000ms after processing of the command.

Byte Storage Field Description
0-2 3 bytes (array uint8) did Three bytes long Node ID
3-8 6 bytes (array uint8) mac Heart Rate belt MAC address
9-11 3 bytes (array uint8) padding Unused command padding

0x88 - Command Set BLE Config

C Structure format

typedef struct SM_Protocol_CommandSetBLEConfig_s
{
    struct 
    {
        uint8_t did[3];
        uint8_t txPower;
    } device[3];

} SM_Protocol_CommandSetBLEConfig_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 136, length = 12 */
    0x88, 0x0c, 0x00, 
    /* Value buffer */

    /* Device 0:
    did 0 = [1, 2, 3],
    txPower = 1
    */
    0x01, 0x02, 0x03, 0x01, 

    /* Device 1:
    did 0 = [16, 32, 48],
    txPower = 1
    */
    0x10, 0x20, 0x30, 0x01, 

    /* Device 2:
    did 0 = [0, 0, 0],
    txPower = 0
    */
    0x10, 0x20, 0x30, 0x01, 
};

Frame transmitted from the PC to the device (Master Anchor) to change BLE configuration. BLE settings will be synced (saved in non-volatile memory) 3000ms after processing of the command.

Byte Storage Field Description
0-2 3 bytes (array uint8) did 0 Three bytes long Node ID
3 1 byte (uint8) txPower 0 BLE TX power profile encoded using following format:
- 0 = -20 dB
- 1 = -16 dB
- 2 = -12 dB
- 3 = -8 dB
- 4 = -4 dB
- 5 = 0 dB
- 6 = 4 dB (only nRF52)
- 7 = 8 dB (only nRF52)
4-6 3 bytes (array uint8) did 1 Three bytes long Node ID
7 1 byte (uint8) txPower 1 BLE TX power profile encoded using following format:
- 0 = -20 dB
- 1 = -16 dB
- 2 = -12 dB
- 3 = -8 dB
- 4 = -4 dB
- 5 = 0 dB
- 6 = 4 dB (only nRF52)
- 7 = 8 dB (only nRF52)
8-10 3 bytes (array uint8) did 2 Three bytes long Node ID
11 1 byte (uint8) txPower 2 BLE TX power profile encoded using following format:
- 0 = -20 dB
- 1 = -16 dB
- 2 = -12 dB
- 3 = -8 dB
- 4 = -4 dB
- 5 = 0 dB
- 6 = 4 dB (only nRF52)
- 7 = 8 dB (only nRF52)

0x8B - Command Set UWB Config

C Structure format

typedef struct SM_Protocol_CommandSetUWBConfig_s
{
    struct 
    {
        uint8_t did[3];
        int8_t ch3;
        int8_t ch5;
        uint8_t txPower;
    } device[2];

} SM_Protocol_CommandSetUWBConfig_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 139, length = 12 */
    0x8b, 0x0c, 0x00, 
    /* Value buffer */

    /* Device 0:
    did 0 = [1, 2, 3],
    ch3 = -5
    ch5 = 5
    txPower = 1
    */
    0x01, 0x02, 0x03, 0xfb, 0x05, 0x01, 

    /* Device 1:
    did 0 = [16, 32, 48],
    seat = 1 
    ch3 = 10
    ch5 = -10
    txPower = 1
    */
    0x10, 0x20, 0x30, 0x0a, 0xf6, 0x01, 
};

Frame transmitted from the PC to the device (Master Anchor) to change UWB configuration.

Byte Storage Field Description
0-2 3 bytes (array uint8) did 0 Three bytes long Node ID
3 1 byte (int8) ch3 0 Antenna delay offset from default value (16456) for UWB channels 1-3
4 1 byte (int8) ch5 0 Antenna delay offset from default value (16456) for UWB channel 5
5 1 byte (uint8) txPower 0 UWB TX power profile encoded using following format:
- minimum: 0 = 0 dB
- maximum: 61 = 30.5 dB
- resolution = 0.5 dB
Values over 61 are not allowed.
6-8 3 bytes (array uint8) did 1 Three bytes long Node ID
9 1 byte (int8) ch3 1 Antenna delay offset from default value (16456) for UWB channels 1-3
10 1 byte (int8) ch5 1 Antenna delay offset from default value (16456) for UWB channel 5
11 1 byte (uint8) txPower 1 UWB TX power profile encoded using following format:
- minimum: 0 = 0 dB
- maximum: 61 = 30.5 dB
- resolution = 0.5 dB
Values over 61 are not allowed.

0x8C - Command Shutdown (kill)

C Structure format

typedef struct  SM_Protocol_CommandShutdown_s
{
    struct
    {
        uint8_t did[3];
    } device[4];

} SM_Protocol_CommandShutdown_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 140, length = 12 */
    0x8c, 0x0c, 0x00, 
    /* Value buffer 
        did 0 = [1, 2, 3],
        did 1 = [0, 0, 0], (unused)
        did 2 = [33, 34, 35],
        did 3 = [18, 34, 50],
    */
    0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x21, 0x22, 
    0x23, 0x12, 0x22, 0x32, 
};

Frame transmitted from the PC to the device (Master Anchor). The request is used to power-off one or many network devices. The target node must be seen by the Master Anchor connected to the software system. Unused devices fields should be set to invalid ID: [0x00, 0x00, 0x00].

Byte Storage Field Description
0-2 3 bytes (array uint8) did 0 Three bytes long Node ID
3-5 3 bytes (array uint8) did 1 Three bytes long Node ID
6-8 3 bytes (array uint8) did 2 Three bytes long Node ID
9-11 3 bytes (array uint8) did 3 Three bytes long Node ID

0x8D - Command Set Board Config

C Structure format

typedef struct  SM_Protocol_CommandSetBoardConfig_s
{
    struct
    {
        uint8_t did[3];
        uint8_t boardConfig;

    } device[3];

} SM_Protocol_CommandSetBoardConfig_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 141, length = 12 */
    0x8d, 0x0c, 0x00, 
    /* Value buffer 
        did 0 = [1, 2, 3], board config: 0x01
        did 1 = [33, 34, 35], board config: 0xAA
        did 2 = [0, 0, 0] (unused)
    */
    0x01, 0x02, 0x03, 0x01,
    0x21, 0x22, 0x23, 0xAA,
    0x00, 0x00, 0x00, 0x00,
};

Frame transmitted from the PC to the device (Master Anchor). The request is used to change board configuration parameters of one or many network devices. The target node must be seen by the Master Anchor connected to the software system. Unused devices fields should be set to invalid ID: [0x00, 0x00, 0x00].

Byte Storage Field Description
0-2 3 bytes (array uint8) did 0 Three bytes long Node ID
3 1 byte (bitmask) board config 0 Board configuration
4-6 3 bytes (array uint8) did 1 Three bytes long Node ID
7 1 byte (bitmask) board config 1 Board configuration
8-10 3 bytes (array uint8) did 2 Three bytes long Node ID
11 1 byte (bitmask) board config 2 Board configuration

Board configuration encoding:

Bit Field Description
0 imu on/off Controls whether the tag outputs IMU measurement data stream, 0: off, 1: on. When disabled no samples are reported with ACP types 4-8 from the node. Movement indicators like motion byte and hit detection remain functional regardless of the configuration.
1 sleep on/off Controls whether the device can enter sleep mode, comletely suspending its operation in UWB network, 0: off, 1: on. Sleep entry can be caused by:
- a tag remaining idle (not moving) for some time
- a tag not detecting UWB network presence
- a field anchor detecting itself as no longer in upright position
2 sleep shallow on/off Controls whether the tag can enter shallow sleep mode, temporarily suspending its operation in UWB network when remaining idle for short time, 0: off, 1: on. The tag will provide data on existing seat but with noticeable breaks used to improve device battery lifetime.
3 wake charging on/off Controls whether the device when put into sleep mode and actively charging can be enabled using Wake On Motion functionality, 0: off, 1: on.
4 haptic on/off Controls whether haptic vibration notifications are generated by a display tag on some events (e.g. message reception), 0: off, 1: on.

0x8E - Command Set Network Configuration

C Structure format

typedef struct SM_Protocol_CommandSetNetworkDeviceConfig_s
{
    struct
    {
        uint8_t did[3];
        uint8_t seat;
        uint8_t frequency;

        struct
        {
            uint8_t allocatorType : 2; /** bits 0:1 Allocator type:
                                                - 0 Fixed,
                                                - 1 Auto Strict,
                                                - 2 Auto Relaxed */
            uint8_t deviceType : 3;    /** bits 2:4 Device type: 
                                                - 0 Default
                                                - 1 Reserved
                                                - 2 Anchor
                                                - 3 Tag
                                                - 4 Sniffer
                                                - 5 Juggler
                                                - 6-7 Reserved */
            uint8_t anchorSeatPool : 1; /** bits 5 Anchor seat pool to use:
                                                 - 0 standard pool
                                                 - 1 offloadless pool
                                         */
            uint8_t reserved : 2;       /** bits 6:7 For future use */
        } config;

    } device[2];

} SM_Protocol_CommandSetNetworkDeviceConfig_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 142, length = 12 */
    0x8e, 0x0c, 0x00, 
    /* Value buffer */

    /* Device 0:
    did 0 = [1, 2, 3],
    seat = 18,
    frequency = 50Hz (5),
    allocator type = Auto Strict,
    device type = Default
    anchor pool = Standard
    */
    0x01, 0x02, 0x03, 0x12, 0x05, 0x01, 

    /* Device 1:
    did 1 = [16, 32, 48],
    seat = 5,
    frequency = 50Hz (5),
    allocator type = Auto Strict,
    device type = Default
    anchor pool = Offloadless
    */
    0x10, 0x20, 0x30, 0x05, 0x05, 0x21,
};

Frame transmitted from the PC to the device (Master Anchor) to change device network configuration which defines its operational parameters for UWB network visibility. Processing of the command requires receiving nodes to perform system reset in order to apply new configuration.

General rules:

Byte Storage Field Description
0-2 3 bytes (array uint8) did 0 Three bytes long Node ID
3 1 byte (uint8) seat 0 Device seat
4 1 byte (uint8) frequency 0 Device nominal frequency:
- 0 - reserved
- 1 - random
- 2 - very slow
- 3 - slow
- 4 - 25Hz
- 5 - 50Hz
- 6 - 75Hz
- 7 - 100Hz
- 8 - 125Hz
- 9 - 150Hz
- 10 - 175Hz
5 [bits 0-1] 2 bits enum allocator type 0 Seat allocator type used on the device:
- 0 - Fixed/Manual
- 1 - Automatic Strict
- 2 - Automatic Relaxed
5 [bits 2-4] 3 bits enum device type 0 Device type configuration
- 0 - default
- 1 - reserved
- 2 - anchor
- 3 - tag
- 4 - sniffer
- 5 - juggler
5 [bit 5] 1 bit enum anchor pool 0 Anchor seat pool to use:
- 0 - Standard
- 1 - Offloadless
6-8 3 bytes (array uint8) did 1 Three bytes long Node ID
9 1 byte (uint8) seat 1 Device seat
10 1 byte (uint8) frequency 1 Device nominal frequency
11 [bits 0-1] 2 bits enum allocator type 1 Seat allocator type used on the device
11 [bits 2-4] 3 bits enum device type 1 Device type configuration

0x8F - Command Set Random Period

C Structure format

typedef struct SM_Protocol_CommandSetRandomPeriod_s
{
    struct
    {
        uint8_t did[3];
        uint16_t period;

    } device[2];

    uint8_t padding[2];

} SM_Protocol_CommandSetRandomPeriod_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 143, length = 12 */
    0x8f, 0x0c, 0x00, 
    /* Value buffer */

    /* Device 0:
    did 0 = [1, 2, 3],
    period = 1000 seconds
    */
    0x01, 0x02, 0x03, 0xE8, 0x03,

    /* Device 1:
    did 1 = [16, 32, 48],
    period = 32 seconds
    */
    0x10, 0x20, 0x30, 0x20, 0x00,

    /* Padding */
    0x00, 0x00,
};

Frame transmitted from the PC to the device (Master Anchor) to change configuration of target nodes random period option, used to define update rate period when tag is operating with random frequency.

Byte Storage Field Description
0-2 3 bytes (array uint8) did 0 Three bytes long Node ID
3-4 2 bytes (uint16) period 0 Random period in seconds
5-7 3 bytes (array uint8) did 1 Three bytes long Node ID
8-9 2 bytes (uint16) period 1 Random period in seconds
10-11 2 bytes padding Padding bytes with arbitrary values

0x90 - Command Set Network Start Delay

C Structure format

typedef struct SM_Protocol_CommandSetNetworkStartDelay_s
{
    struct
    {
        uint8_t did[3];
        uint8_t delayInSec;

    } device[3];

} SM_Protocol_CommandSetNetworkStartDelay_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 144, length = 12 */
    0x90, 0x0c, 0x00, 
    /* Value buffer */

    /* Device 0:
    did 0 = [1, 2, 3],
    delay = 5 seconds
    */
    0x01, 0x02, 0x03, 0x05,

    /* Device 1:
    did 1 = [16, 32, 48],
    delay = 0 seconds
    */
    0x10, 0x20, 0x30, 0x00,

    /* Device 2:
    did 2 = [0, 0, 0],
    delay = 0 seconds
    */
    0x00, 0x00, 0x00, 0x00,
};

Frame transmitted from the PC to the device (Master Anchor) to change configuration of target nodes network start delay settings option, used to define maximum time an anchor is scanning for network presence before deciding to create a new one.

Byte Storage Field Description
0-2 3 bytes (array uint8) did 0 Three bytes long Node ID
3 byte (uint8) delay 0 Network start delay in seconds
4-6 3 bytes (array uint8) did 1 TNetwork start delay in seconds
7 byte (uint8) delay 1 Random period in seconds
8-10 3 bytes (array uint8) did 2 Network start delay in seconds
11 byte (uint8) delay 2 Random period in seconds

0x93 - Command Display Overlay Command

C Structure format

typedef struct SM_Protocol_CommandDisplayOverlayCommand_s
{
    struct
    {
        /** Device ID */
        uint8_t did[3];

        /** Overlay command */
        uint8_t command;

        /** Overlay command parameters */
        uint8_t parameters[2];

    } device[2];

} SM_Protocol_CommandDisplayOverlayCommand_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 147, length = 12 */
    0x93, 0x0c, 0x00, 
    /* Value buffer */

    /* Device 0:
    did 0 = [1, 2, 3],
    command = 1
    parameters = [4, 5]
    */
    0x01, 0x02, 0x03, 0x01, 0x04, 0x05,

    /* Device 1:
    did 1 = [7, 8, 9],
    command = 2
    parameters = [0, 0]
    */
    0x07, 0x08, 0x09, 0x02, 0x00, 0x00,
};

Frame transmitted from the PC to the device (Master Anchor) to relay command to target display tags, which should execute given overlay commands upon frame reception.

Byte Storage Field Description
0-2 3 bytes (array uint8) did 0 Three bytes long Node ID
3 byte (uint8) command Display overlay command
4-5 2 bytes (array uint8) parameters Display overlay command specific paramaters
6-8 3 bytes (array uint8) did 1 Three bytes long Node ID
9 byte (uint8) command Display overlay command
10-11 2 bytes (array uint8) parameters Display overlay command specific paramaters

Supported commands:

Command Parameter 1 Parameter 2 Description
0: Clear All Push (bool) Unused Clears the working buffer, optionally pushing it to display at the end
1: Clear Area Offset (uint8) Area (uint8) Clears specified fragment of the working buffer
2: Push All Clear (bool) Unused Pushes working buffer to the display, optionally clearing it at the end
3: Push Area Offset (uint8) Area (uint8) Pushes specified fragment of the working buffer to the display
4: Save Default Clear (bool) Unused Saves the working buffer as default overlay, optionally clearing it at the end
5: Load Default Push (bool) Unused Loads default overlay into the working buffer, optionally pushing it to display at the end

Offset encoding:

Bits Value Description
0:3 Row Starting cursor row (0-12)
4:7 Column Starting cursor column (0-15)

Area encoding:

Bits Value Description
0:3 Height (Height-1) of modified area in rows
4:7 Width (Width-1) of modified area in columns

0x94 - Command Display Overlay Update

C Structure format

typedef struct SM_Protocol_DisplayOverlayUpdate_s
{
    /** Device ID */
    uint8_t did[3];

    /** Update offset (starting address) */
    struct
    {
        uint8_t row : 4u;    /** Row Id */
        uint8_t column : 4u; /** Column Id */
    } offset;

    /** Map containing symbol IDs to use for addressed overlay blocks */
    uint8_t symbolMap[8];

} SM_Protocol_CommandDisplayOverlayUpdate_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 148, length = 12 */
    0x94, 0x0c, 0x00, 
    /* Value buffer */

    /* Device 0:
    did = [1, 2, 3],
    row = 2
    column = 3
    map = [4, 5, 6, 7, 8, 9, 10, 11]
    */
    0x01, 0x02, 0x03, 0x32, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
};

Frame transmitted from the PC to the device (Master Anchor) to relay command to target display tags, in order to update content of the overlay working buffer, which contains references to graphic symbols defined inside overlay database.

Byte Storage Field Description
0-2 3 bytes (array uint8) did Three bytes long Node ID
3 @ bits 0:2 byte (uint8) row Starting row for working buffer modification
3 @ bits 3:6 byte (uint8) column Starting column for working buffer modification
4-11 8 bytes (array uint8) map Map of the symbols (their IDs) to render at consecutive overlay blocks

0x95 - Command Display Overlay Upload

C Structure format

typedef struct SM_Protocol_DisplayOverlayUpload_s
{
    /** Device ID */
    uint8_t did[3];

    /** Symbol ID */
    uint8_t id;

    /** Symbol pixel bitmap creating 8x8 graphic */
    uint8_t symbolBitmap[8];

} SM_Protocol_CommandDisplayOverlayUpload_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 149, length = 12 */
    0x95, 0x0c, 0x00, 
    /* Value buffer */

    /* Device 0:
    did = [1, 2, 3],
    id = 64
    symbolBitmap = [0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70]
    */
    0x01, 0x02, 0x03, 0x40, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70,
};

Frame transmitted from the PC to the device (Master Anchor) to relay command to target display tags, in order to upload symbol bitmap to the overlay graphic database.

Byte Storage Field Description
0-2 3 bytes (array uint8) did Three bytes long Node ID
3 byte (uint8) id Symbol ID
4-11 8 bytes (array uint8) symbolBitmap Symbol pixel bitmap creating 8x8 graphic

Bitmap layout example for EK string:

Byte/Bit Bit 0 Bit 1 Bit 2 Bit 3 Bit 4 Bit 5 Bit 6 Bit 7 Bitmap byte
Byte 0 0x97
Byte 1 0x91
Byte 2 0x51
Byte 3 0x37
Byte 4 0x31
Byte 5 0x51
Byte 6 0x91
Byte 7 0x97

0x96 - Command MOx setup

C Structure format

typedef struct SM_Protocol_CommandMoxSetup_s
{
    /** Device ID */
    uint8_t did[3];

    /** PWM value */
    uint8_t pwm;

    /** Unused command padding */
    uint8_t padding[8];

} SM_Protocol_CommandMoxSetup_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 150, length = 12 */
    0x96, 0x0c, 0x00,
    /* Value buffer */

    /* Device 0:
    did = [1, 2, 3],
    pwm = 50,
    padding = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
    */
    0x01, 0x02, 0x03, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};

Frame transmitted from the PC to the device (Master Anchor) to relay command to target display tags, in order to update MOx measurement setup. Currently, one parameter is configurable - PWM duty cycle of the light source.

Byte Storage Field Description
0-2 3 bytes (array uint8) did Three bytes long Node ID
3 byte (uint8) pwm Duty cycle of light source PWM
4-11 8 bytes (array uint8) padding Command padding, unused

0xB4 - Command Set ESP Config 1

C Structure format

typedef struct SM_Protocol_EspControl_Message_Network1_s
{
    SM_Protocol_DID_t did;
    Esp_Settings_Esp_Config_t config;
    uint8_t ipAddress[4];
    uint8_t networkMask[4];

} SM_Protocol_EspControl_Message_Network1_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 180, length = 12 */
    0xb4, 0x0c, 0x00, 
    /* Value buffer 
        did         = [1, 2, 3],
        config      = [3],
        ipAddress   = [192, 168, 1, 10],
        networkMask = [255, 255, 255, 0],
    */
    0x01, 0x02, 0x03, 0x03, 0xc0, 0xa8, 0x01, 0x0a,
    0xff, 0xff, 0xff, 0x00, 
};

Frame transmitted from the PC to the device (Master Anchor) to change configuration of ESP32. This frame consists four parameters: target DID, config bits and IP address and Network mask for Ethernet when static IP mode is used.
Config is a bitfield consisting of following value bits:
- Bit 0 - ESP enabled - if true, ESP is powered on. If false, ESP is disabled
- Bit 1 - DHCP enabled - if true, ESP will obtain Ethernet configuration parameters via DHCP. If false, static configuration will be used. - Bit 2 - WiFi AP enabled - if true, ESP WiFi interface is configured in Access Point mode. If false, WiFi is disabled.

Byte Storage Field Description
0-2 3 bytes (array uint8) did Three bytes long Node ID
3 byte (uint8) config ESP32 config bits
4-7 4 bytes (array uint8) ipAddress ETH IP address
8-11 4 bytes (array uint8) networkMask ETH Network mask

0xB5 - Command Set ESP Config 2

C Structure format

typedef struct SM_Protocol_EspControl_Message_Network2_s
{
    SM_Protocol_DID_t did;
    uint8_t defaultGateway[4];

    /* Padding - currently unused */
    uint8_t padding[5];

} SM_Protocol_EspControl_Message_Network2_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 181, length = 12 */
    0xb5, 0x0c, 0x00, 
    /* Value buffer 
        did            = [1, 2, 3],
        defaultGateway = [192, 168, 1, 1],
        padding        = [0, 0, 0, 0, 0],
    */
    0x01, 0x02, 0x03, 0xc0, 0xa8, 0x01, 0x01,
    0x00, 0x00, 0x00, 0x00, 0x00,
};

Frame transmitted from the PC to the device (Master Anchor) to change configuration of ESP32. This frame consists two parameters: target DID and IP address of default gateway for Ethernet when static IP mode is used.

Byte Storage Field Description
0-2 3 bytes (array uint8) did Three bytes long Node ID
3-6 4 bytes (array uint8) defaultGateway Default Gateway IP address
7-11 5 bytes (array uint8) padding Command padding, unused

0xB6 - Command Set ESP WiFi password 1

C Structure format

typedef struct SM_Protocol_EspControl_Message_WifiPassword_s
{
    SM_Protocol_DID_t did;
    uint8_t password[9];

} SM_Protocol_EspControl_Message_WifiPassword_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 182, length = 12 */
    0xb6, 0x0c, 0x00, 
    /* Value buffer 
        did      = [1, 2, 3],
        password = [112, 97, 115, 115, 119, 111, 114, 100, 49]
    */
    0x01, 0x02, 0x03, 0x70, 0x61, 0x73, 0x73,
    0x77, 0x6f, 0x72, 0x64, 0x31,
};

Frame transmitted from the PC to the device (Master Anchor) to change first nine characters of WiFi password. Maximum allowed password length is limited to 18 (including null terminator), so it is divided in two frames - 0xB6 and 0xB7. Command 0xB6 clears currently saved password and writes nine first characters. Then, if password is longer than 9 characters, second frame (0xB7) must be sent. This frame consists two parameters: target DID and password string.

Byte Storage Field Description
0-2 3 bytes (array uint8) did Three bytes long Node ID
3-11 9 bytes (array uint8) password First 9 characters of password

0xB7 - Command Set ESP WiFi password 2

C Structure format

typedef struct SM_Protocol_EspControl_Message_WifiPassword_s
{
    SM_Protocol_DID_t did;
    uint8_t password[9];

} SM_Protocol_EspControl_Message_WifiPassword_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 183, length = 12 */
    0xb7, 0x0c, 0x00, 
    /* Value buffer 
        did      = [1, 2, 3],
        password = [112, 97, 115, 115, 119, 111, 114, 100, 49]
    */
    0x01, 0x02, 0x03, 0x70, 0x61, 0x73, 0x73,
    0x77, 0x6f, 0x72, 0x64, 0x31,
};

Frame transmitted from the PC to the device (Master Anchor) to change last nine characters of WiFi password. Maximum allowed password length is limited to 18 (including null terminator), so it is divided in two frames - 0xB6 and 0xB7. Command 0xB7 reads currently saved password and changes its nine last characters. This frame consists two parameters: target DID and password string.

Byte Storage Field Description
0-2 3 bytes (array uint8) did Three bytes long Node ID
3-11 9 bytes (array uint8) password Last 9 characters of password

0xC4 - Command Commit Seat Plan

C Structure format

typedef struct SM_Protocol_CommandCommitSeatPlan_s
{
    SM_Protocol_DID_t did;
    uint8_t version;
    uint8_t checksum;

    uint8_t padding[7]; // Command padding - unused

} SM_Protocol_CommandCommitSeatPlan_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 183, length = 12 */
    0xc4, 0x0c, 0x00,
    /* Value buffer
        did      = [1, 2, 3],
        version  = 123,
        checksum = 165,
        padding  = [0, 0, 0, 0, 0, 0, 0]

    */
    0x01, 0x02, 0x03, 0x7b, 0xa5, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00,
};

Frame transmitted from the PC to the device (Master Anchor) to indicate that dynamic seat plan is ready to be used. This frame consists three parameters: target DID, seat plan version and its checksum. The checksum is calculated as a CRC-CCITT-8 sum with initial valiue 0xFF of all frequencies present in the plan. When seat plan was changed and the checksum is correct, calling this command will result in a device reset. On power-up, transferred seat plan will be verified for correctness and, if it is correct, will be used instead of a default one. If there was no change to the seat plan, the reset will be ignored. If provided checksum is not equal to the checksum calculated from the seat plan on the device, no action is performed.
Checksum verification can be disabled by setting checksum value to 0xFF. However, value of 0xFF is also a valid checksum that can occur for certain input data. To avoid this problem, when checksum calculated in the firmware is equal to 0xFF, result is modified by appending frequency value of the 0. slot to the end of the frequency array.

Byte Storage Field Description
0-2 3 bytes (array uint8) did Three bytes long Node ID
3 byte (uint8) version Seat plan version number
4 byte (uint8) checksum Seat plan checksum
5-11 3 bytes (array uint8) padding Command padding - unused

0xC5 - Command Set Seat Plan

C Structure format

typedef struct SM_Protocol_CommandSetSeatPlan_s
{
    SM_Protocol_DID_t did;
    struct
    {
        uint8_t id;
        uint8_t frequency;
    } seatConfig[4];

    uint8_t padding; // Command padding - unused

} SM_Protocol_CommandSetSeatPlan_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 197, length = 12 */
    0xc5, 0x0c, 0x00,
    /* Value buffer
        did = [1, 2, 3]
        Seat config 0:
            id = 1
            freq = 5
        Seat config 1:
            id = 5
            freq = 9
        Seat config 2:
            id = 3
            freq = 4
        Seat config 3:
            id = 20
            freq = 4
        padding = 0
    */
    0x01, 0x02, 0x03, 0x01, 0x05, 0x05, 0x09,
    0x03, 0x04, 0x14, 0x04, 0x00,
};

Frame transmitted from the PC to the device (Master Anchor) to set up to four new seat configs. The frame consists of target DID and four seat configs (position in the seat plan (id) and frequency). If frequency is equal to 0, the entry is omitted.

Byte Storage Field Description
0-2 3 bytes (array uint8) did Three bytes long Node ID
3-10 8 bytes seat config Array of four seat config entries
11 byte (uint8) padding Command padding - unused

The seat plan defines order of tags with different frequencies in the cycle. Each seat in the seat plan is parametrized with its supported frequency. When tag with supported frequency joins the network, the network admninistrator will assign it a seat with this frequency, if free seat is available.
Currently used seat plan consists of 41 seats and covers half of the cycle. Second half of the cycle is the same as the first one.
Tags with frequency 25 Hz use slots only in one half of the cycle, the second is unused. High frequency tags (75 Hz and higher) will occupy more than one seat. There is a defined seat separation between seats of a high frequency tag. The number of occurrences and distance between them depends on the frequency:

Frequency Separation
75 Hz 34
100Hz 16, 18
125Hz 8, 17, 9
150 Hz 8, 8, 9, 9
175Hz 8, 8, 8, 8, 8

For example, if slot number 1 is defined as 75 Hz, slot number 35 also will be set to 75 Hz. If user sets slot number 3 to 125 Hz, also slots 11, 28 and 37 will be set to 125 Hz. Updating slot separation is done automatically in the firmware, so only primary setting must be provided, but user have to remember that settings for separated slots will be overwritten. Also, trying to set high frequency seat in a way that the next separated slots will not fit into the seat plan array will be rejected.

Frequency is an uint8 value that is mapped as follows:

Frequency ID Frequency
0 Unknown
1 Random
2 Very Slow
3 Slow
4 25 Hz
5 50 Hz
6 75 Hz
7 100 Hz
8 125 Hz
9 150 Hz
10 175 Hz

0xC6 - Command Factory Reset

C Structure format

typedef struct SM_Protocol_CommandFactoryReset_s
{
    struct
    {
        SM_Protocol_DID_t did;
    } device[4];

} SM_Protocol_CommandFactoryReset_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 198, length = 12 */
    0xc6, 0x0c, 0x00, 
    /* Value buffer 
        did 0 = [1, 2, 3],
        did 1 = [0, 0, 0], (unused)
        did 2 = [33, 34, 35],
        did 3 = [18, 34, 50],
    */
    0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x21, 0x22, 
    0x23, 0x12, 0x22, 0x32, 
};

Frame transmitted from the PC to the device (Master Anchor). The request is used to erase all settings of one or many network devices. The target node must be seen by the Master Anchor connected to the software system. Target device will reboot during the reset. Unused devices fields should be set to invalid ID: [0x00, 0x00, 0x00].

Byte Storage Field Description
0-2 3 bytes (array uint8) did 0 Three bytes long Node ID
3-5 3 bytes (array uint8) did 1 Three bytes long Node ID
6-8 3 bytes (array uint8) did 2 Three bytes long Node ID
9-11 3 bytes (array uint8) did 3 Three bytes long Node ID

C Structure format

typedef struct SM_Protocol_CommandBlinkPylon_s
{
    struct
    {
        uint8_t did[3];
        struct
        {
            uint8_t pattern : 6;    /** bits 0:5 */
            uint8_t brightness : 2; /** bits 6:7 */
        };

    } device[3];

} SM_Protocol_CommandBlinkPylon_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 199, length = 12 */
    0xc7, 0x0c, 0x00, 
    /* Value buffer */

    /* Device 0:
    did 0 = [1, 2, 3],
    brightness = 0x03;
    pattern = 0x06;
    */
    0x01, 0x02, 0x03, 0xc6, 

    /* Device 1:
    did 1 = [0, 0, 1],
    brightness = 0x01;
    pattern = 0x00;
    */
    0x00, 0x00, 0x01, 0x40, 

    /* Device 2:
    did 2 = [0, 0, 0] - not used
    */
    0x00, 0x00, 0x00, 0x00,
};

Frame transmitted from the PC to the device (Master Anchor). The request is used to turn on the application LEDs on one or more devices and configure their light blink parameters. The command executes immediately as soon as it reaches addressed devices. Unlike all other commands, this one is routed "blindly", without checking if the recipent device was active in the current cycle. This behavior is required in case of Pylon tags that are registered in the network as slow tags and therefore are not active in every cycle, but they should be able to execute blink request immediately.

Byte Storage Field Description
0-2 3 bytes (array uint8) did 0 Three bytes long Node ID
3 [bits 0-5] 6 bit enum pattern 0 6 bits long pattern ID [0-63]
3 [bits 6-7] 2 bit enum brightness 0 6 bits long brightness ID [0-3]
4-6 3 bytes (array uint8) did 1 Three bytes long Node ID
7 [bits 0-5] 6 bit enum pattern 1 6 bits long pattern ID [0-63]
7 [bits 6-7] 2 bit enum brightness 1 6 bits long brightness ID [0-3]
8-10 3 bytes (array uint8) did 2 Three bytes long Node ID
11 [bits 0-5] 6 bit enum pattern 2 6 bits long pattern ID [0-63]
11 [bits 6-7] 2 bit enum brightness 2 6 bits long brightness ID [0-3]

C Structure format

typedef struct SM_Protocol_CommandBlinkImmediately_s
{
    struct
    {
        uint8_t did[3];
        struct
        {
            uint8_t pattern : 6;    /** bits 0:5 */
            uint8_t brightness : 2; /** bits 6:7 */
        };

    } device[3];

} SM_Protocol_CommandBlinkImmediately_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 200, length = 12 */
    0xc8, 0x0c, 0x00, 
    /* Value buffer */

    /* Device 0:
    did 0 = [1, 2, 3],
    brightness = 0x03;
    pattern = 0x06;
    */
    0x01, 0x02, 0x03, 0xc6, 

    /* Device 1:
    did 1 = [0, 0, 1],
    brightness = 0x01;
    pattern = 0x00;
    */
    0x00, 0x00, 0x01, 0x40, 

    /* Device 2:
    did 2 = [0, 0, 0] - not used
    */
    0x00, 0x00, 0x00, 0x00,
};

Frame transmitted from the PC to the device (Master Anchor). The request is used to turn on the application LEDs on one or more devices and configure their light blink parameters. The command executes immediately as soon as it reaches addressed devices.

Byte Storage Field Description
0-2 3 bytes (array uint8) did 0 Three bytes long Node ID
3 [bits 0-5] 6 bit enum pattern 0 6 bits long pattern ID [0-63]
3 [bits 6-7] 2 bit enum brightness 0 6 bits long brightness ID [0-3]
4-6 3 bytes (array uint8) did 1 Three bytes long Node ID
7 [bits 0-5] 6 bit enum pattern 1 6 bits long pattern ID [0-63]
7 [bits 6-7] 2 bit enum brightness 1 6 bits long brightness ID [0-3]
8-10 3 bytes (array uint8) did 2 Three bytes long Node ID
11 [bits 0-5] 6 bit enum pattern 2 6 bits long pattern ID [0-63]
11 [bits 6-7] 2 bit enum brightness 2 6 bits long brightness ID [0-3]

C Structure format

typedef struct SM_Protocol_CommandBlink_s
{
    struct
    {
        uint8_t did[3];
        struct
        {
            uint8_t pattern : 6;    /** bits 0:5 */
            uint8_t brightness : 2; /** bits 6:7 */
        };

        uint16_t cycleId;
    } device[2];

} SM_Protocol_CommandBlink_t;

Frame example

uint8_t rawData[] =
{
    /* TLV header: type = 201, length = 12 */
    0xc9, 0x0c, 0x00, 
    /* Value buffer */

    /* Device 0:
    did 0 = [1, 2, 3],
    brightness = 0x03;
    pattern = 0x06;
    cycleId = 2200;
    */

    0x01, 0x02, 0x03, 0xc6, 0x98, 0x08, 

    /* Device 1:
    did 1 = [0, 0, 1],
    brightness = 0x01;
    pattern = 0x00;
    cycleId = 2200;
    */
    0x00, 0x00, 0x01, 0x40, 0x98, 0x08, 
};

Frame transmitted from the PC to the device (Master Anchor). The request is used to turn on the application LEDs on one or more devices and configure their light blink parameters. The command allows to synchronize LEDs enable time to the UWB network cycle.

Byte Storage Field Description
0-2 3 bytes (array uint8) did 0 Three bytes long Node ID
3 [bits 0-5] 6 bit enum pattern 0 6 bits long pattern ID [0-63]
3 [bits 6-7] 2 bit enum brightness 0 6 bits long brightness ID [0-3]
4-5 2 bytes (uint16) cycleId 0 ID of the cycle in which blink will be triggered
6-8 3 bytes (array uint8) did 1 Three bytes long Node ID
9 [bits 0-5] 6 bit enum pattern 1 6 bits long pattern ID [0-63]
9 [bits 6-7] 2 bit enum brightness 1 6 bits long brightness ID [0-3]
10-11 2 bytes (uint16) cycleId 1 ID of the cycle in which blink will be triggered

IMU Bit Stiching

18 Bits 16 Samples

Memory map size = 108 bytes

Offset [7] [6] [5] [4] [3] [2] [1] [0] Group
0 X0[7] X0[6] X0[5] X0[4] X0[3] X0[2] X0[1] X0[0] Samples
1 X0[15] X0[14] X0[13] X0[12] X0[11] X0[10] X0[9] X0[8] Samples
2 Y0[5] Y0[4] Y0[3] Y0[2] Y0[1] Y0[0] X0[17] X0[16] Samples
3 Y0[13] Y0[12] Y0[11] Y0[10] Y0[9] Y0[8] Y0[7] Y0[6] Samples
4 Z0[3] Z0[2] Z0[1] Z0[0] Y0[17] Y0[16] Y0[15] Y0[14] Samples
5 Z0[11] Z0[10] Z0[9] Z0[8] Z0[7] Z0[6] Z0[5] Z0[4] Samples
6 X1[1] X1[0] Z0[17] Z0[16] Z0[15] Z0[14] Z0[13] Z0[12] Samples
7 X1[9] X1[8] X1[7] X1[6] X1[5] X1[4] X1[3] X1[2] Samples
8 X1[17] X1[16] X1[15] X1[14] X1[13] X1[12] X1[11] X1[10] Samples
9 Y1[7] Y1[6] Y1[5] Y1[4] Y1[3] Y1[2] Y1[1] Y1[0] Samples
10 Y1[15] Y1[14] Y1[13] Y1[12] Y1[11] Y1[10] Y1[9] Y1[8] Samples
11 Z1[5] Z1[4] Z1[3] Z1[2] Z1[1] Z1[0] Y1[17] Y1[16] Samples
12 Z1[13] Z1[12] Z1[11] Z1[10] Z1[9] Z1[8] Z1[7] Z1[6] Samples
13 X2[3] X2[2] X2[1] X2[0] Z1[17] Z1[16] Z1[15] Z1[14] Samples
14 X2[11] X2[10] X2[9] X2[8] X2[7] X2[6] X2[5] X2[4] Samples
15 Y2[1] Y2[0] X2[17] X2[16] X2[15] X2[14] X2[13] X2[12] Samples
16 Y2[9] Y2[8] Y2[7] Y2[6] Y2[5] Y2[4] Y2[3] Y2[2] Samples
17 Y2[17] Y2[16] Y2[15] Y2[14] Y2[13] Y2[12] Y2[11] Y2[10] Samples
18 Z2[7] Z2[6] Z2[5] Z2[4] Z2[3] Z2[2] Z2[1] Z2[0] Samples
19 Z2[15] Z2[14] Z2[13] Z2[12] Z2[11] Z2[10] Z2[9] Z2[8] Samples
20 X3[5] X3[4] X3[3] X3[2] X3[1] X3[0] Z2[17] Z2[16] Samples
21 X3[13] X3[12] X3[11] X3[10] X3[9] X3[8] X3[7] X3[6] Samples
22 Y3[3] Y3[2] Y3[1] Y3[0] X3[17] X3[16] X3[15] X3[14] Samples
23 Y3[11] Y3[10] Y3[9] Y3[8] Y3[7] Y3[6] Y3[5] Y3[4] Samples
24 Z3[1] Z3[0] Y3[17] Y3[16] Y3[15] Y3[14] Y3[13] Y3[12] Samples
25 Z3[9] Z3[8] Z3[7] Z3[6] Z3[5] Z3[4] Z3[3] Z3[2] Samples
26 Z3[17] Z3[16] Z3[15] Z3[14] Z3[13] Z3[12] Z3[11] Z3[10] Samples
27 X4[7] X4[6] X4[5] X4[4] X4[3] X4[2] X4[1] X4[0] Samples
28 X4[15] X4[14] X4[13] X4[12] X4[11] X4[10] X4[9] X4[8] Samples
29 Y4[5] Y4[4] Y4[3] Y4[2] Y4[1] Y4[0] X4[17] X4[16] Samples
30 Y4[13] Y4[12] Y4[11] Y4[10] Y4[9] Y4[8] Y4[7] Y4[6] Samples
31 Z4[3] Z4[2] Z4[1] Z4[0] Y4[17] Y4[16] Y4[15] Y4[14] Samples
32 Z4[11] Z4[10] Z4[9] Z4[8] Z4[7] Z4[6] Z4[5] Z4[4] Samples
33 X5[1] X5[0] Z4[17] Z4[16] Z4[15] Z4[14] Z4[13] Z4[12] Samples
34 X5[9] X5[8] X5[7] X5[6] X5[5] X5[4] X5[3] X5[2] Samples
35 X5[17] X5[16] X5[15] X5[14] X5[13] X5[12] X5[11] X5[10] Samples
36 Y5[7] Y5[6] Y5[5] Y5[4] Y5[3] Y5[2] Y5[1] Y5[0] Samples
37 Y5[15] Y5[14] Y5[13] Y5[12] Y5[11] Y5[10] Y5[9] Y5[8] Samples
38 Z5[5] Z5[4] Z5[3] Z5[2] Z5[1] Z5[0] Y5[17] Y5[16] Samples
39 Z5[13] Z5[12] Z5[11] Z5[10] Z5[9] Z5[8] Z5[7] Z5[6] Samples
40 X6[3] X6[2] X6[1] X6[0] Z5[17] Z5[16] Z5[15] Z5[14] Samples
41 X6[11] X6[10] X6[9] X6[8] X6[7] X6[6] X6[5] X6[4] Samples
42 Y6[1] Y6[0] X6[17] X6[16] X6[15] X6[14] X6[13] X6[12] Samples
43 Y6[9] Y6[8] Y6[7] Y6[6] Y6[5] Y6[4] Y6[3] Y6[2] Samples
44 Y6[17] Y6[16] Y6[15] Y6[14] Y6[13] Y6[12] Y6[11] Y6[10] Samples
45 Z6[7] Z6[6] Z6[5] Z6[4] Z6[3] Z6[2] Z6[1] Z6[0] Samples
46 Z6[15] Z6[14] Z6[13] Z6[12] Z6[11] Z6[10] Z6[9] Z6[8] Samples
47 X7[5] X7[4] X7[3] X7[2] X7[1] X7[0] Z6[17] Z6[16] Samples
48 X7[13] X7[12] X7[11] X7[10] X7[9] X7[8] X7[7] X7[6] Samples
49 Y7[3] Y7[2] Y7[1] Y7[0] X7[17] X7[16] X7[15] X7[14] Samples
50 Y7[11] Y7[10] Y7[9] Y7[8] Y7[7] Y7[6] Y7[5] Y7[4] Samples
51 Z7[1] Z7[0] Y7[17] Y7[16] Y7[15] Y7[14] Y7[13] Y7[12] Samples
52 Z7[9] Z7[8] Z7[7] Z7[6] Z7[5] Z7[4] Z7[3] Z7[2] Samples
53 Z7[17] Z7[16] Z7[15] Z7[14] Z7[13] Z7[12] Z7[11] Z7[10] Samples
54 X8[7] X8[6] X8[5] X8[4] X8[3] X8[2] X8[1] X8[0] Samples
55 X8[15] X8[14] X8[13] X8[12] X8[11] X8[10] X8[9] X8[8] Samples
56 Y8[5] Y8[4] Y8[3] Y8[2] Y8[1] Y8[0] X8[17] X8[16] Samples
57 Y8[13] Y8[12] Y8[11] Y8[10] Y8[9] Y8[8] Y8[7] Y8[6] Samples
58 Z8[3] Z8[2] Z8[1] Z8[0] Y8[17] Y8[16] Y8[15] Y8[14] Samples
59 Z8[11] Z8[10] Z8[9] Z8[8] Z8[7] Z8[6] Z8[5] Z8[4] Samples
60 X9[1] X9[0] Z8[17] Z8[16] Z8[15] Z8[14] Z8[13] Z8[12] Samples
61 X9[9] X9[8] X9[7] X9[6] X9[5] X9[4] X9[3] X9[2] Samples
62 X9[17] X9[16] X9[15] X9[14] X9[13] X9[12] X9[11] X9[10] Samples
63 Y9[7] Y9[6] Y9[5] Y9[4] Y9[3] Y9[2] Y9[1] Y9[0] Samples
64 Y9[15] Y9[14] Y9[13] Y9[12] Y9[11] Y9[10] Y9[9] Y9[8] Samples
65 Z9[5] Z9[4] Z9[3] Z9[2] Z9[1] Z9[0] Y9[17] Y9[16] Samples
66 Z9[13] Z9[12] Z9[11] Z9[10] Z9[9] Z9[8] Z9[7] Z9[6] Samples
67 X10[3] X10[2] X10[1] X10[0] Z9[17] Z9[16] Z9[15] Z9[14] Samples
68 X10[11] X10[10] X10[9] X10[8] X10[7] X10[6] X10[5] X10[4] Samples
69 Y10[1] Y10[0] X10[17] X10[16] X10[15] X10[14] X10[13] X10[12] Samples
70 Y10[9] Y10[8] Y10[7] Y10[6] Y10[5] Y10[4] Y10[3] Y10[2] Samples
71 Y10[17] Y10[16] Y10[15] Y10[14] Y10[13] Y10[12] Y10[11] Y10[10] Samples
72 Z10[7] Z10[6] Z10[5] Z10[4] Z10[3] Z10[2] Z10[1] Z10[0] Samples
73 Z10[15] Z10[14] Z10[13] Z10[12] Z10[11] Z10[10] Z10[9] Z10[8] Samples
74 X11[5] X11[4] X11[3] X11[2] X11[1] X11[0] Z10[17] Z10[16] Samples
75 X11[13] X11[12] X11[11] X11[10] X11[9] X11[8] X11[7] X11[6] Samples
76 Y11[3] Y11[2] Y11[1] Y11[0] X11[17] X11[16] X11[15] X11[14] Samples
77 Y11[11] Y11[10] Y11[9] Y11[8] Y11[7] Y11[6] Y11[5] Y11[4] Samples
78 Z11[1] Z11[0] Y11[17] Y11[16] Y11[15] Y11[14] Y11[13] Y11[12] Samples
79 Z11[9] Z11[8] Z11[7] Z11[6] Z11[5] Z11[4] Z11[3] Z11[2] Samples
80 Z11[17] Z11[16] Z11[15] Z11[14] Z11[13] Z11[12] Z11[11] Z11[10] Samples
81 X12[7] X12[6] X12[5] X12[4] X12[3] X12[2] X12[1] X12[0] Samples
82 X12[15] X12[14] X12[13] X12[12] X12[11] X12[10] X12[9] X12[8] Samples
83 Y12[5] Y12[4] Y12[3] Y12[2] Y12[1] Y12[0] X12[17] X12[16] Samples
84 Y12[13] Y12[12] Y12[11] Y12[10] Y12[9] Y12[8] Y12[7] Y12[6] Samples
85 Z12[3] Z12[2] Z12[1] Z12[0] Y12[17] Y12[16] Y12[15] Y12[14] Samples
86 Z12[11] Z12[10] Z12[9] Z12[8] Z12[7] Z12[6] Z12[5] Z12[4] Samples
87 X13[1] X13[0] Z12[17] Z12[16] Z12[15] Z12[14] Z12[13] Z12[12] Samples
88 X13[9] X13[8] X13[7] X13[6] X13[5] X13[4] X13[3] X13[2] Samples
89 X13[17] X13[16] X13[15] X13[14] X13[13] X13[12] X13[11] X13[10] Samples
90 Y13[7] Y13[6] Y13[5] Y13[4] Y13[3] Y13[2] Y13[1] Y13[0] Samples
91 Y13[15] Y13[14] Y13[13] Y13[12] Y13[11] Y13[10] Y13[9] Y13[8] Samples
92 Z13[5] Z13[4] Z13[3] Z13[2] Z13[1] Z13[0] Y13[17] Y13[16] Samples
93 Z13[13] Z13[12] Z13[11] Z13[10] Z13[9] Z13[8] Z13[7] Z13[6] Samples
94 X14[3] X14[2] X14[1] X14[0] Z13[17] Z13[16] Z13[15] Z13[14] Samples
95 X14[11] X14[10] X14[9] X14[8] X14[7] X14[6] X14[5] X14[4] Samples
96 Y14[1] Y14[0] X14[17] X14[16] X14[15] X14[14] X14[13] X14[12] Samples
97 Y14[9] Y14[8] Y14[7] Y14[6] Y14[5] Y14[4] Y14[3] Y14[2] Samples
98 Y14[17] Y14[16] Y14[15] Y14[14] Y14[13] Y14[12] Y14[11] Y14[10] Samples
99 Z14[7] Z14[6] Z14[5] Z14[4] Z14[3] Z14[2] Z14[1] Z14[0] Samples
100 Z14[15] Z14[14] Z14[13] Z14[12] Z14[11] Z14[10] Z14[9] Z14[8] Samples
101 X15[5] X15[4] X15[3] X15[2] X15[1] X15[0] Z14[17] Z14[16] Samples
102 X15[13] X15[12] X15[11] X15[10] X15[9] X15[8] X15[7] X15[6] Samples
103 Y15[3] Y15[2] Y15[1] Y15[0] X15[17] X15[16] X15[15] X15[14] Samples
104 Y15[11] Y15[10] Y15[9] Y15[8] Y15[7] Y15[6] Y15[5] Y15[4] Samples
105 Z15[1] Z15[0] Y15[17] Y15[16] Y15[15] Y15[14] Y15[13] Y15[12] Samples
106 Z15[9] Z15[8] Z15[7] Z15[6] Z15[5] Z15[4] Z15[3] Z15[2] Samples
107 Z15[17] Z15[16] Z15[15] Z15[14] Z15[13] Z15[12] Z15[11] Z15[10] Samples

19 Bits 16 Samples

Memory map size = 114 bytes

Offset [7] [6] [5] [4] [3] [2] [1] [0] Group
0 X0[7] X0[6] X0[5] X0[4] X0[3] X0[2] X0[1] X0[0] Samples
1 X0[15] X0[14] X0[13] X0[12] X0[11] X0[10] X0[9] X0[8] Samples
2 Y0[4] Y0[3] Y0[2] Y0[1] Y0[0] X0[18] X0[17] X0[16] Samples
3 Y0[12] Y0[11] Y0[10] Y0[9] Y0[8] Y0[7] Y0[6] Y0[5] Samples
4 Z0[1] Z0[0] Y0[18] Y0[17] Y0[16] Y0[15] Y0[14] Y0[13] Samples
5 Z0[9] Z0[8] Z0[7] Z0[6] Z0[5] Z0[4] Z0[3] Z0[2] Samples
6 Z0[17] Z0[16] Z0[15] Z0[14] Z0[13] Z0[12] Z0[11] Z0[10] Samples
7 X1[6] X1[5] X1[4] X1[3] X1[2] X1[1] X1[0] Z0[18] Samples
8 X1[14] X1[13] X1[12] X1[11] X1[10] X1[9] X1[8] X1[7] Samples
9 Y1[3] Y1[2] Y1[1] Y1[0] X1[18] X1[17] X1[16] X1[15] Samples
10 Y1[11] Y1[10] Y1[9] Y1[8] Y1[7] Y1[6] Y1[5] Y1[4] Samples
11 Z1[0] Y1[18] Y1[17] Y1[16] Y1[15] Y1[14] Y1[13] Y1[12] Samples
12 Z1[8] Z1[7] Z1[6] Z1[5] Z1[4] Z1[3] Z1[2] Z1[1] Samples
13 Z1[16] Z1[15] Z1[14] Z1[13] Z1[12] Z1[11] Z1[10] Z1[9] Samples
14 X2[5] X2[4] X2[3] X2[2] X2[1] X2[0] Z1[18] Z1[17] Samples
15 X2[13] X2[12] X2[11] X2[10] X2[9] X2[8] X2[7] X2[6] Samples
16 Y2[2] Y2[1] Y2[0] X2[18] X2[17] X2[16] X2[15] X2[14] Samples
17 Y2[10] Y2[9] Y2[8] Y2[7] Y2[6] Y2[5] Y2[4] Y2[3] Samples
18 Y2[18] Y2[17] Y2[16] Y2[15] Y2[14] Y2[13] Y2[12] Y2[11] Samples
19 Z2[7] Z2[6] Z2[5] Z2[4] Z2[3] Z2[2] Z2[1] Z2[0] Samples
20 Z2[15] Z2[14] Z2[13] Z2[12] Z2[11] Z2[10] Z2[9] Z2[8] Samples
21 X3[4] X3[3] X3[2] X3[1] X3[0] Z2[18] Z2[17] Z2[16] Samples
22 X3[12] X3[11] X3[10] X3[9] X3[8] X3[7] X3[6] X3[5] Samples
23 Y3[1] Y3[0] X3[18] X3[17] X3[16] X3[15] X3[14] X3[13] Samples
24 Y3[9] Y3[8] Y3[7] Y3[6] Y3[5] Y3[4] Y3[3] Y3[2] Samples
25 Y3[17] Y3[16] Y3[15] Y3[14] Y3[13] Y3[12] Y3[11] Y3[10] Samples
26 Z3[6] Z3[5] Z3[4] Z3[3] Z3[2] Z3[1] Z3[0] Y3[18] Samples
27 Z3[14] Z3[13] Z3[12] Z3[11] Z3[10] Z3[9] Z3[8] Z3[7] Samples
28 X4[3] X4[2] X4[1] X4[0] Z3[18] Z3[17] Z3[16] Z3[15] Samples
29 X4[11] X4[10] X4[9] X4[8] X4[7] X4[6] X4[5] X4[4] Samples
30 Y4[0] X4[18] X4[17] X4[16] X4[15] X4[14] X4[13] X4[12] Samples
31 Y4[8] Y4[7] Y4[6] Y4[5] Y4[4] Y4[3] Y4[2] Y4[1] Samples
32 Y4[16] Y4[15] Y4[14] Y4[13] Y4[12] Y4[11] Y4[10] Y4[9] Samples
33 Z4[5] Z4[4] Z4[3] Z4[2] Z4[1] Z4[0] Y4[18] Y4[17] Samples
34 Z4[13] Z4[12] Z4[11] Z4[10] Z4[9] Z4[8] Z4[7] Z4[6] Samples
35 X5[2] X5[1] X5[0] Z4[18] Z4[17] Z4[16] Z4[15] Z4[14] Samples
36 X5[10] X5[9] X5[8] X5[7] X5[6] X5[5] X5[4] X5[3] Samples
37 X5[18] X5[17] X5[16] X5[15] X5[14] X5[13] X5[12] X5[11] Samples
38 Y5[7] Y5[6] Y5[5] Y5[4] Y5[3] Y5[2] Y5[1] Y5[0] Samples
39 Y5[15] Y5[14] Y5[13] Y5[12] Y5[11] Y5[10] Y5[9] Y5[8] Samples
40 Z5[4] Z5[3] Z5[2] Z5[1] Z5[0] Y5[18] Y5[17] Y5[16] Samples
41 Z5[12] Z5[11] Z5[10] Z5[9] Z5[8] Z5[7] Z5[6] Z5[5] Samples
42 X6[1] X6[0] Z5[18] Z5[17] Z5[16] Z5[15] Z5[14] Z5[13] Samples
43 X6[9] X6[8] X6[7] X6[6] X6[5] X6[4] X6[3] X6[2] Samples
44 X6[17] X6[16] X6[15] X6[14] X6[13] X6[12] X6[11] X6[10] Samples
45 Y6[6] Y6[5] Y6[4] Y6[3] Y6[2] Y6[1] Y6[0] X6[18] Samples
46 Y6[14] Y6[13] Y6[12] Y6[11] Y6[10] Y6[9] Y6[8] Y6[7] Samples
47 Z6[3] Z6[2] Z6[1] Z6[0] Y6[18] Y6[17] Y6[16] Y6[15] Samples
48 Z6[11] Z6[10] Z6[9] Z6[8] Z6[7] Z6[6] Z6[5] Z6[4] Samples
49 X7[0] Z6[18] Z6[17] Z6[16] Z6[15] Z6[14] Z6[13] Z6[12] Samples
50 X7[8] X7[7] X7[6] X7[5] X7[4] X7[3] X7[2] X7[1] Samples
51 X7[16] X7[15] X7[14] X7[13] X7[12] X7[11] X7[10] X7[9] Samples
52 Y7[5] Y7[4] Y7[3] Y7[2] Y7[1] Y7[0] X7[18] X7[17] Samples
53 Y7[13] Y7[12] Y7[11] Y7[10] Y7[9] Y7[8] Y7[7] Y7[6] Samples
54 Z7[2] Z7[1] Z7[0] Y7[18] Y7[17] Y7[16] Y7[15] Y7[14] Samples
55 Z7[10] Z7[9] Z7[8] Z7[7] Z7[6] Z7[5] Z7[4] Z7[3] Samples
56 Z7[18] Z7[17] Z7[16] Z7[15] Z7[14] Z7[13] Z7[12] Z7[11] Samples
57 X8[7] X8[6] X8[5] X8[4] X8[3] X8[2] X8[1] X8[0] Samples
58 X8[15] X8[14] X8[13] X8[12] X8[11] X8[10] X8[9] X8[8] Samples
59 Y8[4] Y8[3] Y8[2] Y8[1] Y8[0] X8[18] X8[17] X8[16] Samples
60 Y8[12] Y8[11] Y8[10] Y8[9] Y8[8] Y8[7] Y8[6] Y8[5] Samples
61 Z8[1] Z8[0] Y8[18] Y8[17] Y8[16] Y8[15] Y8[14] Y8[13] Samples
62 Z8[9] Z8[8] Z8[7] Z8[6] Z8[5] Z8[4] Z8[3] Z8[2] Samples
63 Z8[17] Z8[16] Z8[15] Z8[14] Z8[13] Z8[12] Z8[11] Z8[10] Samples
64 X9[6] X9[5] X9[4] X9[3] X9[2] X9[1] X9[0] Z8[18] Samples
65 X9[14] X9[13] X9[12] X9[11] X9[10] X9[9] X9[8] X9[7] Samples
66 Y9[3] Y9[2] Y9[1] Y9[0] X9[18] X9[17] X9[16] X9[15] Samples
67 Y9[11] Y9[10] Y9[9] Y9[8] Y9[7] Y9[6] Y9[5] Y9[4] Samples
68 Z9[0] Y9[18] Y9[17] Y9[16] Y9[15] Y9[14] Y9[13] Y9[12] Samples
69 Z9[8] Z9[7] Z9[6] Z9[5] Z9[4] Z9[3] Z9[2] Z9[1] Samples
70 Z9[16] Z9[15] Z9[14] Z9[13] Z9[12] Z9[11] Z9[10] Z9[9] Samples
71 X10[5] X10[4] X10[3] X10[2] X10[1] X10[0] Z9[18] Z9[17] Samples
72 X10[13] X10[12] X10[11] X10[10] X10[9] X10[8] X10[7] X10[6] Samples
73 Y10[2] Y10[1] Y10[0] X10[18] X10[17] X10[16] X10[15] X10[14] Samples
74 Y10[10] Y10[9] Y10[8] Y10[7] Y10[6] Y10[5] Y10[4] Y10[3] Samples
75 Y10[18] Y10[17] Y10[16] Y10[15] Y10[14] Y10[13] Y10[12] Y10[11] Samples
76 Z10[7] Z10[6] Z10[5] Z10[4] Z10[3] Z10[2] Z10[1] Z10[0] Samples
77 Z10[15] Z10[14] Z10[13] Z10[12] Z10[11] Z10[10] Z10[9] Z10[8] Samples
78 X11[4] X11[3] X11[2] X11[1] X11[0] Z10[18] Z10[17] Z10[16] Samples
79 X11[12] X11[11] X11[10] X11[9] X11[8] X11[7] X11[6] X11[5] Samples
80 Y11[1] Y11[0] X11[18] X11[17] X11[16] X11[15] X11[14] X11[13] Samples
81 Y11[9] Y11[8] Y11[7] Y11[6] Y11[5] Y11[4] Y11[3] Y11[2] Samples
82 Y11[17] Y11[16] Y11[15] Y11[14] Y11[13] Y11[12] Y11[11] Y11[10] Samples
83 Z11[6] Z11[5] Z11[4] Z11[3] Z11[2] Z11[1] Z11[0] Y11[18] Samples
84 Z11[14] Z11[13] Z11[12] Z11[11] Z11[10] Z11[9] Z11[8] Z11[7] Samples
85 X12[3] X12[2] X12[1] X12[0] Z11[18] Z11[17] Z11[16] Z11[15] Samples
86 X12[11] X12[10] X12[9] X12[8] X12[7] X12[6] X12[5] X12[4] Samples
87 Y12[0] X12[18] X12[17] X12[16] X12[15] X12[14] X12[13] X12[12] Samples
88 Y12[8] Y12[7] Y12[6] Y12[5] Y12[4] Y12[3] Y12[2] Y12[1] Samples
89 Y12[16] Y12[15] Y12[14] Y12[13] Y12[12] Y12[11] Y12[10] Y12[9] Samples
90 Z12[5] Z12[4] Z12[3] Z12[2] Z12[1] Z12[0] Y12[18] Y12[17] Samples
91 Z12[13] Z12[12] Z12[11] Z12[10] Z12[9] Z12[8] Z12[7] Z12[6] Samples
92 X13[2] X13[1] X13[0] Z12[18] Z12[17] Z12[16] Z12[15] Z12[14] Samples
93 X13[10] X13[9] X13[8] X13[7] X13[6] X13[5] X13[4] X13[3] Samples
94 X13[18] X13[17] X13[16] X13[15] X13[14] X13[13] X13[12] X13[11] Samples
95 Y13[7] Y13[6] Y13[5] Y13[4] Y13[3] Y13[2] Y13[1] Y13[0] Samples
96 Y13[15] Y13[14] Y13[13] Y13[12] Y13[11] Y13[10] Y13[9] Y13[8] Samples
97 Z13[4] Z13[3] Z13[2] Z13[1] Z13[0] Y13[18] Y13[17] Y13[16] Samples
98 Z13[12] Z13[11] Z13[10] Z13[9] Z13[8] Z13[7] Z13[6] Z13[5] Samples
99 X14[1] X14[0] Z13[18] Z13[17] Z13[16] Z13[15] Z13[14] Z13[13] Samples
100 X14[9] X14[8] X14[7] X14[6] X14[5] X14[4] X14[3] X14[2] Samples
101 X14[17] X14[16] X14[15] X14[14] X14[13] X14[12] X14[11] X14[10] Samples
102 Y14[6] Y14[5] Y14[4] Y14[3] Y14[2] Y14[1] Y14[0] X14[18] Samples
103 Y14[14] Y14[13] Y14[12] Y14[11] Y14[10] Y14[9] Y14[8] Y14[7] Samples
104 Z14[3] Z14[2] Z14[1] Z14[0] Y14[18] Y14[17] Y14[16] Y14[15] Samples
105 Z14[11] Z14[10] Z14[9] Z14[8] Z14[7] Z14[6] Z14[5] Z14[4] Samples
106 X15[0] Z14[18] Z14[17] Z14[16] Z14[15] Z14[14] Z14[13] Z14[12] Samples
107 X15[8] X15[7] X15[6] X15[5] X15[4] X15[3] X15[2] X15[1] Samples
108 X15[16] X15[15] X15[14] X15[13] X15[12] X15[11] X15[10] X15[9] Samples
109 Y15[5] Y15[4] Y15[3] Y15[2] Y15[1] Y15[0] X15[18] X15[17] Samples
110 Y15[13] Y15[12] Y15[11] Y15[10] Y15[9] Y15[8] Y15[7] Y15[6] Samples
111 Z15[2] Z15[1] Z15[0] Y15[18] Y15[17] Y15[16] Y15[15] Y15[14] Samples
112 Z15[10] Z15[9] Z15[8] Z15[7] Z15[6] Z15[5] Z15[4] Z15[3] Samples
113 Z15[18] Z15[17] Z15[16] Z15[15] Z15[14] Z15[13] Z15[12] Z15[11] Samples

11 Bits 16 Samples

Memory map size = 66 bytes

Offset [7] [6] [5] [4] [3] [2] [1] [0] Group
0 X0[7] X0[6] X0[5] X0[4] X0[3] X0[2] X0[1] X0[0] Samples
1 Y0[4] Y0[3] Y0[2] Y0[1] Y0[0] X0[10] X0[9] X0[8] Samples
2 Z0[1] Z0[0] Y0[10] Y0[9] Y0[8] Y0[7] Y0[6] Y0[5] Samples
3 Z0[9] Z0[8] Z0[7] Z0[6] Z0[5] Z0[4] Z0[3] Z0[2] Samples
4 X1[6] X1[5] X1[4] X1[3] X1[2] X1[1] X1[0] Z0[10] Samples
5 Y1[3] Y1[2] Y1[1] Y1[0] X1[10] X1[9] X1[8] X1[7] Samples
6 Z1[0] Y1[10] Y1[9] Y1[8] Y1[7] Y1[6] Y1[5] Y1[4] Samples
7 Z1[8] Z1[7] Z1[6] Z1[5] Z1[4] Z1[3] Z1[2] Z1[1] Samples
8 X2[5] X2[4] X2[3] X2[2] X2[1] X2[0] Z1[10] Z1[9] Samples
9 Y2[2] Y2[1] Y2[0] X2[10] X2[9] X2[8] X2[7] X2[6] Samples
10 Y2[10] Y2[9] Y2[8] Y2[7] Y2[6] Y2[5] Y2[4] Y2[3] Samples
11 Z2[7] Z2[6] Z2[5] Z2[4] Z2[3] Z2[2] Z2[1] Z2[0] Samples
12 X3[4] X3[3] X3[2] X3[1] X3[0] Z2[10] Z2[9] Z2[8] Samples
13 Y3[1] Y3[0] X3[10] X3[9] X3[8] X3[7] X3[6] X3[5] Samples
14 Y3[9] Y3[8] Y3[7] Y3[6] Y3[5] Y3[4] Y3[3] Y3[2] Samples
15 Z3[6] Z3[5] Z3[4] Z3[3] Z3[2] Z3[1] Z3[0] Y3[10] Samples
16 X4[3] X4[2] X4[1] X4[0] Z3[10] Z3[9] Z3[8] Z3[7] Samples
17 Y4[0] X4[10] X4[9] X4[8] X4[7] X4[6] X4[5] X4[4] Samples
18 Y4[8] Y4[7] Y4[6] Y4[5] Y4[4] Y4[3] Y4[2] Y4[1] Samples
19 Z4[5] Z4[4] Z4[3] Z4[2] Z4[1] Z4[0] Y4[10] Y4[9] Samples
20 X5[2] X5[1] X5[0] Z4[10] Z4[9] Z4[8] Z4[7] Z4[6] Samples
21 X5[10] X5[9] X5[8] X5[7] X5[6] X5[5] X5[4] X5[3] Samples
22 Y5[7] Y5[6] Y5[5] Y5[4] Y5[3] Y5[2] Y5[1] Y5[0] Samples
23 Z5[4] Z5[3] Z5[2] Z5[1] Z5[0] Y5[10] Y5[9] Y5[8] Samples
24 X6[1] X6[0] Z5[10] Z5[9] Z5[8] Z5[7] Z5[6] Z5[5] Samples
25 X6[9] X6[8] X6[7] X6[6] X6[5] X6[4] X6[3] X6[2] Samples
26 Y6[6] Y6[5] Y6[4] Y6[3] Y6[2] Y6[1] Y6[0] X6[10] Samples
27 Z6[3] Z6[2] Z6[1] Z6[0] Y6[10] Y6[9] Y6[8] Y6[7] Samples
28 X7[0] Z6[10] Z6[9] Z6[8] Z6[7] Z6[6] Z6[5] Z6[4] Samples
29 X7[8] X7[7] X7[6] X7[5] X7[4] X7[3] X7[2] X7[1] Samples
30 Y7[5] Y7[4] Y7[3] Y7[2] Y7[1] Y7[0] X7[10] X7[9] Samples
31 Z7[2] Z7[1] Z7[0] Y7[10] Y7[9] Y7[8] Y7[7] Y7[6] Samples
32 Z7[10] Z7[9] Z7[8] Z7[7] Z7[6] Z7[5] Z7[4] Z7[3] Samples
33 X8[7] X8[6] X8[5] X8[4] X8[3] X8[2] X8[1] X8[0] Samples
34 Y8[4] Y8[3] Y8[2] Y8[1] Y8[0] X8[10] X8[9] X8[8] Samples
35 Z8[1] Z8[0] Y8[10] Y8[9] Y8[8] Y8[7] Y8[6] Y8[5] Samples
36 Z8[9] Z8[8] Z8[7] Z8[6] Z8[5] Z8[4] Z8[3] Z8[2] Samples
37 X9[6] X9[5] X9[4] X9[3] X9[2] X9[1] X9[0] Z8[10] Samples
38 Y9[3] Y9[2] Y9[1] Y9[0] X9[10] X9[9] X9[8] X9[7] Samples
39 Z9[0] Y9[10] Y9[9] Y9[8] Y9[7] Y9[6] Y9[5] Y9[4] Samples
40 Z9[8] Z9[7] Z9[6] Z9[5] Z9[4] Z9[3] Z9[2] Z9[1] Samples
41 X10[5] X10[4] X10[3] X10[2] X10[1] X10[0] Z9[10] Z9[9] Samples
42 Y10[2] Y10[1] Y10[0] X10[10] X10[9] X10[8] X10[7] X10[6] Samples
43 Y10[10] Y10[9] Y10[8] Y10[7] Y10[6] Y10[5] Y10[4] Y10[3] Samples
44 Z10[7] Z10[6] Z10[5] Z10[4] Z10[3] Z10[2] Z10[1] Z10[0] Samples
45 X11[4] X11[3] X11[2] X11[1] X11[0] Z10[10] Z10[9] Z10[8] Samples
46 Y11[1] Y11[0] X11[10] X11[9] X11[8] X11[7] X11[6] X11[5] Samples
47 Y11[9] Y11[8] Y11[7] Y11[6] Y11[5] Y11[4] Y11[3] Y11[2] Samples
48 Z11[6] Z11[5] Z11[4] Z11[3] Z11[2] Z11[1] Z11[0] Y11[10] Samples
49 X12[3] X12[2] X12[1] X12[0] Z11[10] Z11[9] Z11[8] Z11[7] Samples
50 Y12[0] X12[10] X12[9] X12[8] X12[7] X12[6] X12[5] X12[4] Samples
51 Y12[8] Y12[7] Y12[6] Y12[5] Y12[4] Y12[3] Y12[2] Y12[1] Samples
52 Z12[5] Z12[4] Z12[3] Z12[2] Z12[1] Z12[0] Y12[10] Y12[9] Samples
53 X13[2] X13[1] X13[0] Z12[10] Z12[9] Z12[8] Z12[7] Z12[6] Samples
54 X13[10] X13[9] X13[8] X13[7] X13[6] X13[5] X13[4] X13[3] Samples
55 Y13[7] Y13[6] Y13[5] Y13[4] Y13[3] Y13[2] Y13[1] Y13[0] Samples
56 Z13[4] Z13[3] Z13[2] Z13[1] Z13[0] Y13[10] Y13[9] Y13[8] Samples
57 X14[1] X14[0] Z13[10] Z13[9] Z13[8] Z13[7] Z13[6] Z13[5] Samples
58 X14[9] X14[8] X14[7] X14[6] X14[5] X14[4] X14[3] X14[2] Samples
59 Y14[6] Y14[5] Y14[4] Y14[3] Y14[2] Y14[1] Y14[0] X14[10] Samples
60 Z14[3] Z14[2] Z14[1] Z14[0] Y14[10] Y14[9] Y14[8] Y14[7] Samples
61 X15[0] Z14[10] Z14[9] Z14[8] Z14[7] Z14[6] Z14[5] Z14[4] Samples
62 X15[8] X15[7] X15[6] X15[5] X15[4] X15[3] X15[2] X15[1] Samples
63 Y15[5] Y15[4] Y15[3] Y15[2] Y15[1] Y15[0] X15[10] X15[9] Samples
64 Z15[2] Z15[1] Z15[0] Y15[10] Y15[9] Y15[8] Y15[7] Y15[6] Samples
65 Z15[10] Z15[9] Z15[8] Z15[7] Z15[6] Z15[5] Z15[4] Z15[3] Samples

18 Bits 7 Samples

Memory map size = 48 bytes

Offset [7] [6] [5] [4] [3] [2] [1] [0] Group
0 X0[7] X0[6] X0[5] X0[4] X0[3] X0[2] X0[1] X0[0] Samples
1 X0[15] X0[14] X0[13] X0[12] X0[11] X0[10] X0[9] X0[8] Samples
2 Y0[5] Y0[4] Y0[3] Y0[2] Y0[1] Y0[0] X0[17] X0[16] Samples
3 Y0[13] Y0[12] Y0[11] Y0[10] Y0[9] Y0[8] Y0[7] Y0[6] Samples
4 Z0[3] Z0[2] Z0[1] Z0[0] Y0[17] Y0[16] Y0[15] Y0[14] Samples
5 Z0[11] Z0[10] Z0[9] Z0[8] Z0[7] Z0[6] Z0[5] Z0[4] Samples
6 X1[1] X1[0] Z0[17] Z0[16] Z0[15] Z0[14] Z0[13] Z0[12] Samples
7 X1[9] X1[8] X1[7] X1[6] X1[5] X1[4] X1[3] X1[2] Samples
8 X1[17] X1[16] X1[15] X1[14] X1[13] X1[12] X1[11] X1[10] Samples
9 Y1[7] Y1[6] Y1[5] Y1[4] Y1[3] Y1[2] Y1[1] Y1[0] Samples
10 Y1[15] Y1[14] Y1[13] Y1[12] Y1[11] Y1[10] Y1[9] Y1[8] Samples
11 Z1[5] Z1[4] Z1[3] Z1[2] Z1[1] Z1[0] Y1[17] Y1[16] Samples
12 Z1[13] Z1[12] Z1[11] Z1[10] Z1[9] Z1[8] Z1[7] Z1[6] Samples
13 X2[3] X2[2] X2[1] X2[0] Z1[17] Z1[16] Z1[15] Z1[14] Samples
14 X2[11] X2[10] X2[9] X2[8] X2[7] X2[6] X2[5] X2[4] Samples
15 Y2[1] Y2[0] X2[17] X2[16] X2[15] X2[14] X2[13] X2[12] Samples
16 Y2[9] Y2[8] Y2[7] Y2[6] Y2[5] Y2[4] Y2[3] Y2[2] Samples
17 Y2[17] Y2[16] Y2[15] Y2[14] Y2[13] Y2[12] Y2[11] Y2[10] Samples
18 Z2[7] Z2[6] Z2[5] Z2[4] Z2[3] Z2[2] Z2[1] Z2[0] Samples
19 Z2[15] Z2[14] Z2[13] Z2[12] Z2[11] Z2[10] Z2[9] Z2[8] Samples
20 X3[5] X3[4] X3[3] X3[2] X3[1] X3[0] Z2[17] Z2[16] Samples
21 X3[13] X3[12] X3[11] X3[10] X3[9] X3[8] X3[7] X3[6] Samples
22 Y3[3] Y3[2] Y3[1] Y3[0] X3[17] X3[16] X3[15] X3[14] Samples
23 Y3[11] Y3[10] Y3[9] Y3[8] Y3[7] Y3[6] Y3[5] Y3[4] Samples
24 Z3[1] Z3[0] Y3[17] Y3[16] Y3[15] Y3[14] Y3[13] Y3[12] Samples
25 Z3[9] Z3[8] Z3[7] Z3[6] Z3[5] Z3[4] Z3[3] Z3[2] Samples
26 Z3[17] Z3[16] Z3[15] Z3[14] Z3[13] Z3[12] Z3[11] Z3[10] Samples
27 X4[7] X4[6] X4[5] X4[4] X4[3] X4[2] X4[1] X4[0] Samples
28 X4[15] X4[14] X4[13] X4[12] X4[11] X4[10] X4[9] X4[8] Samples
29 Y4[5] Y4[4] Y4[3] Y4[2] Y4[1] Y4[0] X4[17] X4[16] Samples
30 Y4[13] Y4[12] Y4[11] Y4[10] Y4[9] Y4[8] Y4[7] Y4[6] Samples
31 Z4[3] Z4[2] Z4[1] Z4[0] Y4[17] Y4[16] Y4[15] Y4[14] Samples
32 Z4[11] Z4[10] Z4[9] Z4[8] Z4[7] Z4[6] Z4[5] Z4[4] Samples
33 X5[1] X5[0] Z4[17] Z4[16] Z4[15] Z4[14] Z4[13] Z4[12] Samples
34 X5[9] X5[8] X5[7] X5[6] X5[5] X5[4] X5[3] X5[2] Samples
35 X5[17] X5[16] X5[15] X5[14] X5[13] X5[12] X5[11] X5[10] Samples
36 Y5[7] Y5[6] Y5[5] Y5[4] Y5[3] Y5[2] Y5[1] Y5[0] Samples
37 Y5[15] Y5[14] Y5[13] Y5[12] Y5[11] Y5[10] Y5[9] Y5[8] Samples
38 Z5[5] Z5[4] Z5[3] Z5[2] Z5[1] Z5[0] Y5[17] Y5[16] Samples
39 Z5[13] Z5[12] Z5[11] Z5[10] Z5[9] Z5[8] Z5[7] Z5[6] Samples
40 X6[3] X6[2] X6[1] X6[0] Z5[17] Z5[16] Z5[15] Z5[14] Samples
41 X6[11] X6[10] X6[9] X6[8] X6[7] X6[6] X6[5] X6[4] Samples
42 Y6[1] Y6[0] X6[17] X6[16] X6[15] X6[14] X6[13] X6[12] Samples
43 Y6[9] Y6[8] Y6[7] Y6[6] Y6[5] Y6[4] Y6[3] Y6[2] Samples
44 Y6[17] Y6[16] Y6[15] Y6[14] Y6[13] Y6[12] Y6[11] Y6[10] Samples
45 Z6[7] Z6[6] Z6[5] Z6[4] Z6[3] Z6[2] Z6[1] Z6[0] Samples
46 Z6[15] Z6[14] Z6[13] Z6[12] Z6[11] Z6[10] Z6[9] Z6[8] Samples
47 --- --- --- --- --- --- Z6[17] Z6[16] Samples

19 Bits 7 Samples

Memory map size = 50 bytes

Offset [7] [6] [5] [4] [3] [2] [1] [0] Group
0 X0[7] X0[6] X0[5] X0[4] X0[3] X0[2] X0[1] X0[0] Samples
1 X0[15] X0[14] X0[13] X0[12] X0[11] X0[10] X0[9] X0[8] Samples
2 Y0[4] Y0[3] Y0[2] Y0[1] Y0[0] X0[18] X0[17] X0[16] Samples
3 Y0[12] Y0[11] Y0[10] Y0[9] Y0[8] Y0[7] Y0[6] Y0[5] Samples
4 Z0[1] Z0[0] Y0[18] Y0[17] Y0[16] Y0[15] Y0[14] Y0[13] Samples
5 Z0[9] Z0[8] Z0[7] Z0[6] Z0[5] Z0[4] Z0[3] Z0[2] Samples
6 Z0[17] Z0[16] Z0[15] Z0[14] Z0[13] Z0[12] Z0[11] Z0[10] Samples
7 X1[6] X1[5] X1[4] X1[3] X1[2] X1[1] X1[0] Z0[18] Samples
8 X1[14] X1[13] X1[12] X1[11] X1[10] X1[9] X1[8] X1[7] Samples
9 Y1[3] Y1[2] Y1[1] Y1[0] X1[18] X1[17] X1[16] X1[15] Samples
10 Y1[11] Y1[10] Y1[9] Y1[8] Y1[7] Y1[6] Y1[5] Y1[4] Samples
11 Z1[0] Y1[18] Y1[17] Y1[16] Y1[15] Y1[14] Y1[13] Y1[12] Samples
12 Z1[8] Z1[7] Z1[6] Z1[5] Z1[4] Z1[3] Z1[2] Z1[1] Samples
13 Z1[16] Z1[15] Z1[14] Z1[13] Z1[12] Z1[11] Z1[10] Z1[9] Samples
14 X2[5] X2[4] X2[3] X2[2] X2[1] X2[0] Z1[18] Z1[17] Samples
15 X2[13] X2[12] X2[11] X2[10] X2[9] X2[8] X2[7] X2[6] Samples
16 Y2[2] Y2[1] Y2[0] X2[18] X2[17] X2[16] X2[15] X2[14] Samples
17 Y2[10] Y2[9] Y2[8] Y2[7] Y2[6] Y2[5] Y2[4] Y2[3] Samples
18 Y2[18] Y2[17] Y2[16] Y2[15] Y2[14] Y2[13] Y2[12] Y2[11] Samples
19 Z2[7] Z2[6] Z2[5] Z2[4] Z2[3] Z2[2] Z2[1] Z2[0] Samples
20 Z2[15] Z2[14] Z2[13] Z2[12] Z2[11] Z2[10] Z2[9] Z2[8] Samples
21 X3[4] X3[3] X3[2] X3[1] X3[0] Z2[18] Z2[17] Z2[16] Samples
22 X3[12] X3[11] X3[10] X3[9] X3[8] X3[7] X3[6] X3[5] Samples
23 Y3[1] Y3[0] X3[18] X3[17] X3[16] X3[15] X3[14] X3[13] Samples
24 Y3[9] Y3[8] Y3[7] Y3[6] Y3[5] Y3[4] Y3[3] Y3[2] Samples
25 Y3[17] Y3[16] Y3[15] Y3[14] Y3[13] Y3[12] Y3[11] Y3[10] Samples
26 Z3[6] Z3[5] Z3[4] Z3[3] Z3[2] Z3[1] Z3[0] Y3[18] Samples
27 Z3[14] Z3[13] Z3[12] Z3[11] Z3[10] Z3[9] Z3[8] Z3[7] Samples
28 X4[3] X4[2] X4[1] X4[0] Z3[18] Z3[17] Z3[16] Z3[15] Samples
29 X4[11] X4[10] X4[9] X4[8] X4[7] X4[6] X4[5] X4[4] Samples
30 Y4[0] X4[18] X4[17] X4[16] X4[15] X4[14] X4[13] X4[12] Samples
31 Y4[8] Y4[7] Y4[6] Y4[5] Y4[4] Y4[3] Y4[2] Y4[1] Samples
32 Y4[16] Y4[15] Y4[14] Y4[13] Y4[12] Y4[11] Y4[10] Y4[9] Samples
33 Z4[5] Z4[4] Z4[3] Z4[2] Z4[1] Z4[0] Y4[18] Y4[17] Samples
34 Z4[13] Z4[12] Z4[11] Z4[10] Z4[9] Z4[8] Z4[7] Z4[6] Samples
35 X5[2] X5[1] X5[0] Z4[18] Z4[17] Z4[16] Z4[15] Z4[14] Samples
36 X5[10] X5[9] X5[8] X5[7] X5[6] X5[5] X5[4] X5[3] Samples
37 X5[18] X5[17] X5[16] X5[15] X5[14] X5[13] X5[12] X5[11] Samples
38 Y5[7] Y5[6] Y5[5] Y5[4] Y5[3] Y5[2] Y5[1] Y5[0] Samples
39 Y5[15] Y5[14] Y5[13] Y5[12] Y5[11] Y5[10] Y5[9] Y5[8] Samples
40 Z5[4] Z5[3] Z5[2] Z5[1] Z5[0] Y5[18] Y5[17] Y5[16] Samples
41 Z5[12] Z5[11] Z5[10] Z5[9] Z5[8] Z5[7] Z5[6] Z5[5] Samples
42 X6[1] X6[0] Z5[18] Z5[17] Z5[16] Z5[15] Z5[14] Z5[13] Samples
43 X6[9] X6[8] X6[7] X6[6] X6[5] X6[4] X6[3] X6[2] Samples
44 X6[17] X6[16] X6[15] X6[14] X6[13] X6[12] X6[11] X6[10] Samples
45 Y6[6] Y6[5] Y6[4] Y6[3] Y6[2] Y6[1] Y6[0] X6[18] Samples
46 Y6[14] Y6[13] Y6[12] Y6[11] Y6[10] Y6[9] Y6[8] Y6[7] Samples
47 Z6[3] Z6[2] Z6[1] Z6[0] Y6[18] Y6[17] Y6[16] Y6[15] Samples
48 Z6[11] Z6[10] Z6[9] Z6[8] Z6[7] Z6[6] Z6[5] Z6[4] Samples
49 --- Z6[18] Z6[17] Z6[16] Z6[15] Z6[14] Z6[13] Z6[12] Samples

11 Bits 7 Samples

Memory map size = 29 bytes

Offset [7] [6] [5] [4] [3] [2] [1] [0] Group
0 X0[7] X0[6] X0[5] X0[4] X0[3] X0[2] X0[1] X0[0] Samples
1 Y0[4] Y0[3] Y0[2] Y0[1] Y0[0] X0[10] X0[9] X0[8] Samples
2 Z0[1] Z0[0] Y0[10] Y0[9] Y0[8] Y0[7] Y0[6] Y0[5] Samples
3 Z0[9] Z0[8] Z0[7] Z0[6] Z0[5] Z0[4] Z0[3] Z0[2] Samples
4 X1[6] X1[5] X1[4] X1[3] X1[2] X1[1] X1[0] Z0[10] Samples
5 Y1[3] Y1[2] Y1[1] Y1[0] X1[10] X1[9] X1[8] X1[7] Samples
6 Z1[0] Y1[10] Y1[9] Y1[8] Y1[7] Y1[6] Y1[5] Y1[4] Samples
7 Z1[8] Z1[7] Z1[6] Z1[5] Z1[4] Z1[3] Z1[2] Z1[1] Samples
8 X2[5] X2[4] X2[3] X2[2] X2[1] X2[0] Z1[10] Z1[9] Samples
9 Y2[2] Y2[1] Y2[0] X2[10] X2[9] X2[8] X2[7] X2[6] Samples
10 Y2[10] Y2[9] Y2[8] Y2[7] Y2[6] Y2[5] Y2[4] Y2[3] Samples
11 Z2[7] Z2[6] Z2[5] Z2[4] Z2[3] Z2[2] Z2[1] Z2[0] Samples
12 X3[4] X3[3] X3[2] X3[1] X3[0] Z2[10] Z2[9] Z2[8] Samples
13 Y3[1] Y3[0] X3[10] X3[9] X3[8] X3[7] X3[6] X3[5] Samples
14 Y3[9] Y3[8] Y3[7] Y3[6] Y3[5] Y3[4] Y3[3] Y3[2] Samples
15 Z3[6] Z3[5] Z3[4] Z3[3] Z3[2] Z3[1] Z3[0] Y3[10] Samples
16 X4[3] X4[2] X4[1] X4[0] Z3[10] Z3[9] Z3[8] Z3[7] Samples
17 Y4[0] X4[10] X4[9] X4[8] X4[7] X4[6] X4[5] X4[4] Samples
18 Y4[8] Y4[7] Y4[6] Y4[5] Y4[4] Y4[3] Y4[2] Y4[1] Samples
19 Z4[5] Z4[4] Z4[3] Z4[2] Z4[1] Z4[0] Y4[10] Y4[9] Samples
20 X5[2] X5[1] X5[0] Z4[10] Z4[9] Z4[8] Z4[7] Z4[6] Samples
21 X5[10] X5[9] X5[8] X5[7] X5[6] X5[5] X5[4] X5[3] Samples
22 Y5[7] Y5[6] Y5[5] Y5[4] Y5[3] Y5[2] Y5[1] Y5[0] Samples
23 Z5[4] Z5[3] Z5[2] Z5[1] Z5[0] Y5[10] Y5[9] Y5[8] Samples
24 X6[1] X6[0] Z5[10] Z5[9] Z5[8] Z5[7] Z5[6] Z5[5] Samples
25 X6[9] X6[8] X6[7] X6[6] X6[5] X6[4] X6[3] X6[2] Samples
26 Y6[6] Y6[5] Y6[4] Y6[3] Y6[2] Y6[1] Y6[0] X6[10] Samples
27 Z6[3] Z6[2] Z6[1] Z6[0] Y6[10] Y6[9] Y6[8] Y6[7] Samples
28 --- Z6[10] Z6[9] Z6[8] Z6[7] Z6[6] Z6[5] Z6[4] Samples

Document revision

Unreleased

[1.2.1] - 2024-12-23

Added

[1.2.0] - 2024-11-22

Added

[1.1.0] - 2024-07-12

Added

[1.0.1] - 2024-06-21

Added

Changed

[1.0.0] - 2024-05-20

Changed

[0.16.0] - 2024-05-10

Added

Changed

[0.15.0] - 2024-04-17

Added

[0.14.0] - 2024-03-27

Added

Changed

[0.13.0] - 2024-03-20

Added

Changed

[0.12.0] - 2024-02-19

Added

[0.11.0] - 2024-01-18

Added

[0.10.0] - 2023-12-20

Added

Changed

Fixed

[0.8.0] - 2023-11-27

Added

Removed

[0.7.1] - 2023-11-10

Added

[0.7.0] - 2023-10-30

Added

[0.6.1] - 2023-10-13

Added

Changed

[0.6.0] - 2023-10-04

Added

Changed

[0.5.0] - 2023-09-15

Changed

[0.4.0] - 2023-08-31

Added

[0.3.0] - 2023-08-23

Added

[0.2.0] - 2023-08-14

Fixed

Added

Changed

[0.1.0] - 2023-07-19

Initial document release - Partial description of the TLV protocol.

EXTRAS

Some C code to play with structures

#include <stdint.h>
#include <stdio.h>
#include <string.h>

#define TLV_HEADER_LENGTH 3u

typedef enum __attribute__((packed)) Protocol_FrameType_e
{
    /* Anchor -> PC*/
    Protocol_Frame_ToF                   = 0x01,
    Protocol_Frame_Serial                = 0x02,
    Protocol_Frame_AdminDistributionData = 0x03,
    Protocol_Frame_CompressedData        = 0x04,
    Protocol_Frame_AccelerometerData     = 0x05,
    Protocol_Frame_GyroscopeData         = 0x06,
    Protocol_Frame_MagnetometerData      = 0x07,
    Protocol_Frame_Sync_Blink            = 201,
    Protocol_Frame_Sync_Shutdown         = 140,
    Protocol_Frame_SetUWBConfig          = 139,

} Protocol_FrameType_t;

typedef struct __attribute__((packed)) Protocol_Frame_s
{
    uint8_t type;
    uint16_t length;
    uint8_t value[];

} Protocol_Frame_t;


typedef struct __attribute__((packed)) ToF_Tag_s
{
    uint8_t did[3];
    struct
    {
        uint8_t receivedByTag : 4;    /** bits 0:3 */
        uint8_t receivedByAnchor : 4; /** bits 4:7 */
    } rssi;

    uint16_t tof[];
} ToF_Tag_t;

typedef struct __attribute__((packed)) ToF_TagList_s
{
    struct
    {
        uint8_t numberOfToFs : 6; /** bits 0:5 */
        uint8_t precision : 2;    /** bits 6:7 */
    };

    uint8_t tagCount;
    uint8_t tagData[];

} ToF_TagList_t;

typedef struct __attribute__((packed)) Protocol_ToFReport_s
{
    uint8_t did[3];
    uint16_t cycleId;

    struct
    {
        uint8_t did[3];
        struct
        {
            uint8_t receivedByTag : 4;    /** bits 0:3 */
            uint8_t receivedByAnchor : 4; /** bits 4:7 */
        } rssi;

        uint16_t tof[4];
    } a2a;

    uint8_t tagListsCount;
    uint8_t tagListsData[];

} Protocol_ToFReport_t;

typedef struct __attribute__((packed)) Protocol_Sensors_s
{
    uint8_t did[3];
    uint16_t cycleId;
    uint8_t slotId;
    uint8_t data[];
} Protocol_Sensors_t;

typedef struct __attribute__((packed)) Protocol_AdminDistributionInfo_s
{
    uint8_t did[3];
    uint16_t cycleId;

    /* Constants */

    uint8_t apiVersion;
    uint8_t fwVersion;
    uint8_t hwid;
    uint8_t MAC[6];

    /* Config */
    struct
    {
        uint8_t deviceOffset;
        uint8_t frequency;
        uint8_t seat;
    } cycle;

    uint8_t serialNumber[4];
    char name[9];
    char group[3];
    uint8_t license[2];

    struct
    {
        uint8_t txPowerPreset;
    } bt;

    struct
    {
        uint8_t ch3AntennaDelay;
        uint8_t ch5AntennaDelay;
        uint8_t txPowerPreset;
    } uwb;

    struct
    {
        uint8_t boardConfig;
        uint8_t pressureCallibration;

        struct
        {
            uint8_t x;
            uint8_t y;
            uint8_t z;
        } magnetoBias;

        struct
        {
            uint8_t x;
            uint8_t y;
            uint8_t z;
        } magnetoScalar;

        struct
        {
            uint8_t x;
            uint8_t y;
            uint8_t z;
        } accelerometerBias;

    } sensorsConfig;

    /* Dynamic sensor data */
    struct
    {
        uint8_t vbat;
        uint8_t motionByte;
        uint8_t pressure;
        uint8_t heartRate;
        uint8_t mox;
    } sensorsData;

} Protocol_AdminDistributionInfo_t;

typedef struct __attribute__((packed)) SM_Protocol_CommandShutdown_s
{
    struct __attribute__((packed))
    {
        uint8_t did[3];
    } device[4];

} SM_Protocol_CommandShutdown_t;

typedef struct __attribute__((packed)) SM_Protocol_CommandBlink_s
{
    struct __attribute__((packed))
    {
        uint8_t did[3];
        struct
        {
            uint8_t pattern : 6;    /** bits 0:5 */
            uint8_t brightness : 2; /** bits 6:7 */
        };

        uint16_t cycleId;
    } device[2];

} SM_Protocol_CommandBlink_t;

typedef struct __attribute__((packed)) SM_Protocol_CommandSetSeatBasic_s
{
    struct __attribute__((packed))
    {
        uint8_t did[3];
        uint8_t seat;
    } device[3];

} SM_Protocol_CommandSetSeatBasic_t;

typedef struct __attribute__((packed)) SM_Protocol_CommandSetUWBConfig_s
{
    struct __attribute__((packed))
    {
        uint8_t did[3];
        int8_t ch3;
        int8_t ch5;
        uint8_t txPower;
    } device[2];

} SM_Protocol_CommandSetUWBConfig_t;

#define ARRAY_SIZE(type) (sizeof(type) / sizeof(type[0]))

uint32_t bitStitcher_compress(
    const int32_t* const input,
    uint32_t* const output,
    uint8_t length,
    uint8_t bitsPerSample,
    uint8_t initialOffset)
{
    const uint8_t outputBits = sizeof(*output) * 8u;
    const uint32_t valueMask = ~(-(1 << bitsPerSample));

    uint8_t bitsLeft    = outputBits - initialOffset;
    uint32_t* outputPtr = &output[0];
    for (uint8_t inputIdx = 0; inputIdx < length; ++inputIdx)
    {
        const uint32_t value = input[inputIdx] & valueMask;
        if (bitsLeft >= bitsPerSample)
        {
            *outputPtr |= value << (outputBits - bitsLeft);
            bitsLeft -= bitsPerSample;

            if (bitsLeft == 0)
            {
                ++outputPtr;
                bitsLeft = outputBits;
            }
        }
        else
        {
            const uint32_t part1Mask = ~(-(1 << bitsLeft));
            const uint32_t part1     = value & part1Mask;
            const uint32_t part2     = value >> bitsLeft;
            const uint8_t part2Bits  = bitsPerSample - bitsLeft;

            *outputPtr |= part1 << (outputBits - bitsLeft);
            ++outputPtr;
            *outputPtr |= part2;

            bitsLeft = outputBits - part2Bits;
        }
    }

    return (uint32_t)((uintptr_t)outputPtr - (uintptr_t)output)
           + (sizeof(*output) - (bitsLeft / 8u));
}

static void bitStitcher_helper_print(uint32_t* buffer, uint8_t length)
{
    printf("WORDS\n");
    for (uint8_t i = 0; i < length; ++i)
    {
        printf("%02d: ", i);
        for (int8_t j = 31; j >= 0; --j)
        {
            printf("%d", (buffer[i] >> j) & 0x01);
            if (j % 8 == 0)
            {
                printf(" ");
            }
        }

        printf("  |  0x%08x\n", buffer[i]);
    }
}

static void bitStitcher_helper_printBytes(uint8_t* buffer, uint8_t length)
{
    printf("BYTES\n");
    for (uint8_t i = 0; i < length; ++i)
    {
        printf("%02d: ", i);
        for (int8_t j = 7; j >= 0; --j)
        {
            printf("%d", (buffer[i] >> j) & 0x01);
        }

        printf("  |  0x%02x\n", buffer[i]);
    }
}

void print_raw(uint8_t* buffer, uint16_t size)
{
    Protocol_Frame_t* frame = (Protocol_Frame_t*)buffer;

    printf("/* TLV header: type = %u, length = %u */\n\r", frame->type, frame->length);
    printf("0x%02x, 0x%02x, 0x%02x, \n\r", buffer[0], buffer[1], buffer[2]);
    printf("/* Value buffer */\n\r");
    for (uint8_t i = TLV_HEADER_LENGTH; i < size;)
    {
        for (uint8_t j = 0; j < 8 && i < size; ++j, ++i)
        {
            printf("0x%02x, ", buffer[i]);
        }
        printf("\n\r");
    }
}

void test_tof(void)
{
    uint8_t raw[1000] = {0};

    Protocol_Frame_t* frame = (Protocol_Frame_t*)raw;
    frame->type             = Protocol_Frame_ToF;

    Protocol_ToFReport_t* value = (Protocol_ToFReport_t*)frame->value;

    value->did[0]  = 5;
    value->did[1]  = 10;
    value->did[2]  = 15;
    value->cycleId = 5000;

    /* There is only one A2A report */
    value->a2a.did[0]                = 20;
    value->a2a.did[1]                = 40;
    value->a2a.did[2]                = 60;
    value->a2a.rssi.receivedByAnchor = 5;
    value->a2a.rssi.receivedByTag    = 12;
    value->a2a.tof[0]                = 300;
    value->a2a.tof[1]                = 302;
    value->a2a.tof[2]                = 301;
    value->a2a.tof[3]                = 303;

    value->tagListsCount = 1;

    /* Fill tag lists */
    ToF_TagList_t* taglist0 = (ToF_TagList_t*)value->tagListsData;
    taglist0->numberOfToFs  = 3;
    taglist0->precision     = 0;
    taglist0->tagCount      = 2;

    /* Size Helpers */
    const size_t headerSize = sizeof(value->a2a) + sizeof(value->cycleId)
                              + sizeof(value->did) + sizeof(value->tagListsCount);

    const size_t list0TagSize =
        sizeof(((ToF_Tag_t*)0)->did) + sizeof(((ToF_Tag_t*)0)->rssi)
        + (taglist0->numberOfToFs * (sizeof(((ToF_Tag_t*)0)->tof[0])));

    const size_t list0Size = sizeof(uint8_t) + sizeof(((ToF_TagList_t*)0)->tagCount)
                             + taglist0->tagCount * list0TagSize;

    /* Fill Tags */
    ToF_Tag_t* tag0 = (ToF_Tag_t*)&taglist0->tagData[0 * list0TagSize];

    tag0->did[0]                = 33;
    tag0->did[1]                = 34;
    tag0->did[2]                = 35;
    tag0->rssi.receivedByAnchor = 10;
    tag0->rssi.receivedByTag    = 12;
    tag0->tof[0]                = 200;
    tag0->tof[1]                = 201;
    tag0->tof[2]                = 202;

    ToF_Tag_t* tag1 = (ToF_Tag_t*)&taglist0->tagData[1 * list0TagSize];

    tag1->did[0]                = 33;
    tag1->did[1]                = 34;
    tag1->did[2]                = 36;
    tag1->rssi.receivedByAnchor = 11;
    tag1->rssi.receivedByTag    = 13;
    tag1->tof[0]                = 204;
    tag1->tof[1]                = 205;
    tag1->tof[2]                = 206;

    frame->length = headerSize + list0Size;
    print_raw(raw, frame->length + TLV_HEADER_LENGTH);
}

void test_sensors(void)
{
    uint8_t raw[1000] = {0};

    Protocol_Frame_t* frame = (Protocol_Frame_t*)raw;
    frame->type             = Protocol_Frame_Serial;


    Protocol_Sensors_t* value = (Protocol_Sensors_t*)frame->value;
    value->did[0]             = 10;
    value->did[1]             = 20;
    value->did[2]             = 30;
    value->cycleId            = 10000;
    value->slotId             = 50;

    sprintf(value->data, "AIRTLS");


    frame->length = sizeof(Protocol_Sensors_t) + strlen(value->data);

    print_raw(raw, frame->length + TLV_HEADER_LENGTH);
}

void test_compressedData(void)
{
    uint8_t raw[1000] = {0};

    Protocol_Frame_t* frame = (Protocol_Frame_t*)raw;
    frame->type             = Protocol_Frame_CompressedData;


    Protocol_Sensors_t* value = (Protocol_Sensors_t*)frame->value;
    value->did[0]             = 10;
    value->did[1]             = 20;
    value->did[2]             = 30;
    value->cycleId            = 10000;
    value->slotId             = 50;

    sprintf(value->data, "AIRTLS");

    for (uint8_t i = 0; i < 100; ++i)
    {
        value->data[i] = i;
    }

    frame->length = sizeof(Protocol_Sensors_t) + 100;

    print_raw(raw, frame->length + TLV_HEADER_LENGTH);
}

void test_accelerometerData(void)
{
    // clang-format off
    static const int32_t samples[] = {
        -3044,      2329,        1406,
        -3054,      2335,        1427,
        -3048,      2329,        1420,
        -3066,      2283,        1467,
        -3059,      2262,        1454,
        -3085,      2270,        1471,
        -120064,    -131064,     130979,
        131070,     131069,      124216,
        -28471,     -130818,     63966,
        -3044,      2329,        1406,
        -3054,      2335,        1427,
        -3048,      2329,        1420,
        -3066,      2283,        1467,
        -3059,      2262,        1454,
        -3085,      2270,        1471,
        -3059,      2262,        1454,
    };
    // clang-format on

    uint8_t raw[1000] = {0};

    Protocol_Frame_t* frame = (Protocol_Frame_t*)raw;
    frame->type             = Protocol_Frame_AccelerometerData;


    Protocol_Sensors_t* value = (Protocol_Sensors_t*)frame->value;
    value->did[0]             = 10;
    value->did[1]             = 20;
    value->did[2]             = 30;
    value->cycleId            = 1234;
    value->slotId             = 77;

    uint8_t accelerometerDataSize =
        bitStitcher_compress(samples, value->data, ARRAY_SIZE(samples), 18, 8);

    frame->length = sizeof(Protocol_Sensors_t) + accelerometerDataSize;

    print_raw(raw, frame->length + TLV_HEADER_LENGTH);
}

void test_gyroscopeData(void)
{
    // clang-format off
    static const int32_t samples[] = {
        -647, -229, 427,
        -333, -229, 274,
        -374, -229, 427,
        -206, -167, 290,
        -390, -188, 290,
        -229, -251, 381,
        -354, -229, 381,
        -312, -229, 274,
        -167, -206, 290,
        -251, -229, 381,
        -229, -333, 274,
        -229, -374, 427,
        -188, -390, 290,
        -229, -354, 381,
        -229, -647, 427,
        -229, -312, 274
    };
    // clang-format on

    uint8_t raw[1000] = {0};

    Protocol_Frame_t* frame = (Protocol_Frame_t*)raw;
    frame->type             = Protocol_Frame_GyroscopeData;


    Protocol_Sensors_t* value = (Protocol_Sensors_t*)frame->value;
    value->did[0]             = 10;
    value->did[1]             = 20;
    value->did[2]             = 30;
    value->cycleId            = 4321;
    value->slotId             = 77;

    uint8_t accelerometerDataSize =
        bitStitcher_compress(samples, value->data, ARRAY_SIZE(samples), 19, 8);

    frame->length = sizeof(Protocol_Sensors_t) + accelerometerDataSize;

    print_raw(raw, frame->length + TLV_HEADER_LENGTH);
}

void test_magnetometerData(void)
{
    // clang-format off
    static const int32_t samples[] = {
        -248, -579, -425,
        -259, -580, -430,
        -258, -577, -429,
        -258, -577, -432,
        -257, -579, -432,
        -248, -578, -429,
        -259, -580, -425,
        -257, -578, -432,
        -259, -577, -430,
        -258, -580, -425,
        -257, -577, -432,
        -248, -579, -429,
        -259, -578, -430,
        -258, -577, -425,
        -248, -580, -432,
        -258, -578, -429
    };
    // clang-format on

    uint8_t raw[1000] = {0};

    Protocol_Frame_t* frame = (Protocol_Frame_t*)raw;
    frame->type             = Protocol_Frame_MagnetometerData;


    Protocol_Sensors_t* value = (Protocol_Sensors_t*)frame->value;
    value->did[0]             = 10;
    value->did[1]             = 20;
    value->did[2]             = 30;
    value->cycleId            = 1111;
    value->slotId             = 33;

    uint8_t accelerometerDataSize =
        bitStitcher_compress(samples, value->data, ARRAY_SIZE(samples), 11, 8);

    frame->length = sizeof(Protocol_Sensors_t) + accelerometerDataSize;

    print_raw(raw, frame->length + TLV_HEADER_LENGTH);
}

void test_adminDistribution(void)
{
    uint8_t raw[1000] = {0};

    Protocol_Frame_t* frame = (Protocol_Frame_t*)raw;
    frame->type             = Protocol_Frame_AdminDistributionData;
    frame->length           = sizeof(Protocol_AdminDistributionInfo_t);

    Protocol_AdminDistributionInfo_t* value =
        (Protocol_AdminDistributionInfo_t*)frame->value;

    value->did[0]     = 5;
    value->did[1]     = 10;
    value->did[2]     = 15;
    value->cycleId    = 7000;
    value->apiVersion = 1;
    value->fwVersion  = 2;
    value->hwid       = 3;

    value->MAC[0] = 0x11;
    value->MAC[1] = 0x12;
    value->MAC[2] = 0x13;
    value->MAC[3] = 0x14;
    value->MAC[4] = 0x15;
    value->MAC[5] = 0x16;

    value->serialNumber[0] = 1;
    value->serialNumber[1] = 0;
    value->serialNumber[2] = 0;
    value->serialNumber[3] = 0;

    value->license[0] = 221;
    value->license[1] = 222;

    value->cycle.deviceOffset                 = 0;
    value->cycle.frequency                    = 50;
    value->cycle.seat                         = 34;
    value->bt.txPowerPreset                   = 2;
    value->uwb.ch3AntennaDelay                = 99;
    value->uwb.ch5AntennaDelay                = 100;
    value->uwb.txPowerPreset                  = 1;
    value->sensorsConfig.boardConfig          = 0x00;
    value->sensorsConfig.pressureCallibration = 10;
    value->sensorsConfig.magnetoBias.x        = 7;
    value->sensorsConfig.magnetoBias.y        = 8;
    value->sensorsConfig.magnetoBias.z        = 9;
    value->sensorsConfig.magnetoScalar.x      = 4;
    value->sensorsConfig.magnetoScalar.y      = 5;
    value->sensorsConfig.magnetoScalar.z      = 6;
    value->sensorsConfig.accelerometerBias.x  = 1;
    value->sensorsConfig.accelerometerBias.y  = 2;
    value->sensorsConfig.accelerometerBias.z  = 3;
    value->sensorsData.vbat                   = 30;
    value->sensorsData.motionByte             = 1;
    value->sensorsData.pressure               = 98;
    value->sensorsData.heartRate              = 99;
    value->sensorsData.mox                    = 100;

    sprintf(value->name, "AIRTLS");
    sprintf(value->group, "AB");

    print_raw(raw, frame->length + TLV_HEADER_LENGTH);
}

void test_shutdown(void)
{
    uint8_t raw[1000] = {0};

    Protocol_Frame_t* frame = (Protocol_Frame_t*)raw;
    frame->type             = Protocol_Frame_Sync_Shutdown;
    frame->length           = sizeof(SM_Protocol_CommandShutdown_t);

    SM_Protocol_CommandShutdown_t* value =
        (SM_Protocol_CommandShutdown_t*)frame->value;

    value->device[0].did[0] = 0x01;
    value->device[0].did[1] = 0x02;
    value->device[0].did[2] = 0x03;

    value->device[1].did[0] = 0x00;
    value->device[1].did[1] = 0x00;
    value->device[1].did[2] = 0x00;

    value->device[2].did[0] = 0x21;
    value->device[2].did[1] = 0x22;
    value->device[2].did[2] = 0x23;

    value->device[3].did[0] = 0x12;
    value->device[3].did[1] = 0x22;
    value->device[3].did[2] = 0x32;

    print_raw(raw, frame->length + TLV_HEADER_LENGTH);

}

void test_blink(void)
{
    uint8_t raw[1000] = {0};

    Protocol_Frame_t* frame = (Protocol_Frame_t*)raw;
    frame->type             = Protocol_Frame_Sync_Blink;
    frame->length           = sizeof(SM_Protocol_CommandBlink_t);

    SM_Protocol_CommandBlink_t* value =
        (SM_Protocol_CommandBlink_t*)frame->value;

    value->device[0].did[0] = 0x01;
    value->device[0].did[1] = 0x02;
    value->device[0].did[2] = 0x03;
    value->device[0].brightness = 0x03;
    value->device[0].pattern = 0x06;
    value->device[0].cycleId = 2200;

    value->device[1].did[0] = 0x00;
    value->device[1].did[1] = 0x00;
    value->device[1].did[2] = 0x00;
    value->device[1].brightness = 0x01;
    value->device[1].pattern = 0x00;
    value->device[1].cycleId = 2200;

    print_raw(raw, frame->length + TLV_HEADER_LENGTH);
}

void test_seatAllocation(void)
{
    uint8_t raw[1000] = {0};

    Protocol_Frame_t* frame = (Protocol_Frame_t*)raw;
    frame->type             = 254;
    frame->length           = sizeof(SM_Protocol_CommandSetSeatBasic_t);

    SM_Protocol_CommandSetSeatBasic_t* value =
        (SM_Protocol_CommandSetSeatBasic_t*)frame->value;

    value->device[0].did[0] = 0x01;
    value->device[0].did[1] = 0x02;
    value->device[0].did[2] = 0x03;
    value->device[0].seat = 1;

    value->device[1].did[0] = 0x10;
    value->device[1].did[1] = 0x20;
    value->device[1].did[2] = 0x30;
    value->device[1].seat = 5;

    value->device[2].did[0] = 0x11;
    value->device[2].did[1] = 0x22;
    value->device[2].did[2] = 0x33;
    value->device[2].seat = 10;


    print_raw(raw, frame->length + TLV_HEADER_LENGTH);
}

void test_setUWBConfig(void)
{
    uint8_t raw[1000] = {0};

    Protocol_Frame_t* frame = (Protocol_Frame_t*)raw;
    frame->type             = Protocol_Frame_SetUWBConfig;
    frame->length           = sizeof(SM_Protocol_CommandSetUWBConfig_t);

    SM_Protocol_CommandSetUWBConfig_t* value =
        (SM_Protocol_CommandSetUWBConfig_t*)frame->value;

    value->device[0].did[0] = 0x01;
    value->device[0].did[1] = 0x02;
    value->device[0].did[2] = 0x03;
    value->device[0].ch3 = -5;
    value->device[0].ch5 = 5;
    value->device[0].txPower = 1;

    value->device[1].did[0] = 0x10;
    value->device[1].did[1] = 0x20;
    value->device[1].did[2] = 0x30;
    value->device[1].ch3 = 10;
    value->device[1].ch5 = -10;
    value->device[1].txPower = 1;

    print_raw(raw, frame->length + TLV_HEADER_LENGTH);
}

int main()
{
    test_tof();
    test_sensors();
    test_adminDistribution();
    test_compressedData();
    test_accelerometerData();
    test_gyroscopeData();
    test_shutdown();
    test_blink();
    test_seatAllocation();
    test_setUWBConfig();

    return 0;
}

To play with raw data structures some C code was provided. It can be run in online GCC compiler:

www.onlinegdb.com

c