Bulk 大数据上传

更新时间:2024-11-20 08:51:21下载pdf

Bulk 大数据通道,即将按照特定格式存储在设备本地的数据,快速传输到 App 端的数据通道协议。

概述

使用大数据通道可使设备快速上报 DP 数据。常应用于设备本地需要缓存大量离线数据的品类,且在蓝牙连接后快速上报本地离线数据。

例如蓝牙 智能手表/手环 等品类,在断开蓝牙连接后,设备依然会持续产生 分钟步数分钟里程分钟心率 等数据,但由于这些数据在离线情况下无法上传到手机,因此设备先将此类离线数据暂时缓存在本地 Flash 中。当用户打开 App 蓝牙连接设备后,App 将通过 大数据通道 指令通知设备上传本地缓存数据。

数据读取流程

Bulk 大数据上传

协议命令

读取 Bulk 信息

  • App -> 设备

    字段 字节数 说明
    version 1 Bulk 大数据版本,当前固定为0x00。
    bulk_type 1 Bulk 大数据类型,由具体产品定义,从 1 开始,0 表示通用类型数据。
  • 设备 -> App

    字段 字节数 说明
    version 1 Bulk 大数据版本,当前固定为0x00。
    bulk_type 1 Bulk 大数据类型,由具体产品定义,从 1 开始,0 表示通用类型数据。
    status 1 状态
    • 0:成功,后跟数据有效
    • 1:bulk_type无效,后跟数据无效或无后跟数据
    • 其他:保留
    flag 1 是否要App解析大数据
  • 0:需要
  • 1:不需要App解析,需要面板或者云端解析
  • 其他:保留
  • total_length 4 数据总长度,大端格式。
    total_crc32 4 数据总 CRC32,大端格式。
    block_length 2 块长度。大数据读取以块为单位,App 每次发送要读取的块编号,块数据会以回复的单包长度为单位分包发送。

读取 Bulk 块数据

App每发送一次读取 Bulk 块数据指令,设备会以响应帧中的单包长度,通过上报 Bulk 数据指令,分包上报块数据。

  • App -> 设备

    字段 字节数 说明
    version 1 Bulk 大数据版本,当前固定为0x00。
    bulk_type 1 Bulk 大数据类型,由具体产品定义,从 1 开始,0 表示通用类型数据。
    block_number 2 块编号:当前要读取的块编号(从0开始)。
  • 设备 -> App

    字段 字节数 说明
    version 1 Bulk 大数据版本,当前固定为0x00。
    bulk_type 1 Bulk 大数据类型,由具体产品定义,从 1 开始,0 表示通用类型数据。
    status 1 状态
    • 0:成功,后跟数据有效
    • 1:bulk_type无效,后跟数据无效或无后跟数据
    • 2:块号无效
    • 其他:保留
    block_number 2 块编号:当前要读取的块编号。
    block_length 2 块长度:当前要读取的块数据长度。
    packet_length 2 单包长度:本次读取的块数据上报的分包最大长度。
    block_crc16 2 块CRC16:本地读取的块数据的 CRC16

上报 Bulk 数据

App 每发送一次读取 Bulk 块数据指令,设备会通过该指令,自动分包发送本块数据。

