Files
BLE-USB_Dongle/apps/usr_le_code/usr_usb_jb.c
T
wuchuyuan 79e15a9ccc 1、对逗猫棒和喂食器的设备增加应答功能
2、对于V3协议的设备,目前只能电脑端应答,对电脑端的应答进行封包并通过BLE发送
2026-07-30 14:43:30 +08:00

521 lines
15 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/******************************************************************************
* @file usr_usb_jb.c
* @brief USB CDC 见宝帧解析:校验后写入产品码并重广播
* @author cyWu <1917507415@qq.com>
* @date 2026.07.24
* @version V1.0.2
* @history
* - V1.0.2, 2026.07.28, cyWu, 非产品码命令整帧透传 BLE REPORTCMD_V2
* - V1.0.1, 2026.07.24, cyWu, CDC 中断内禁止 syscfg_write,改延时到任务上下文
* - V1.0.0, 2026.07.24, cyWu, 首次发布
******************************************************************************/
#include "system/includes.h"
#include "app_config.h"
#include "vm.h"
#include "usr_usb_jb.h"
#include "bleproto_api.h"
#define LOG_TAG_CONST APP
#define LOG_TAG "[USB_JB]"
#define LOG_ERROR_ENABLE
#define LOG_DEBUG_ENABLE
#define LOG_INFO_ENABLE
#define LOG_CLI_ENABLE
#include "debug.h"
#if TCFG_USB_SLAVE_CDC_ENABLE
#include "usb/device/cdc.h"
#endif
/*============================================================================*/
/* 协议常量(对齐 jb_protocol / jb_common */
/*============================================================================*/
#define PKT_HEAD_0 0x55
#define PKT_HEAD_1 0xAA
#define MAX_PACKAGE_LEN 128
#define JB_RB_CAPACITY 256
#define USR_PROD_MAGIC 0x5A
#define USR_JB_BLE_CMD_REPORT 100 /* 0x07/0x34/0x38 → 主动上报类 */
#define USR_JB_BLE_CMD_ACK 110 /* 0x04/0x06/0x31/0x37 → 应答类 */
#define USR_JB_BLE_TX_SZ 384
#ifndef CFG_USER_PROD_INFO
#define CFG_USER_PROD_INFO 28
#endif
extern void usr_ble_adv_init(void);
extern void usr_ble_send_data(u8 *data, u16 len);
extern u16 usr_get_conn_service(void);
/*============================================================================*/
/* 类型 */
/*============================================================================*/
typedef struct {
uint8_t magic;
uint8_t prodcode[USR_PRODCODE_LEN];
uint8_t pcode[USR_PCODE_LEN];
} usr_prod_info_t;
typedef struct {
uint8_t buf[JB_RB_CAPACITY];
uint16_t write;
uint16_t read;
uint16_t count;
} usr_jb_rb_t;
/*============================================================================*/
/* 静态变量 */
/*============================================================================*/
static const uint8_t s_default_prodcode[USR_PRODCODE_LEN] = {'C', 'B', 'C', '0'};
static const uint8_t s_default_pcode[USR_PCODE_LEN] = {'P', 'I', 'C', 'B', 'C', '0'};
static uint8_t s_prodcode[USR_PRODCODE_LEN];
static uint8_t s_pcode[USR_PCODE_LEN];
static usr_jb_rb_t s_rb;
/* 待写 flash / 回 ACK 的缓存(ISR 只填,定时器里执行) */
static uint8_t s_pending_save;
static uint8_t s_pending_cmd;
static uint8_t s_pending_sn;
static uint8_t s_pending_prod[USR_PRODCODE_LEN];
static uint8_t s_pending_pcode[USR_PCODE_LEN];
static int s_process_timer;
static uint8_t s_ble_txbuf[USR_JB_BLE_TX_SZ];
static uint8_t s_last_ble_msgid; /* 最近一次 BLE RX 的 msgid */
void usr_ble_rx_msgid_save(uint8_t msgid)
{
s_last_ble_msgid = msgid & 0x0F;
printf("usr_ble rx msgid save=%u\n", s_last_ble_msgid);
}
uint8_t usr_ble_rx_msgid_get(void)
{
return s_last_ble_msgid;
}
/**
* @brief 按 USB JB 命令映射 ble_proto cmd / gateway_int
* @param jb_cmd USB 帧 CMDframe[4]
* @param pkt 待填充的 ble_proto 包
* @return 0 可透传, -1 不支持的命令
*/
static int usr_jb_map_ble_pkt(uint8_t jb_cmd, ble_proto_packet_t *pkt)
{
if (!pkt) {
return -1;
}
memset(pkt, 0, sizeof(*pkt));
switch (jb_cmd) {
case 0x07: /* REPORT */
case 0x34: /* OTA_COMPLETE */
case 0x38: /* OTA_STOP_MCU */
pkt->cmd = USR_JB_BLE_CMD_REPORT;
pkt->gateway_int_cnt = 0;
break;
case 0x04: /* CONTROL_ACK */
case 0x06: /* READ_ACK */
case 0x31: /* OTA_NOTIFY_ACK */
case 0x37: /* OTA_STOP_BLE_ACK */
pkt->cmd = USR_JB_BLE_CMD_ACK;
pkt->gateway_int_cnt = 2;
pkt->gateway_int[0] = 0;
pkt->gateway_int[1] = 0;
break;
default:
return -1;
}
return 0;
}
/**
* @brief 将完整 JB 帧封装为 REPORTCMD_V2 后经 BLE 发出
* @param frame 完整 55 AA 帧
* @param len 帧长度
*/
static void usr_jb_forward_to_ble(const uint8_t *frame, uint16_t len)
{
bleproto_appdata_desc_t encode_desc = {0};
ble_proto_packet_t *pkt;
uint8_t jb_cmd;
int elen;
if (!frame || len < 7 || len > MAX_PACKAGE_LEN) {
return;
}
if (usr_get_conn_service() == 0) {
printf("usr_jb ble forward skip: not connected\n");
return;
}
jb_cmd = frame[4];
pkt = &encode_desc.datast.reportcmd_v2_req;
if (usr_jb_map_ble_pkt(jb_cmd, pkt) != 0) {
printf("usr_jb ble forward ignore jb_cmd=0x%02x\n", jb_cmd);
return;
}
encode_desc.header.version = 0;
encode_desc.header.datafmt = BLEPROTO_APPDATA_DATAFMT_JSON;
/* 100 上报用 REQ110 应答用 RSP */
encode_desc.header.msgtype = (pkt->cmd == USR_JB_BLE_CMD_ACK)
? BLEPROTO_APPDATA_MSGTYPE_RSP
: BLEPROTO_APPDATA_MSGTYPE_REQ;
encode_desc.header.msgid = usr_ble_rx_msgid_get();
encode_desc.header.encrypt = 0;
encode_desc.header.serviceid = (pkt->cmd == USR_JB_BLE_CMD_ACK)
? E_BLEPROTO_SERVICE_ID_RUNCMD_V2
: E_BLEPROTO_SERVICE_ID_REPORTCMD_V2;
pkt->reserved = 0;
pkt->device_int_len = 0;
pkt->device_str_len = 0;
pkt->device_bytes_len = len;
pkt->bytes_data = frame;
elen = bleproto_appdata_encode(s_ble_txbuf, sizeof(s_ble_txbuf),
sizeof(s_ble_txbuf), &encode_desc);
printf("usr_jb ble forward jb_cmd=0x%02x ble_cmd=%u msgtype=%u msgid=%u gw_cnt=%u elen=%d\n",
jb_cmd, pkt->cmd, encode_desc.header.msgtype,
encode_desc.header.msgid, pkt->gateway_int_cnt, elen);
if (elen > 0) {
usr_ble_send_data(s_ble_txbuf, (u16)elen);
printf_buf(s_ble_txbuf, elen);
} else {
printf("usr_jb ble encode fail\n");
}
}
/*============================================================================*/
/* 环形缓冲 / 校验 */
/*============================================================================*/
static void usr_jb_rb_init(usr_jb_rb_t *rb)
{
memset(rb, 0, sizeof(*rb));
}
static void usr_jb_rb_push(usr_jb_rb_t *rb, uint8_t byte)
{
if (rb->count >= JB_RB_CAPACITY) {
rb->read = (rb->read + 1) % JB_RB_CAPACITY;
rb->count--;
}
rb->buf[rb->write] = byte;
rb->write = (rb->write + 1) % JB_RB_CAPACITY;
rb->count++;
}
static uint8_t usr_jb_rb_pop(usr_jb_rb_t *rb)
{
uint8_t b = rb->buf[rb->read];
rb->read = (rb->read + 1) % JB_RB_CAPACITY;
rb->count--;
return b;
}
static uint16_t usr_jb_rb_available(const usr_jb_rb_t *rb)
{
return rb->count;
}
static uint16_t usr_jb_read_u16_be(const uint8_t *buf)
{
return ((uint16_t)buf[0] << 8) | buf[1];
}
/**
* @brief 校验和:sum(LEN_H .. payload_last) & 0xFF
*/
static uint8_t usr_jb_checksum(const uint8_t *frame, uint16_t total_len)
{
uint16_t sum = 0;
uint16_t i;
if (!frame || total_len < 4) {
return 0;
}
for (i = 2; i < total_len - 1; i++) {
sum += frame[i];
}
return (uint8_t)(sum & 0xFF);
}
/**
* @brief 从环形缓冲提取完整帧(逻辑对齐 jbGetOnePacket
* @return 0 成功, 1 需要更多数据, -2 校验失败
*/
static int8_t usr_jb_get_one_packet(uint8_t *out, uint16_t *out_len)
{
static uint8_t state = 0;
static uint16_t expect = 0;
static uint16_t idx = 0;
static uint8_t prev = 0;
while (usr_jb_rb_available(&s_rb)) {
uint8_t b = usr_jb_rb_pop(&s_rb);
if (state == 0) {
if (prev == PKT_HEAD_0 && b == PKT_HEAD_1) {
out[0] = PKT_HEAD_0;
out[1] = PKT_HEAD_1;
idx = 2;
state = 1;
}
} else if (state == 1) {
out[idx++] = b;
if (idx == 6) {
expect = 4 + usr_jb_read_u16_be(&out[2]);
if (expect > MAX_PACKAGE_LEN || expect < 7) {
state = 0;
} else {
state = 2;
}
}
} else {
out[idx++] = b;
if (idx >= expect) {
state = 0;
if (out[expect - 1] == usr_jb_checksum(out, expect)) {
*out_len = expect;
return 0;
}
printf("usr_jb checksum fail expect=0x%02x calc=0x%02x\n",
out[expect - 1], usr_jb_checksum(out, expect));
return -2;
}
}
prev = b;
}
return 1;
}
/*============================================================================*/
/* 产品码 flash */
/*============================================================================*/
static void usr_prodcode_set_default(void)
{
memcpy(s_prodcode, s_default_prodcode, USR_PRODCODE_LEN);
memcpy(s_pcode, s_default_pcode, USR_PCODE_LEN);
}
void usr_prodcode_init(void)
{
usr_prod_info_t info;
int ret;
usr_jb_rb_init(&s_rb);
s_pending_save = 0;
s_process_timer = 0;
usr_prodcode_set_default();
memset(&info, 0, sizeof(info));
ret = syscfg_read(CFG_USER_PROD_INFO, (u8 *)&info, sizeof(info));
if (ret > 0 && info.magic == USR_PROD_MAGIC) {
memcpy(s_prodcode, info.prodcode, USR_PRODCODE_LEN);
memcpy(s_pcode, info.pcode, USR_PCODE_LEN);
printf("prodcode from flash: %c%c%c%c / %c%c%c%c%c%c\n",
s_prodcode[0], s_prodcode[1], s_prodcode[2], s_prodcode[3],
s_pcode[0], s_pcode[1], s_pcode[2], s_pcode[3], s_pcode[4], s_pcode[5]);
} else {
printf("prodcode use default: %c%c%c%c / %c%c%c%c%c%c\n",
s_prodcode[0], s_prodcode[1], s_prodcode[2], s_prodcode[3],
s_pcode[0], s_pcode[1], s_pcode[2], s_pcode[3], s_pcode[4], s_pcode[5]);
}
}
void usr_prodcode_get(uint8_t *prodcode, uint8_t *pcode)
{
if (prodcode) {
memcpy(prodcode, s_prodcode, USR_PRODCODE_LEN);
}
if (pcode) {
memcpy(pcode, s_pcode, USR_PCODE_LEN);
}
}
/**
* @brief 写 VM(必须在任务/定时器上下文调用,禁止在 CDC 中断里调用)
*/
static int usr_prodcode_save(const uint8_t *prodcode, const uint8_t *pcode)
{
usr_prod_info_t info;
int ret;
if (!prodcode || !pcode) {
return -1;
}
memset(&info, 0, sizeof(info));
info.magic = USR_PROD_MAGIC;
memcpy(info.prodcode, prodcode, USR_PRODCODE_LEN);
memcpy(info.pcode, pcode, USR_PCODE_LEN);
memcpy(s_prodcode, prodcode, USR_PRODCODE_LEN);
memcpy(s_pcode, pcode, USR_PCODE_LEN);
ret = syscfg_write(CFG_USER_PROD_INFO, (u8 *)&info, sizeof(info));
if (ret <= 0) {
printf("prodcode flash write fail ret=%d\n", ret);
return -1;
}
printf("prodcode saved: %c%c%c%c / %c%c%c%c%c%c ret=%d\n",
s_prodcode[0], s_prodcode[1], s_prodcode[2], s_prodcode[3],
s_pcode[0], s_pcode[1], s_pcode[2], s_pcode[3], s_pcode[4], s_pcode[5],
ret);
return 0;
}
/**
* @brief 解析产品码 payload(仅 TLV
* 格式: [len=4][ProdtCode 4B][len=6][productCode 6B]
* @return 0 成功, -1 失败
*/
static int usr_jb_parse_prod_payload(const uint8_t *payload, uint16_t payload_len,
uint8_t *prodcode, uint8_t *pcode)
{
if (!payload || !prodcode || !pcode) {
return -1;
}
/* TLV: 04 xx xx xx xx 06 yy yy yy yy yy yy */
if (payload_len < (1 + USR_PRODCODE_LEN + 1 + USR_PCODE_LEN)) {
return -1;
}
if (payload[0] != USR_PRODCODE_LEN) {
return -1;
}
if (payload[1 + USR_PRODCODE_LEN] != USR_PCODE_LEN) {
return -1;
}
memcpy(prodcode, &payload[1], USR_PRODCODE_LEN);
memcpy(pcode, &payload[1 + USR_PRODCODE_LEN + 1], USR_PCODE_LEN);
return 0;
}
#if TCFG_USB_SLAVE_CDC_ENABLE
/**
* @brief 回 ACKCMD = 请求 CMD+1payload = result
*/
static void usr_jb_send_ack(uint8_t cmd, uint8_t sn, uint8_t result)
{
uint8_t buf[8];
uint16_t len_field = 4; /* CMD+SN+result+CKSUM */
uint16_t total = len_field + 4;
buf[0] = PKT_HEAD_0;
buf[1] = PKT_HEAD_1;
buf[2] = (uint8_t)(len_field >> 8);
buf[3] = (uint8_t)(len_field & 0xFF);
buf[4] = (uint8_t)(cmd + 1);
buf[5] = sn;
buf[6] = result;
buf[7] = usr_jb_checksum(buf, total);
cdc_write_data(0, buf, total);
}
#endif
/**
* @brief 任务上下文:解析帧 / 写 flash / ACK / 重建广播
*/
static void usr_jb_process_timeout(void *priv)
{
uint8_t pkt[MAX_PACKAGE_LEN];
uint16_t pkt_len;
int8_t ret;
uint8_t cmd;
uint8_t sn;
uint16_t payload_len;
(void)priv;
s_process_timer = 0;
/* 先把环形缓冲里的完整帧抽完 */
do {
pkt_len = 0;
ret = usr_jb_get_one_packet(pkt, &pkt_len);
if (ret != 0) {
break;
}
cmd = pkt[4];
sn = pkt[5];
payload_len = (pkt_len >= 7) ? (pkt_len - 7) : 0;
printf("usr_jb cmd=0x%02x sn=%u plen=%u\n", cmd, sn, payload_len);
printf_buf(pkt, pkt_len);
if (cmd == USR_JB_CMD_SET_PRODCODE) {
if (usr_jb_parse_prod_payload(&pkt[6], payload_len,
s_pending_prod, s_pending_pcode) != 0) {
printf("usr_jb set prodcode payload invalid\n");
#if TCFG_USB_SLAVE_CDC_ENABLE
usr_jb_send_ack(cmd, sn, 0x01);
#endif
continue;
}
/* 缓存后统一落盘,避免同一周期多次写 flash */
s_pending_cmd = cmd;
s_pending_sn = sn;
s_pending_save = 1;
continue;
}
/* 其它命令:整帧透传到 BLE(按 JB cmd 映射 ble_proto cmd */
usr_jb_forward_to_ble(pkt, pkt_len);
} while (1);
if (!s_pending_save) {
return;
}
s_pending_save = 0;
if (usr_prodcode_save(s_pending_prod, s_pending_pcode) == 0) {
#if TCFG_USB_SLAVE_CDC_ENABLE
usr_jb_send_ack(s_pending_cmd, s_pending_sn, 0x00);
#endif
extern void usr_goto_pair_mode(void);
usr_goto_pair_mode();
printf("prodcode applied, adv rebuilt\n");
} else {
#if TCFG_USB_SLAVE_CDC_ENABLE
usr_jb_send_ack(s_pending_cmd, s_pending_sn, 0x05);
#endif
}
}
/**
* @brief USB CDC 喂数:仅压入环形缓冲并挂定时器(可在中断调用)
*/
void usr_usb_jb_feed(uint8_t *buf, uint32_t len)
{
uint32_t i;
if (!buf || len == 0) {
return;
}
for (i = 0; i < len; i++) {
usr_jb_rb_push(&s_rb, buf[i]);
}
/* 延时到系统定时器任务再解析/写 flash,避免中断里擦写导致复位 */
if (s_process_timer == 0) {
s_process_timer = sys_timeout_add(NULL, usr_jb_process_timeout, 20);
}
}