Files
2026-03-22 15:24:40 +08:00

377 lines
12 KiB
C

/**
* 自然写互动课堂应用开发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 */