举例说明:

  1. 假设设备当前共有 9KB 数据,准备每块最大传输 2KB,所以共需要分为 5 块来传输,最后一块 1KB。
  2. App 首先发送 读取bulk信息 指令,设备回复数据总长度为 9KB,总数据的 CRC32,以及每块最大 2KB。
  3. App 发送 读取 Bulk 块数据 指令读取第一块数据,指定块编号为 0,设备回复了本块数据长度、本块数据 CRC16,最大分包长度。App 收到设备的回复后开始计时,准备接收本块数据。
  4. 设备通过 上报 Bulk 数据 指令开始分包发送本块数据,App 无需回复该指令,只负责数据接收。
  5. App 通过 上报 Bulk 数据 指令接收完一整块数据后(根据接收的长度判断是否结束),开始校验本块数据,若校验失败,则重新读取,若校验成功,则继续读取下一块数据。
  6. 全部数据读取完后,App 会做一次总长度和总CRC32校验。
  7. App 全部校验通过后,按需发送 擦除 Bulk 数据 指令,擦除设备中已被读取的大数据。
  • 设备 -> App

    字段 字节数 说明
    version 1 Bulk 大数据版本,当前固定为0x00。
    bulk_type 1 Bulk 大数据类型,由具体产品定义,从 1 开始,0 表示通用类型数据。
    block_number 2 块编号:当前要传输的块编号。
    packet_number 2 当前包编号:当前块数据中的数据分包发送编号,从0开始。
    packet_length 2 当前包长度:当前包数据长度。
    packet_data packet_length 当前包数据。

擦除 Bulk 数据

  • App -> 设备

    字段 字节数 说明
    version 1 Bulk 大数据版本,当前固定为0x00。
    bulk_type 1 Bulk 大数据类型,由具体产品定义,从 1 开始,0 表示通用类型数据。
  • 设备 -> App

    字段 字节数 说明
    version 1 Bulk 大数据版本,当前固定为0x00。
    bulk_type 1 Bulk 大数据类型,由具体产品定义,从 1 开始,0 表示通用类型数据。
    status 1 状态
    • 0:成功
    • 1:失败
    • 其他:保留

TLD 格式

TLD 格式主要用于设备本地存储和 App(不包括云端)数据解析。Bulk 大数据需要按照 TLD 格式传输,才能被 App 正确解析。目前有两种格式可供选择,请根据设备实际情况来选择。

字段 字节数 说明
Type 1 格式类型
  • 0x01:同一时刻产生的dp数据( App 解析)
  • 0x02:同一时刻产生的dp数据(包含用于嵌入式内存对齐的无效部分,App 解析)
  • 其他:保留
Len 2 数据长度
Data Len 数据长度 受到每包数据最大长度限制/不受限制(传输时不考虑存储结构)

Type=0x01

同一时刻产生的 DP 数据( App 解析)

Data 格式:

字段 字节数 说明
UNIX_time 4 UNIX时间戳
DP_1 DP1数据,详见 涂鸦DP数据
DP_n DPn数据,详见 涂鸦DP数据

Type=0x02

同一时刻产生的 DP 数据(包含用于嵌入式内存对齐的无效部分,App 解析)

Data 格式:

字段 字节数 说明
storage_header 6 与嵌入式存储结构相关,App无需解析
valid_data_len 2 指UNIX时间戳+DP数据的总长度,大端传输
UNIX_time 4 UNIX时间戳
DP_1 DP1数据,详见 涂鸦DP数据
DP_n DPn数据,详见 涂鸦DP数据
invalid_data Len-6-2-valid_data_len 无意义的填充数据,主要用于嵌入式内存对齐

DP 数据格式

Dp点1的数据 ~ Dp点n的数据
1 2 3-4 5~ ~ n n+1 n+2-n+3 n+4~
Dp_id Dp_type Dp_len Dp_data ~ Dp_id Dp_type Dp_len Dp_data

API

tuya_ble_bulk_data_read_block_size_get

函数名 tuya_ble_bulk_data_read_block_size_get
函数原型 uint32_t tuya_ble_bulk_data_read_block_size_get(void)
功能概述 读取 Bulk 大数据块长度
参数
返回值 Bulk 大数据块长度
备注 块的大小,是通过调用 tuya_ble_bulk_data_read_block_size_get 这个函数来确定的。这个函数会根据蓝牙MTU以及堆栈的大小,来确定一个块数据大小。

示例:

static uint32_t bulk_data_read_block_size = 0;

void tuya_ble_app_bulk_data_handler(tuya_ble_bulk_data_request_t *p_data)
{
    switch (p_data->evt)
    {
        case TUYA_BLE_BULK_DATA_EVT_READ_INFO: {
            bulk_data_read_block_size = tuya_ble_bulk_data_read_block_size_get();
        } break;
		//...
    }

}

