software copyright
This commit is contained in:
@@ -0,0 +1,376 @@
|
||||
/**
|
||||
* 自然写互动课堂应用开发SDK软件 V1.0
|
||||
* BLE协议解析核心模块 - 蓝牙5.0点阵笔通信协议实现
|
||||
*
|
||||
* 跨平台C语言核心库,负责解析点阵笔BLE GATT数据
|
||||
* 提供笔迹坐标解包、协议帧校验、数据压缩解压等底层能力
|
||||
* 通过JNI/ObjC Bridge/FFI供各平台SDK调用
|
||||
*/
|
||||
|
||||
#ifndef BLE_PROTOCOL_H
|
||||
#define BLE_PROTOCOL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ==================== 协议常量定义 ==================== */
|
||||
|
||||
/* BLE GATT Service UUID(自定义服务) */
|
||||
#define WRITECH_SERVICE_UUID "0000FFE0-0000-1000-8000-00805F9B34FB"
|
||||
/* 笔迹数据Characteristic UUID */
|
||||
#define STROKE_DATA_CHAR_UUID "0000FFE1-0000-1000-8000-00805F9B34FB"
|
||||
/* 设备信息Characteristic UUID */
|
||||
#define DEVICE_INFO_CHAR_UUID "0000FFE2-0000-1000-8000-00805F9B34FB"
|
||||
/* 配置写入Characteristic UUID */
|
||||
#define CONFIG_WRITE_CHAR_UUID "0000FFE3-0000-1000-8000-00805F9B34FB"
|
||||
/* OTA DFU Characteristic UUID */
|
||||
#define OTA_DFU_CHAR_UUID "0000FFE4-0000-1000-8000-00805F9B34FB"
|
||||
|
||||
/* 协议帧标志 */
|
||||
#define FRAME_HEADER_MAGIC 0xAA55
|
||||
#define FRAME_MAX_PAYLOAD_SIZE 240 /* MTU=247, 减去帧头7字节 */
|
||||
#define MAX_POINTS_PER_FRAME 34 /* 每帧最多34个坐标点 */
|
||||
|
||||
/* 帧类型定义 */
|
||||
#define FRAME_TYPE_STROKE_DATA 0x01 /* 笔迹坐标数据 */
|
||||
#define FRAME_TYPE_PEN_UP 0x02 /* 抬笔事件 */
|
||||
#define FRAME_TYPE_PEN_DOWN 0x03 /* 落笔事件 */
|
||||
#define FRAME_TYPE_DEVICE_STATUS 0x04 /* 设备状态(电量等) */
|
||||
#define FRAME_TYPE_OFFLINE_SYNC 0x05 /* 离线数据同步 */
|
||||
#define FRAME_TYPE_OTA_DATA 0x06 /* OTA升级数据 */
|
||||
#define FRAME_TYPE_CONFIG_RSP 0x07 /* 配置响应 */
|
||||
|
||||
/* ==================== 数据结构定义 ==================== */
|
||||
|
||||
/**
|
||||
* 原始笔迹坐标点(7字节紧凑编码)
|
||||
* x: 16位无符号整数,点阵坐标X(分辨率约300DPI)
|
||||
* y: 16位无符号整数,点阵坐标Y
|
||||
* pressure: 8位无符号整数,压力值(0-255)
|
||||
* timestamp_delta: 16位无符号整数,距上一点的时间差(毫秒)
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t x; /* X坐标(大端序) */
|
||||
uint16_t y; /* Y坐标(大端序) */
|
||||
uint8_t pressure; /* 压力值 0-255 */
|
||||
uint16_t timestamp_delta; /* 时间增量(毫秒) */
|
||||
} __attribute__((packed)) StrokePointRaw;
|
||||
|
||||
/**
|
||||
* 解码后的笔迹坐标点
|
||||
*/
|
||||
typedef struct {
|
||||
float x; /* X坐标(浮点) */
|
||||
float y; /* Y坐标(浮点) */
|
||||
float pressure; /* 压力值 0.0-1.0 */
|
||||
uint32_t timestamp; /* 绝对时间戳(毫秒) */
|
||||
uint8_t pen_state; /* 0=落笔, 1=抬笔 */
|
||||
} StrokePoint;
|
||||
|
||||
/**
|
||||
* BLE协议帧头(7字节)
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t magic; /* 帧头魔数 0xAA55 */
|
||||
uint8_t frame_type; /* 帧类型 */
|
||||
uint8_t sequence; /* 帧序号(0-255循环) */
|
||||
uint16_t payload_length; /* 负载长度 */
|
||||
uint8_t checksum; /* 帧头校验和(XOR) */
|
||||
} __attribute__((packed)) FrameHeader;
|
||||
|
||||
/**
|
||||
* 笔迹数据帧
|
||||
*/
|
||||
typedef struct {
|
||||
FrameHeader header;
|
||||
uint8_t point_count; /* 本帧包含的坐标点数 */
|
||||
uint32_t page_id; /* 点阵码页面ID */
|
||||
StrokePointRaw points[MAX_POINTS_PER_FRAME]; /* 坐标点数组 */
|
||||
uint16_t crc16; /* CRC-16校验 */
|
||||
} __attribute__((packed)) StrokeDataFrame;
|
||||
|
||||
/**
|
||||
* 设备状态帧
|
||||
*/
|
||||
typedef struct {
|
||||
FrameHeader header;
|
||||
uint8_t battery_level; /* 电量百分比 0-100 */
|
||||
uint8_t charging_state; /* 充电状态: 0=未充电, 1=充电中, 2=已充满 */
|
||||
uint16_t firmware_version; /* 固件版本 (major*256+minor) */
|
||||
uint8_t connection_state; /* 连接状态 */
|
||||
uint32_t serial_number; /* 设备序列号 */
|
||||
uint16_t crc16;
|
||||
} __attribute__((packed)) DeviceStatusFrame;
|
||||
|
||||
/**
|
||||
* 解析回调函数类型定义
|
||||
*/
|
||||
typedef void (*on_stroke_point_cb)(const StrokePoint* point, void* user_data);
|
||||
typedef void (*on_pen_event_cb)(uint8_t event_type, uint32_t timestamp, void* user_data);
|
||||
typedef void (*on_device_status_cb)(uint8_t battery, uint8_t charging, uint16_t fw_ver, void* user_data);
|
||||
|
||||
/* ==================== 协议解析器 ==================== */
|
||||
|
||||
/**
|
||||
* BLE协议解析器上下文
|
||||
*/
|
||||
typedef struct {
|
||||
/* 接收缓冲区(处理分包/粘包) */
|
||||
uint8_t recv_buffer[512];
|
||||
size_t recv_length;
|
||||
|
||||
/* 序号跟踪(乱序检测) */
|
||||
uint8_t expected_sequence;
|
||||
|
||||
/* 时间戳基准 */
|
||||
uint32_t base_timestamp;
|
||||
uint32_t last_timestamp;
|
||||
|
||||
/* 统计信息 */
|
||||
uint32_t total_frames;
|
||||
uint32_t total_points;
|
||||
uint32_t error_frames;
|
||||
uint32_t lost_frames;
|
||||
|
||||
/* 回调函数 */
|
||||
on_stroke_point_cb stroke_cb;
|
||||
on_pen_event_cb pen_event_cb;
|
||||
on_device_status_cb status_cb;
|
||||
void* user_data;
|
||||
} BleProtocolParser;
|
||||
|
||||
/**
|
||||
* 初始化协议解析器
|
||||
*/
|
||||
static inline void ble_parser_init(BleProtocolParser* parser) {
|
||||
memset(parser, 0, sizeof(BleProtocolParser));
|
||||
parser->expected_sequence = 0;
|
||||
parser->base_timestamp = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置回调函数
|
||||
*/
|
||||
static inline void ble_parser_set_callbacks(
|
||||
BleProtocolParser* parser,
|
||||
on_stroke_point_cb stroke_cb,
|
||||
on_pen_event_cb pen_event_cb,
|
||||
on_device_status_cb status_cb,
|
||||
void* user_data
|
||||
) {
|
||||
parser->stroke_cb = stroke_cb;
|
||||
parser->pen_event_cb = pen_event_cb;
|
||||
parser->status_cb = status_cb;
|
||||
parser->user_data = user_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算CRC-16校验值(CCITT标准)
|
||||
*/
|
||||
static uint16_t calc_crc16(const uint8_t* data, size_t length) {
|
||||
uint16_t crc = 0xFFFF;
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
crc ^= (uint16_t)data[i] << 8;
|
||||
for (int j = 0; j < 8; j++) {
|
||||
if (crc & 0x8000)
|
||||
crc = (crc << 1) ^ 0x1021;
|
||||
else
|
||||
crc <<= 1;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验帧头
|
||||
*/
|
||||
static int validate_frame_header(const FrameHeader* header) {
|
||||
/* 校验魔数 */
|
||||
if (header->magic != FRAME_HEADER_MAGIC) return -1;
|
||||
/* 校验负载长度 */
|
||||
if (header->payload_length > FRAME_MAX_PAYLOAD_SIZE) return -2;
|
||||
/* 校验帧头XOR校验和 */
|
||||
uint8_t xor_sum = 0;
|
||||
const uint8_t* p = (const uint8_t*)header;
|
||||
for (int i = 0; i < 6; i++) xor_sum ^= p[i];
|
||||
if (xor_sum != header->checksum) return -3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 大端序转小端序(16位)
|
||||
*/
|
||||
static inline uint16_t be16_to_le(uint16_t value) {
|
||||
return (value >> 8) | (value << 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析笔迹数据帧
|
||||
* 从帧中提取坐标点并通过回调函数输出
|
||||
*/
|
||||
static int parse_stroke_frame(BleProtocolParser* parser, const uint8_t* data, size_t length) {
|
||||
if (length < sizeof(FrameHeader) + 5) return -1;
|
||||
|
||||
const FrameHeader* header = (const FrameHeader*)data;
|
||||
|
||||
/* 帧头校验 */
|
||||
if (validate_frame_header(header) != 0) {
|
||||
parser->error_frames++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 序号连续性检查 */
|
||||
if (header->sequence != parser->expected_sequence) {
|
||||
uint8_t lost = header->sequence - parser->expected_sequence;
|
||||
parser->lost_frames += lost;
|
||||
}
|
||||
parser->expected_sequence = header->sequence + 1;
|
||||
|
||||
/* 解析负载 */
|
||||
const uint8_t* payload = data + sizeof(FrameHeader);
|
||||
uint8_t point_count = payload[0];
|
||||
uint32_t page_id = *(uint32_t*)(payload + 1);
|
||||
|
||||
if (point_count > MAX_POINTS_PER_FRAME) {
|
||||
parser->error_frames++;
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* CRC校验(校验帧头+负载) */
|
||||
size_t crc_data_len = length - 2;
|
||||
uint16_t expected_crc = *(uint16_t*)(data + crc_data_len);
|
||||
uint16_t actual_crc = calc_crc16(data, crc_data_len);
|
||||
if (expected_crc != actual_crc) {
|
||||
parser->error_frames++;
|
||||
return -3;
|
||||
}
|
||||
|
||||
/* 解析每个坐标点 */
|
||||
const StrokePointRaw* raw_points = (const StrokePointRaw*)(payload + 5);
|
||||
for (int i = 0; i < point_count; i++) {
|
||||
StrokePoint decoded;
|
||||
decoded.x = (float)be16_to_le(raw_points[i].x);
|
||||
decoded.y = (float)be16_to_le(raw_points[i].y);
|
||||
decoded.pressure = raw_points[i].pressure / 255.0f;
|
||||
|
||||
/* 累加时间增量得到绝对时间戳 */
|
||||
uint16_t delta = be16_to_le(raw_points[i].timestamp_delta);
|
||||
parser->last_timestamp += delta;
|
||||
decoded.timestamp = parser->base_timestamp + parser->last_timestamp;
|
||||
decoded.pen_state = 0; /* 落笔状态 */
|
||||
|
||||
/* 通过回调函数输出 */
|
||||
if (parser->stroke_cb) {
|
||||
parser->stroke_cb(&decoded, parser->user_data);
|
||||
}
|
||||
parser->total_points++;
|
||||
}
|
||||
|
||||
parser->total_frames++;
|
||||
return point_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 输入BLE Notify接收到的数据
|
||||
* 处理分包/粘包,自动检测帧边界并分发解析
|
||||
*/
|
||||
static int ble_parser_feed(BleProtocolParser* parser, const uint8_t* data, size_t length) {
|
||||
/* 追加到接收缓冲区 */
|
||||
if (parser->recv_length + length > sizeof(parser->recv_buffer)) {
|
||||
/* 缓冲区溢出,丢弃旧数据 */
|
||||
parser->recv_length = 0;
|
||||
}
|
||||
memcpy(parser->recv_buffer + parser->recv_length, data, length);
|
||||
parser->recv_length += length;
|
||||
|
||||
int parsed_count = 0;
|
||||
|
||||
/* 扫描缓冲区查找完整帧 */
|
||||
while (parser->recv_length >= sizeof(FrameHeader)) {
|
||||
/* 查找帧头魔数 */
|
||||
if (parser->recv_buffer[0] != 0xAA || parser->recv_buffer[1] != 0x55) {
|
||||
/* 跳过非法字节 */
|
||||
memmove(parser->recv_buffer, parser->recv_buffer + 1, parser->recv_length - 1);
|
||||
parser->recv_length--;
|
||||
continue;
|
||||
}
|
||||
|
||||
FrameHeader* header = (FrameHeader*)parser->recv_buffer;
|
||||
size_t frame_size = sizeof(FrameHeader) + header->payload_length + 2; /* +2 for CRC */
|
||||
|
||||
if (parser->recv_length < frame_size) {
|
||||
break; /* 帧数据不完整,等待更多数据 */
|
||||
}
|
||||
|
||||
/* 根据帧类型分发解析 */
|
||||
switch (header->frame_type) {
|
||||
case FRAME_TYPE_STROKE_DATA:
|
||||
parse_stroke_frame(parser, parser->recv_buffer, frame_size);
|
||||
parsed_count++;
|
||||
break;
|
||||
case FRAME_TYPE_PEN_UP:
|
||||
if (parser->pen_event_cb) {
|
||||
parser->pen_event_cb(1, parser->last_timestamp, parser->user_data);
|
||||
}
|
||||
break;
|
||||
case FRAME_TYPE_PEN_DOWN:
|
||||
if (parser->pen_event_cb) {
|
||||
parser->pen_event_cb(0, parser->last_timestamp, parser->user_data);
|
||||
}
|
||||
break;
|
||||
case FRAME_TYPE_DEVICE_STATUS: {
|
||||
DeviceStatusFrame* status = (DeviceStatusFrame*)parser->recv_buffer;
|
||||
if (parser->status_cb) {
|
||||
parser->status_cb(status->battery_level, status->charging_state,
|
||||
status->firmware_version, parser->user_data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* 移除已处理的帧 */
|
||||
memmove(parser->recv_buffer, parser->recv_buffer + frame_size,
|
||||
parser->recv_length - frame_size);
|
||||
parser->recv_length -= frame_size;
|
||||
}
|
||||
|
||||
return parsed_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取解析器统计信息
|
||||
*/
|
||||
static inline void ble_parser_get_stats(const BleProtocolParser* parser,
|
||||
uint32_t* total_frames, uint32_t* total_points,
|
||||
uint32_t* error_frames, uint32_t* lost_frames) {
|
||||
if (total_frames) *total_frames = parser->total_frames;
|
||||
if (total_points) *total_points = parser->total_points;
|
||||
if (error_frames) *error_frames = parser->error_frames;
|
||||
if (lost_frames) *lost_frames = parser->lost_frames;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置解析器状态
|
||||
*/
|
||||
static inline void ble_parser_reset(BleProtocolParser* parser) {
|
||||
parser->recv_length = 0;
|
||||
parser->expected_sequence = 0;
|
||||
parser->last_timestamp = 0;
|
||||
parser->total_frames = 0;
|
||||
parser->total_points = 0;
|
||||
parser->error_frames = 0;
|
||||
parser->lost_frames = 0;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* BLE_PROTOCOL_H */
|
||||
Reference in New Issue
Block a user