Qt C++ Desktop Reading Tutorial
Build a lightweight Qt SerialPort desktop tool to read ANROT packets, validate live data, and prototype GUI workflows.
- qt
- cplusplus
- serial
- tutorial
Qt C++ example
Protocol Support
| Protocol / frame | Support | How this example handles it |
|---|---|---|
0x91 IMUSOL | Supported | Decodes a single-device float frame into receive_imusol and updates the UI. |
0x62 GWSOL | Supported | Decodes gateway float collection frames into receive_gwsol. |
0x63 GWSOL Compact | Supported | Decodes compact int16 gateway frames with 0x93 node blocks, converts them to float values, and displays them. |
0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xD1 | Supported | Parses loose single-field output modes. |
0xF0 Pressure | Skipped | The decoder skips this item and the UI does not display pressure. |
This example uses Qt Widgets and Qt SerialPort to build a small desktop tool. Use it to confirm a device is streaming correctly, then drop the decoder into your own C++ / GUI project.
Download the example
Download and unzip: demo-qt-cplusplus-en.zip
Main structure:
demo_qtc++/
├── CHReceiver.pro
├── main.cpp
├── mainwindow.h / mainwindow.cpp / mainwindow.ui
└── include/
├── packet.h / packet.cpp
└── imu_data_decode.h / imu_data_decode.cpp
Test environment
- Qt 5.9.9 + Windows 10.
- The device is connected over USB or USB-UART.
- Supports the ANROT binary frames
0x91,0x62, and0x63.
1. Open the project
Open demo_qtc++/CHReceiver.pro in Qt Creator. Qt SerialPort is already enabled in the .pro file:
QT += serialport
To move the decoder into your own Qt project, you need at least:
#include "include/imu_data_decode.h"
#include "include/packet.h"
and add include/packet.* and include/imu_data_decode.* to the project.
2. Initialize the decoder
Call once before you start receiving serial data:
imu_data_decode_init();
3. Receive data
On every QSerialPort::readyRead, read all buffered bytes and feed each one to packet_decode():
void MainWindow::read_serial()
{
QByteArray arr = m_reader.readAll();
for (int i = 0; i < arr.size(); i++) {
uint8_t c = static_cast<uint8_t>(arr[i]);
packet_decode(c);
}
}
packet_decode() handles frame sync, length, CRC, and payload items. After a frame is decoded successfully, the result is written into the global data structures.
4. Read single-device data
A single IMU’s 0x91 data lands in receive_imusol (note: it is receive_imusol, the global instance — not the type receive_imusol_packet_t). All fields are float:
float roll = receive_imusol.eul[0];
float pitch = receive_imusol.eul[1];
float yaw = receive_imusol.eul[2];
float ax = receive_imusol.acc[0];
float ay = receive_imusol.acc[1];
float az = receive_imusol.acc[2];
float qw = receive_imusol.quat[0];
float qx = receive_imusol.quat[1];
float qy = receive_imusol.quat[2];
float qz = receive_imusol.quat[3];
The decoded structures are declared in imu_data_decode.h:
typedef struct receive_imusol_packet_t {
uint8_t tag; /* source packet tag (0x91 / 0x93) */
uint8_t id; /* node device ID */
uint32_t times; /* timestamp (ms) */
float acc[3]; /* g */
float gyr[3]; /* deg/s */
float mag[3]; /* uT */
float eul[3]; /* Roll, Pitch, Yaw (deg) */
float quat[4]; /* W, X, Y, Z */
} receive_imusol_packet_t;
typedef struct receive_gwsol_packet_t {
uint8_t tag; /* 0x62 or 0x63 */
uint8_t gw_id; /* gateway network ID */
uint8_t n; /* node count (0~16) */
receive_imusol_packet_t receive_imusol[MAX_LENGTH];
} receive_gwsol_packet_t;
5. Read gateway multi-node data
0x62 and 0x63 are both normalized into receive_gwsol.receive_imusol[], so the same loop handles either format:
if (receive_gwsol.tag == KItemGWSOL || receive_gwsol.tag == KItemGWSOL_Compact)
{
uint8_t gw_id = receive_gwsol.gw_id;
uint8_t node_count = receive_gwsol.n;
for (int i = 0; i < node_count; i++)
{
uint8_t node_id = receive_gwsol.receive_imusol[i].id;
float roll = receive_gwsol.receive_imusol[i].eul[0];
float pitch = receive_gwsol.receive_imusol[i].eul[1];
float yaw = receive_gwsol.receive_imusol[i].eul[2];
}
}
0x63 is a compact format with smaller frames; it is still decoded into the same float fields, so UI or logging code can use it directly.
Packet reference
0X91 (IMUSOL)
A total of 76 bytes, the newly added data packet is used to replace A0, B0, C0, D0, D1 and other data packets. The raw sensor output and attitude solution data of the IMU are integrated.
| byte offset | type | bytes | unit | description | | -------- | -------- | ----- | -------------------- | ---- -------------------------------------------------- ------ | | 0 | uint8_t | 1 | - | Packet label: 0x91 | | 1 | uint8_t | 1 | - | ID | | 2 | - | 6 | - | Reserved | | 8 | uint32_t | 4 | ms | Timestamp information, accumulated since the system is powered on, increments by 1 every millisecond | | 12 | float | 12 | 1G (1G = 1 gravitational acceleration) | X, Y, Z axis acceleration, note that the unit is different from 0xA0 | | 24 | float | 12 | deg/s | Angular velocity of X, Y, Z axis, note that the unit is different from 0xB0 | | 36 | float | 12 | uT | Magnetic field strength of X, Y, Z axis (supported by Hi229, note that the unit is different from 0xC0) | | 48 | float | 12 | deg | Node Euler angle set, the order is: Roll (Roll), Pitch (Pitch), Yaw (Yaw) (note that the order and unit are different from 0xD0 packets) | | 60 | float | 16 | - | Node quaternion set, in order WXYZ |
0x62 (GWSOL)
The first 8 bytes of the Hi221Dongle (wireless receiver) data packet are receiver information. The latter is divided into N data blocks. Each data block describes the attitude data of a node (up to 16 nodes are supported). The size of each data block is 76 bytes, and the data structure is the same as 0x91. This protocol has a large amount of data. It is recommended to adjust the baud rate to above 460800 to obtain a stable frame rate output.
The protocol structure is as follows:
| byte offset | bytes | type | unit | description | | ------------------------ | ----- | ------- | ---- | ----- -------------------- | | 0 | 1 | uint8_t | - | Packet label: 0x62 | | 1 | 1 | uint8_t | - | GWID, receiver network ID | | 2 | 1 | uint8_t | - | N, this frame contains the number of node data blocks | | 3 | 5 | - | - | Reserved | | ----Start of node data block---- | - | - | - | Data structure is the same as 0x91 | | 8+76N(N=0-15) | 1 | uint8_t | - | Packet label: 0x91 | | 9+76N(N=0-15) | 1 | uint8_t | - | ID of node N | | 10+76N | 10 | - | - | Reserved | | 20+76N | 12 | float | - | Node N triaxial acceleration | | 32+76N | 12 | float | - | Node N triaxial angular velocity | | 44+76N | 12 | float | - | Node N-axis magnetic field strength | | 56+76N | 12 | float | - | Node N Euler angles | | 68+76N | 16 | float | - | Node N Quaternion | | ----End of node data block---- | - | - | - | ----------- |