tuya_ble_bulk_data_response

函数名 tuya_ble_bulk_data_response
函数原型 tuya_ble_status_t tuya_ble_bulk_data_response(tuya_ble_bulk_data_response_t *p_data)
功能概述 Bulk 大数据上传响应
参数 p_data[in]:bulk_data 响应数据
返回值
  • TUYA_BLE_SUCCESS:成功
  • TUYA_BLE_ERR_INVALID_STATE:无效状态,不能启动,例如当前蓝牙未连接
  • TUYA_BLE_ERR_INVALID_LENGTH:数据长度错误
  • TUYA_BLE_ERR_NO_MEM:内存申请失败
  • TUYA_BLE_ERR_NO_EVENT:队列空间满
备注 在设备接收到 App 的 Bulk 大数据命令后,需要调用该 API 进行响应。返回的参数需要按照上述说明的 协议命令 正确返回响应数据。

示例:

void tuya_ble_app_bulk_data_handler(tuya_ble_bulk_data_request_t *p_data)
{
    uint8_t rsp_flag = 1;
    tuya_ble_bulk_data_response_t rsp_data;

    switch (p_data->evt)
    {
        case TUYA_BLE_BULK_DATA_EVT_READ_INFO: {
            //...
        } break;

        case TUYA_BLE_BULK_DATA_EVT_READ_BLOCK: {
            //...
        } break;

        case TUYA_BLE_BULK_DATA_EVT_SEND_DATA: {
            //...
        } break;

        case TUYA_BLE_BULK_DATA_EVT_ERASE: {
            //...
        } break;

        default: {
            rsp_flag = 0;
        } break;
    }

    if (rsp_flag)
    {
        tuya_ble_bulk_data_response(&rsp_data);
    }
}

添加 Bulk 事件

蓝牙 SDK 通过消息(Message)或者设备应用程序注册的回调(Callback)函数向设备应用程序发送数据(状态、数据等)。

当 App 向设备发送 Bulk 大数据相关命令后,蓝牙 SDK 都会向应用程序发送如下的 Bulk 大数据回调事件。需要您添加对应回调处理函数,并在回调处理函数中对大数据协议指令针对处理。更多详情,请参考 注册消息队列

TUYA_BLE_CB_EVT_BULK_DATA

Event TUYA_BLE_CB_EVT_BULK_DATA
对应数据结构 tuya_ble_bulk_data_request_t
描述 Bulk 大数据上传通道数据请求回调事件
备注 用户可在该回调事件下处理 Bulk 大数据命令

数据结构:

typedef enum {
    TUYA_BLE_BULK_DATA_EVT_READ_INFO,	        /**< 读取bulk信息事件*/
    TUYA_BLE_BULK_DATA_EVT_READ_BLOCK,	    	/**< 读取 Bulk 块数据事件*/
    TUYA_BLE_BULK_DATA_EVT_SEND_DATA,	    	/**< 上报 Bulk 数据事件*/
    TUYA_BLE_BULK_DATA_EVT_ERASE,               /**< 擦除 Bulk 数据事件*/
    TUYA_BLE_BULK_DATA_EVT_UNKONWN,            /**< 未知事件*/
} tuya_ble_bulk_data_evt_type_t;

typedef struct {
    uint16_t block_number;
} tuya_ble_bulk_data_evt_read_block_req_t;

typedef struct {
    uint16_t block_number;
} tuya_ble_bulk_data_evt_send_data_req_t;

typedef struct {
    tuya_ble_bulk_data_evt_type_t evt;  //具体大数据命令
    uint8_t bulk_type;
    union
    {
        tuya_ble_bulk_data_evt_read_block_req_t block_data_req_data;
        tuya_ble_bulk_data_evt_send_data_req_t send_data_req_data;
    } params;
} tuya_ble_bulk_data_request_t;

