/** * 自然写互动课堂应用开发SDK软件 V1.0 * BLE协议解析核心模块 - 蓝牙5.0点阵笔通信协议实现 * * 跨平台C语言核心库,负责解析点阵笔BLE GATT数据 * 提供笔迹坐标解包、协议帧校验、数据压缩解压等底层能力 * 通过JNI/ObjC Bridge/FFI供各平台SDK调用 */ #ifndef BLE_PROTOCOL_H #define BLE_PROTOCOL_H #include #include #include #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 */