示例:

static void tuya_ble_sdk_callback(tuya_ble_cb_evt_param_t *event)
{
    switch (event->evt)
    {
        //...

        case TUYA_BLE_CB_EVT_BULK_DATA: {  //在事件下补充大数据处理函数
            tuya_ble_app_bulk_data_handler(&event->bulk_req_data);
        } break;

        //...
    }
}

应用示例

数据结构:

typedef struct {
    uint8_t status;	/**< 0-成功,后跟数据有效;1-无效,后跟数据无效或无后跟数据;其他-保留*/
    uint8_t flag;	/**< 0-需要 App 解析大数据;1-不需要 App 解析大数据,需要面板或云端去解析*/
    uint32_t bulk_data_length;			/**< 数据总长度*/
    uint32_t bulk_data_crc;				/**< 数据总CRC32*/
    uint16_t block_data_length;			/**< 块长度*/
} tuya_ble_bulk_data_evt_read_info_res_t;

typedef struct {
    uint8_t status;						/**< 0-成功;1-Type无效;2-块号无效;其他-保留*/
    uint16_t block_number;				/**< 块编号*/
    uint16_t block_data_length;			/**< 块长度*/
    uint16_t block_data_crc16;			/**< 块数据CRC16*/
    uint16_t max_packet_data_length;	/**< 本次读取的块数据上报的分包最大长度*/
} tuya_ble_bulk_data_evt_read_block_res_t;

typedef struct {
    uint16_t current_block_number;		/**< 当前传输的数据块编号*/
    uint16_t current_block_length;		/**< 当前传输的数据块总长度*/
    uint8_t  *p_current_block_data;		/**< 当前传输的数据块数据指针*/
} tuya_ble_bulk_data_evt_send_data_res_t;

typedef struct {
    uint8_t status;						/**< 0-成功;1-失败;其他-保留*/
} tuya_ble_bulk_data_evt_erase_res_t;

typedef struct {
    tuya_ble_bulk_data_evt_type_t evt;
    uint8_t bulk_type;
    union
    {
        tuya_ble_bulk_data_evt_read_info_res_t bulk_info_res_data;
        tuya_ble_bulk_data_evt_read_block_res_t block_res_data;
        tuya_ble_bulk_data_evt_send_data_res_t send_res_data;
        tuya_ble_bulk_data_evt_erase_res_t erase_res_data;
    } params;
} tuya_ble_bulk_data_response_t;

示例:

void tuya_ble_app_bulk_data_handler(tuya_ble_bulk_data_request_t *p_data)
{
    uint8_t rsp_flag = 1;
    tuya_ble_bulk_data_response_t rsp_data;

    switch (p_data->evt)
    {
        case TUYA_BLE_BULK_DATA_EVT_READ_INFO: {
            //解析判断 p_data 等数据
            //根据条件赋值 rsp_data.bulk_info_res_data 等数据
        } break;

        case TUYA_BLE_BULK_DATA_EVT_READ_BLOCK: {
            //解析判断 p_data->params.block_data_req_data 等数据
            //根据条件赋值 rsp_data.block_res_data 等数据
        } break;

        case TUYA_BLE_BULK_DATA_EVT_SEND_DATA: {
            //解析判断 p_data->params.send_data_req_data 等数据
            //根据条件赋值 rsp_data.send_res_data 等数据
        } break;

        case TUYA_BLE_BULK_DATA_EVT_ERASE: {
            //解析判断 p_data 等数据
            //根据条件赋值 rsp_data.erase_res_data 等数据
        } break;

        default: {
            rsp_flag = 0;
        } break;
    }

    if (rsp_flag)
    {
        tuya_ble_bulk_data_response(&rsp_data);       // 返回应答数据
        if (rsp_data.evt == TUYA_BLE_BULK_DATA_EVT_SEND_DATA) {
            tuya_ble_free(rsp_data.params.send_res_data.p_current_block_data);
        }
    }
}