更新时间:2024-11-20 02:13:24下载pdf
除了必要的基础功能外,蓝牙通用串口协议还提供了很多拓展功能,包括模组低功耗、MCU 固件 OTA 升级、产测、时间查询等多个实用功能。
本文展示的 功能协议示例 均由模组调试助手展示。推荐您使用 涂鸦模组调试助手 协助调试,可快速了解协议实现流程。
通过 MCU 给蓝牙模组发送低功耗配置,可以让模组进入低功耗状态。MCU 根据蓝牙通用串口协议配置模组参数,如修改广播间隔、关闭模组计时器等可以让模组的功耗进一步降低,满足电池类对功耗要求较高的产品使用需求。
在硬件设计时若不使用低功耗功能,只需接 TX、RX 两根引脚,即可完成基本的协议交互。若要使用低功耗模式,您还需要接上模组的低功耗控制脚。
模组唤醒引脚:模组的低功耗状态唤醒引脚,需要 MCU 接上配合使用。使能模组低功耗,且将模组低功耗引脚拉低后,模组串口不再接收数据,但是蓝牙通信和串口下发仍然正常工作。引脚拉高后,模组串口接收正常使用。
模组型号 | 模组低功耗控制引脚 | 低功耗对应的电平 | 非低功耗对应的电平 |
---|---|---|---|
BT3L-A1、BT3L、BT3L-A、BT3L-G、BT7L-G、BT7L、BT7L-IPEX、BT2S | 模组丝印 TL_B5 引脚 | 低 | 高 |
BT5S | 模组丝印 TL_C3 引脚 | 低 | 高 |
当模组从低功耗模式下唤醒时,需要延时 10 ms,模组串口才能接收到 MCU 数据。当模组处于深度睡眠模式,唤醒模组后,将重启模组,此时需要延时 600 ms,串口才能接受数据。
MCU 唤醒引脚:在低功耗模式下,当模组需要发送数据给 MCU 时,模组会改变 MCU 唤醒脚的电平 10 ms,然后再发送数据,发送完成后恢复默认电平。
模组说明 | 模组唤醒 MCU 引脚 | 发数据时电平 | 空闲时电平 |
---|---|---|---|
BT3L-A1、BT3L、BT3L-A、BT3L-G、BT7L-G、BT7L、BT7L-IPEX、BT2S | 模组丝印 TL_D2 引脚 | 高 | 低 |
BT5S | 模组丝印 TL_D2 引脚 | 高 | 低 |
功能说明:唤醒脚各平台默认上下拉情况可能存在差异,MCU 端与模组唤醒脚对接的引脚不要使用悬空状态。部分模组不支持该功能,详情请参考 蓝牙通用串口协议低功耗说明章节。
对于功耗要求较高的蓝牙设备,比如电池供电,需要降低设备使用过程的功耗,以延长设备的使用时间。这种情况下就可以使用蓝牙低功耗方案。
通过 MCU 发送使能低功耗指令到模组,让模组进入低功耗状态,可大幅降低模组功耗,若需要将模组唤醒,只需要拉高模组唤醒引脚即可恢复通信。
低功耗方案涉及多个协议指令:
0xE5
0xE2
0xE4
0xE7
0xE3
0xB0
0xB1
指令详情介绍,请参考 蓝牙通用串口协议-低功耗功能附加协议。
0xE5
指令作用:MCU 发送该指令到模组,使能模组低功耗。
应用场景:模组出厂时为非低功耗模组,需要通过该指令使能低功耗功能,然后才能通过拉低低功耗引脚,使模组进入低功耗状态,该设置将永久储存。
0xE2
指令作用:低功耗状态下,可通过该指令修改蓝牙广播间隔,进一步降低模组功耗。
应用场景:为降低休眠时的功耗,可以通过该指令修改广播间隔,广播参数将永久保存,当设置为 0 时,将关闭广播,该设置将永久存储。
0xE4
指令作用:MCU 发送该指令到模组,通知模组关闭内部计时器。
应用场景:为降低 Telink 芯片休眠时的功耗,可以通过该指令关闭蓝牙模组内部计时器功能,如果 MCU 不需要时间功能,可以关闭该功能,当计时功能和广播功能都关闭后,然后拉低模组唤醒脚,模组将进入深度睡眠,功耗降低至 3uA,唤醒后,模组重启运行,该设置将永久存储。
BK3431Q 和 TYBN1 中,由于单纯计时功耗比较低,主要耗电的是存储 Flash 操作,这里只是关闭了 Flash 存储操作。打开后,时钟可周期性(1min)存 Flash,可解决重启后模组 RTC 时钟时间重置的问题。(默认为关闭)
0xE7
指令作用:MCU 发送该指令到模组,通知模组主动断开蓝牙连接。
应用场景:为降低休眠时的功耗,MCU 在不需要蓝牙连接的时间,可以先断开模组蓝牙连接,再控制模组进入低功耗,以达到模组最低功耗状态。
低功耗与非低功耗状态下都可使用该指令主动断开蓝牙连接。
0xE3
指令作用:修改模组低功耗状态唤醒引脚
应用场景:主要用于芯片对接自定义模组低功耗唤醒引脚。需要使用非默认引脚外的引脚进行唤醒模组,建议在 MCU 初始化串口后立即通过该指令对模组低功耗唤醒引脚进行配置。该设置永久存储。
由于模组进入低功耗后串口不可上行,所以 MCU 需要在模组上电 1s 内(模组上电 1s 内不会进入低功耗)进行设置或者在开低功耗使能之前设置(模组默认是关低功耗使能的)。
目前该指令仅适用于 BK3432 固件。
0xB0
指令作用:MCU 可通过该指令设置 MCU 自身从低功耗唤醒所需的时间。
应用场景:该指令适用于 TYBN1 门锁通用固件 6.2 以上版本支持,BK3431Q 门锁通用固件 3.3 以上版本支持。该指令用于配置低功耗模式下模组向 MCU 发送数据时拉高 MCU 唤醒引脚的时间,默认 200ms,即模组要给 MCU 发送数据时,先把 MCU 唤醒脚拉高 200ms,再发送串口数据,发送数据完成后又将 MCU 唤醒脚拉低。
0x02
后,调用该接口设置 MCU 低功耗配置(该设置非永久存储,模组上电或者重启后恢复默认值 200ms)。0xB1
指令作用:MCU 向模组发送配置连接模式。
目前该指令仅适用于 BT2S、BT5S、BT3L、BT7L(Telink 825x 通用固件 8.10+ 版本支持)。
使能低功耗功能
修改低功耗模式下的广播间隔
关闭系统计时器
主动断开蓝牙连接
使能低功耗函数 bt_enable_lowpoer_req()
定义在 protocol.c
文件中,作用是 MCU 通过该指令使能模组低功耗。
应用步骤:
打开 TUYA_BCI_UART_COMMON_ENANBLE_LOWER_POWER
宏定义,开启 MCU 使能低功耗功能。
MCU 自行调用使能低功耗函数 bt_enable_lowpoer_req()
,发送使能低功耗功能指令 0xE5
到模组,使能低功耗。
在串口接收数据处理函数 data_handle()
中,收到模组发送的低功耗使能结果通知,SDK 调用使能低功耗结果处理函数 bt_enable_lowpoer_result()
接收处理,该函数逻辑代码需要用户自行完成。
#define TUYA_BCI_UART_COMMON_ENANBLE_LOWER_POWER 0xE5 //Low power enable
/*****************************************************************************
Function name: bt_enable_lowpoer_req
Function description: send a request to enable low power consumption to the module (currently only applicable to telink platform)
Input parameters: value 0 off, 1 on
Return parameter: none
*****************************************************************************/
void bt_enable_lowpoer_req(unsigned char value)
{
unsigned short length = 0;
length = set_bt_uart_byte(length,value);
bt_uart_write_frame(TUYA_BCI_UART_COMMON_ENANBLE_LOWER_POWER,length);
}
void data_handle(unsigned short offset)
{
......
#ifdef TUYA_BCI_UART_COMMON_ENANBLE_LOWER_POWER
case TUYA_BCI_UART_COMMON_ENANBLE_LOWER_POWER:
bt_enable_lowpoer_result(bt_uart_rx_buf[offset + DATA_START]);
break;
#endif
......
}
/*****************************************************************************
Function name: bt_enable_lowpoer_result
Function description: processing result
Input parameters: 0 successful, other failed
Return parameter: none
Instructions: MCU needs to improve the function on its own.
*****************************************************************************/
void bt_enable_lowpoer_result(unsigned char result)
{
#error "Please improve the function by yourself and delete the line after completion"
if(result == 0x00)
{
//success
}
else
{
//failed
}
}
修改广播间隔函数 bt_modify_adv_interval_req()
定义在 protocol.c
文件中,作用是 MCU 通过该指令修改模组低功耗模式下的广播间隔。
应用步骤:
打开 TUYA_BCI_UART_COMMON_MODIFY_ADV_INTERVAL
宏定义,开启 MCU 修改广播间隔功能。
MCU自行调用修改广播间隔函数 bt_modify_adv_interval_req()
,配置广播间隔后发送使能低功耗功能指令 0xE2
到模组,修改低功耗模式下的广播间隔。
在串口接收数据处理函数 data_handle()
中,收到模组发送的低功耗模式下的广播间隔修改结果通知,SDK 调用修改广播间隔结果处理函数 bt_modify_adv_interval_result()
接收处理,该函数逻辑代码需要用户自行完成。
#define TUYA_BCI_UART_COMMON_MODIFY_ADV_INTERVAL 0xE2//Modify the sleep mode broadcast interval
/*****************************************************************************
Function name: bt_modify_adv_interval_req
Function description: send a request to the module to modify the broadcast interval of the module at low power consumption
Input parameter: value * 100ms equals the broadcast interval, value (0-20 to be modified)
Return parameter: none
Instructions for use:
*****************************************************************************/
void bt_modify_adv_interval_req(unsigned char value)
{
unsigned short length = 0;
length = set_bt_uart_byte(length,value);
bt_uart_write_frame(TUYA_BCI_UART_COMMON_MODIFY_ADV_INTERVAL,length);
}
void data_handle(unsigned short offset)
{
......
#ifdef TUYA_BCI_UART_COMMON_MODIFY_ADV_INTERVAL
case TUYA_BCI_UART_COMMON_MODIFY_ADV_INTERVAL:
bt_modify_adv_interval_result(bt_uart_rx_buf[offset + DATA_START]);
break;
#endif
......
}
/*****************************************************************************
Function name: bt_modify_adv_interval_result
Function description:Processing the result of modifying the broadcast interval
Input parameters: result synchronization result 0 successful, other failed
Return parameter: none
Instructions: MCU needs to improve the function on its own.
*****************************************************************************/
void bt_modify_adv_interval_result(unsigned char result)
{
#error "Please improve the function by yourself and delete the line after completion"
if(result == 0x00)
{
//success
}
else
{
//failed
}
}
关闭系统计时器函数 bt_close_timer_req()
定义在 protocol.c
文件中,作用是 MCU 通过该指令使能模组低功耗。
应用步骤:
打开 TUYA_BCI_UART_COMMON_TURNOFF_SYSTEM_TIME
宏定义,开启 MCU 关闭模组计时器功能。
MCU 自行调用关闭系统计时器函数 bt_close_timer_req()
,发送使能低功耗功能指令 0xE4
到模组,关闭模组计时器。
在串口接收数据处理函数 data_handle()
中,收到模组发送的关闭计时器结果通知,SDK 调用关闭计时器结果处理函数 bt_close_timer_result()
接收处理,该函数逻辑代码需要用户自行完成。
#define TUYA_BCI_UART_COMMON_TURNOFF_SYSTEM_TIME 0xE4//Turn off the system clock function
/*****************************************************************************
Function name: bt_close_timer_req
Function description: send a request to the module to turn off the system clock (currently available on telink platform only)
Input parameters: value 0 off, 1 on
Return parameter: none
Instructions for use:
*****************************************************************************/
void bt_close_timer_req(unsigned char value)
{
unsigned short length = 0;
length = set_bt_uart_byte(length,value);
bt_uart_write_frame(TUYA_BCI_UART_COMMON_TURNOFF_SYSTEM_TIME,length);
}
void data_handle(unsigned short offset)
{
......
#ifdef TUYA_BCI_UART_COMMON_TURNOFF_SYSTEM_TIME
case TUYA_BCI_UART_COMMON_TURNOFF_SYSTEM_TIME:
bt_close_timer_result(bt_uart_rx_buf[offset + DATA_START]);
break;
#endif
......
}
/*****************************************************************************
Function name: bt_close_timer_result
Function description: processing result
Input parameters: 0 successful, other failed
Return parameter: none
Instructions: MCU needs to improve the function on its own.
*****************************************************************************/
void bt_close_timer_result(unsigned char result)
{
#error "Please improve the function by yourself and delete the line after completion"
if(result == 0x00)
{
//success
}
else
{
//failed
}
}
主动断开蓝牙函数 bt_enable_lowpoer_req()
定义在 protocol.c
文件中,作用是 MCU 通过该指令主动断开蓝牙连接。
应用步骤:
打开 TUYA_BCI_UART_COMMON_ACTIVE_DISCONNECT
宏定义,开启 MCU 主动断开蓝牙连接功能。
MCU 自行调用主动断开蓝牙函数 bt_disconnect_req()
,发送主动断开蓝牙指令 0xE7
到模组,主动断开蓝牙连接。
在串口接收数据处理函数 data_handle()
中,收到模组发送的主动断开蓝牙结果通知,SDK 调用主动断开蓝牙结果处理函数 bt_disconnect_result()
接收处理,该函数逻辑代码需要用户自行完成。
#define TUYA_BCI_UART_COMMON_ACTIVE_DISCONNECT 0xE7 //Disconnect device Bluetooth connection
/*****************************************************************************
Function name: bt_disconnect_req
Function description: send a request to disconnect the Bluetooth connection to the module
Input parameters: value 0 off, 1 on
Return parameter: none
Instructions for use:
*****************************************************************************/
void bt_disconnect_req(void)
{
bt_uart_write_frame(TUYA_BCI_UART_COMMON_ACTIVE_DISCONNECT,0);
}
void data_handle(unsigned short offset)
{
......
#ifdef TUYA_BCI_UART_COMMON_ACTIVE_DISCONNECT
case TUYA_BCI_UART_COMMON_ACTIVE_DISCONNECT:
bt_disconnect_result(bt_uart_rx_buf[offset + DATA_START]);
break;
#endif
......
}
/*****************************************************************************
Function name: bt_disconnect_result
Function description: receive the result that the module is disconnected from Bluetooth
Input parameters: result result 0 successful, other failed
Return parameter: none
Instructions: MCU needs to improve the function on its own.
*****************************************************************************/
void bt_disconnect_result(unsigned char result)
{
#error "Please improve the function by yourself and delete the line after completion"
if(result == 0x00)
{
//success
}
else
{
//failed
}
}
OTA(Over-the-Air)即空中下载技术,通过网络远程为设备更新和升级软件程序。
蓝牙通用串口协议支持 MCU OTA 功能,通过涂鸦开发者平台,先将需要更新的固件文件上传至涂鸦,然后通过蓝牙网关或手机 App(如涂鸦智能)等载体下载固件并透传给蓝牙模组。蓝牙模组再通过涂鸦协议对文件进行分包传输,最后 MCU 接收升级包并写入本地闪存,最终实现固件的升级。
详细说明请参考 OTA 升级说明。
MCU OTA 功能需要设备 MCU 资源支持,设备配网成功后,通过平台配置或升级检测,可以启动 OTA 流程,升级流程如下:
设备不论是在调试阶段还是出厂阶段,都可以使用 MCU OTA 功能进行 MCU 固件的更新迭代,实现设备功能的优化升级。在 MCU 硬件资源支持的情况下,建议增加 OTA 功能。
固件管理:使用 MCU OTA 功能需要先将验证正确的 MCU 固件上传到涂鸦,固件上传及管理操作,请参考 固件管理。
根据是否移植涂鸦协议的 MCU SDK 分为移植 SDK 和自行对接,这两种方式,都需要自行完成 BootLoader 开发。
设备需要先完成协议基础功能对接,MCU RAM 建议大于 512 Byte。
OTA 流程涉及多个协议指令:
0xEA
0xEB
0xEC
0xED
0xEE
0xEA
指令作用:模组发送该指令到 MCU,查询 MCU 串口能传输的最大单包长度。
应用场景:启动 OTA 升级后,模组首先会使用 0xEA
指令向 MCU 确认传输升级包分包大小。
指令详细介绍,请参考 蓝牙通用串口协议-MCU OTA 升级请求。
0xEB
指令作用:模组通过该指令发送升级文件信息到 MCU,包括产品 PID、文件版本、文件 MD5、文件长度及 CRC32。MCU 查询后可比较升级信息决定是否升级。
应用场景:启动 OTA 升级后,模组首先会使用 0xEA
指令向 MCU 确认传输升级包分包大小,确认后模组会使用 0xEB
指令向 MCU 发送升级文件信息,MCU 收到后可比较升级信息决定是否升级。
指令详细介绍,请参考 蓝牙通用串口协议-MCU OTA 升级文件信息
0xEC
指令作用:模组和 MCU 协商文件起始传输偏移量。
应用场景:在 MCU 回复模组发送的升级文件信息确认指令 0xEB
,确认升级后,模组发送升级文件偏移请求指令到 MCU,与 MCU 协商文件传输的偏移地址。
指令详细介绍,请参考 蓝牙通用串口协议-MCU OTA 升级文件偏移请求。
0xED
指令作用:模组通过该指令将升级包分包传输给 MCU。
应用场景:模组与 MCU 协商完文件传输的偏移地址后,模组通过升级数据指令给 MCU 传输升级包分包。
指令详细介绍,请参考 蓝牙通用串口协议-MCU OTA 升级数据。
0xEE
指令作用:OTA 结束时,模组向 MCU 确认升级结果。
应用场景:模组给 MCU 传输完升级包分包后,发送升级结束指令到 MCU 确认本次升级结果。
指令详细介绍,请参考 蓝牙通用串口协议-MCU OTA 升级结束。
升级请求回复函数mcu_ota_start_req()
定义在 mcu_ota_handler.c
文件中,作用是回复模组发送的升级请求,商定升级最大单包长度。
应用步骤:
打开 SUPPORT_MCU_FIRM_UPDATE
宏定义,开启 MCU 固件升级功能。
在串口接收数据处理函数 data_handle()
中,收到模组发送的最大单包数据请求升级,SDK 调用 OTA 处理函数 mcu_ota_proc()
接收处理。
OTA 处理函数 mcu_ota_proc()
判断是升级请求指令,调用升级请求回复函数 mcu_ota_start_req()
发送升级标志位、MCU 当前固件版本号和 MCU 可接受的最大单包长度。
#define SUPPORT_MCU_FIRM_UPDATE //Enable MCU firmware upgrade (default)
/* Firmware package size selection */
#ifndef SUPPORT_MCU_FIRM_UPDATE
#define BT_UART_QUEUE_LMT 16 //The size of the data receiving queue can be reduced if the RAM of MCU is not enough.
#define BT_UART_RECV_BUF_LMT 128 //According to the size of the user's DP data, it must be greater than 32
#else
#define BT_UART_QUEUE_LMT 512 //The size of the data receiving queue can be reduced if the RAM of MCU is not enough.
#define BT_UART_RECV_BUF_LMT 256 //Firmware upgrade buffer, large cache required, must be greater than 260
#endif
void data_handle(unsigned short offset)
{
......
#ifdef SUPPORT_MCU_FIRM_UPDATE
case TUYA_BCI_UART_COMMON_MCU_OTA_REQUEST:
case TUYA_BCI_UART_COMMON_MCU_OTA_FILE_INFO:
case TUYA_BCI_UART_COMMON_MCU_OTA_FILE_OFFSET:
case TUYA_BCI_UART_COMMON_MCU_OTA_DATA:
case TUYA_BCI_UART_COMMON_MCU_OTA_END:
total_len = bt_uart_rx_buf[offset + LENGTH_HIGH] * 0x100;
total_len += bt_uart_rx_buf[offset + LENGTH_LOW];
mcu_ota_proc(cmd_type,&bt_uart_rx_buf[offset + DATA_START],total_len);
break;
#endif
......
}
void mcu_ota_proc(uint16_t cmd,uint8_t*recv_data,uint32_t recv_len)
{
......
case TUYA_BCI_UART_COMMON_MCU_OTA_REQUEST:
mcu_ota_start_req(recv_data,recv_len);
break;
......
}
static void mcu_ota_start_req(uint8_t*recv_data,uint32_t recv_len)
{
uint8_t p_buf[12];
uint8_t payload_len = 0;
uint32_t current_version = MCU_OTA_VERSION;
uint16_t length = 0;
if(mcu_ota_status_get()!=MCU_OTA_STATUS_NONE)
{
TUYA_OTA_LOG("current ota status is not MCU_OTA_STATUS_NONE and is : %d !",mcu_ota_status_get());
return;
}
p_buf[0] = MCU_OTA_TYPE;
p_buf[1] = (current_version>>16)&0xff;
p_buf[2] = (current_version>>8)&0xff;
p_buf[3] = current_version&0xff;
p_buf[4] = MAX_DFU_DATA_LEN>>8;
p_buf[5] = MAX_DFU_DATA_LEN;
mcu_ota_status_set(MCU_OTA_STATUS_START);
payload_len = 6;
length = set_bt_uart_buffer(length,(unsigned char *)p_buf,payload_len);
bt_uart_write_frame(TUYA_BCI_UART_COMMON_MCU_OTA_REQUEST,length);
}
升级文件信息确认函数 mcu_ota_file_info_req()
定义在 mcu_ota_handler.c
文件中,作用是回复模组发送的升级文件信息指令 0xEB
,确认当前升级状态及升级文件信息。
应用步骤:
打开 SUPPORT_MCU_FIRM_UPDATE
宏定义,开启 MCU 固件升级功能。
在串口接收数据处理函数 data_handle()
中,收到模组发送的升级文件信息,SDK 调用OTA处理函数 mcu_ota_proc()
接收处理。
OTA 处理函数 mcu_ota_proc()
判断是升级文件信息指令 0xEB
,调用升级文件信息确认函数 mcu_ota_file_info_req()
发送当前升级状态和升级文件信息。
void data_handle(unsigned short offset)
{
......
#ifdef SUPPORT_MCU_FIRM_UPDATE
case TUYA_BCI_UART_COMMON_MCU_OTA_REQUEST:
case TUYA_BCI_UART_COMMON_MCU_OTA_FILE_INFO:
case TUYA_BCI_UART_COMMON_MCU_OTA_FILE_OFFSET:
case TUYA_BCI_UART_COMMON_MCU_OTA_DATA:
case TUYA_BCI_UART_COMMON_MCU_OTA_END:
total_len = bt_uart_rx_buf[offset + LENGTH_HIGH] * 0x100;
total_len += bt_uart_rx_buf[offset + LENGTH_LOW];
mcu_ota_proc(cmd_type,&bt_uart_rx_buf[offset + DATA_START],total_len);
break;
#endif
......
}
void mcu_ota_proc(uint16_t cmd,uint8_t*recv_data,uint32_t recv_len)
{
......
case TUYA_BCI_UART_COMMON_MCU_OTA_FILE_INFO:
mcu_ota_file_info_req(recv_data,recv_len);
break;
......
}
static void mcu_ota_file_info_req(uint8_t*recv_data,uint32_t recv_len)
{
uint8_t p_buf[30];
uint8_t payload_len = 0;
uint32_t file_version;
uint32_t file_length;
uint32_t file_crc;
uint8_t file_md5;
// uint8_t file_md5[16];
uint16_t length = 0;
uint8_t state;
if(mcu_ota_status_get()!=MCU_OTA_STATUS_START)
{
TUYA_OTA_LOG("current ota status is not MCU_OTA_STATUS_START and is : %d !",mcu_ota_status_get());
return;
}
file_version = recv_data[0+8]<<16;
file_version += recv_data[1+8]<<8;
file_version += recv_data[2+8];
if(memcmp(s_dfu_settings.progress.firmware_file_md5,&recv_data[3+8],16)==0)
{
file_md5 = TRUE;
}
else
{
file_md5 = FALSE;
}
file_length = recv_data[27]<<24;
file_length += recv_data[28]<<16;
file_length += recv_data[29]<<8;
file_length += recv_data[30];
file_crc = recv_data[31]<<24;
file_crc += recv_data[32]<<16;
file_crc += recv_data[33]<<8;
file_crc += recv_data[34];
if (memcmp(&recv_data[0], PRODUCT_KEY, 8) == 0)
{
if((file_version > MCU_OTA_VERSION)&&(file_length <= APP_NEW_FW_MAX_SIZE))
{
if(file_md5&&(s_dfu_settings.progress.firmware_file_version==file_version)&&(s_dfu_settings.progress.firmware_file_length==file_length)
&&(s_dfu_settings.progress.firmware_file_crc==file_crc))
{
state = 0;
}
else
{
memset(&s_dfu_settings.progress, 0, sizeof(dfu_progress_t));
s_dfu_settings.progress.firmware_image_crc_last = 0;
s_dfu_settings.progress.firmware_file_version = file_version;
s_dfu_settings.progress.firmware_file_length = file_length;
s_dfu_settings.progress.firmware_file_crc = file_crc;
memcpy(s_dfu_settings.progress.firmware_file_md5,&recv_data[3+8],16);
s_dfu_settings.write_offset = s_dfu_settings.progress.firmware_image_offset_last;
state = 0;
mcu_flash_write(DFU_SETTING_SAVE_ADDR,(uint8_t*)&s_dfu_settings,sizeof(s_dfu_settings));
}
m_firmware_start_addr = APP_NEW_FW_START_ADR;
m_firmware_size_req = s_dfu_settings.progress.firmware_file_length;
}
else
{
if(file_version <= MCU_OTA_VERSION)
{
TUYA_OTA_LOG("ota file version error !");
state = 2;
}
else
{
TUYA_OTA_LOG("ota file length is bigger than rev space !");
state = 3;
}
}
}
else
{
TUYA_OTA_LOG("ota pid error !");
state = 1;
}
memset(p_buf,0,sizeof(p_buf));
p_buf[0] = state;
if(state==0)
{
uint32_t crc_temp = 0; if(file_crc_check_in_flash(s_dfu_settings.progress.firmware_image_offset_last,&crc_temp)==0)
{
if(crc_temp != s_dfu_settings.progress.firmware_image_crc_last)
{
s_dfu_settings.progress.firmware_image_offset_last = 0;
s_dfu_settings.progress.firmware_image_crc_last = 0;
s_dfu_settings.write_offset = s_dfu_settings.progress.firmware_image_offset_last;
mcu_flash_write(DFU_SETTING_SAVE_ADDR,(uint8_t*)&s_dfu_settings,sizeof(s_dfu_settings));
}
}
p_buf[1] = s_dfu_settings.progress.firmware_image_offset_last>>24;
p_buf[2] = s_dfu_settings.progress.firmware_image_offset_last>>16;
p_buf[3] = s_dfu_settings.progress.firmware_image_offset_last>>8;
p_buf[4] = (uint8_t)s_dfu_settings.progress.firmware_image_offset_last;
p_buf[5] = s_dfu_settings.progress.firmware_image_crc_last>>24;
p_buf[6] = s_dfu_settings.progress.firmware_image_crc_last>>16;
p_buf[7] = s_dfu_settings.progress.firmware_image_crc_last>>8;
p_buf[8] = (uint8_t)s_dfu_settings.progress.firmware_image_crc_last;
mcu_ota_status_set(MCU_OTA_STATUS_FILE_INFO);
current_package = 0;
last_package = 0;
TUYA_OTA_LOG("ota file length : 0x%04x",s_dfu_settings.progress.firmware_file_length);
TUYA_OTA_LOG("ota file crc : 0x%04x",s_dfu_settings.progress.firmware_file_crc);
TUYA_OTA_LOG("ota file version : 0x%04x",s_dfu_settings.progress.firmware_file_version);
TUYA_OTA_LOG("ota firmware_image_offset_last : 0x%04x",s_dfu_settings.progress.firmware_image_offset_last);
TUYA_OTA_LOG("ota firmware_image_crc_last : 0x%04x",s_dfu_settings.progress.firmware_image_crc_last);
TUYA_OTA_LOG("ota firmware write offset : 0x%04x",s_dfu_settings.write_offset);
}
payload_len = 25;
length = set_bt_uart_buffer(length,(unsigned char *)p_buf,payload_len);
bt_uart_write_frame(TUYA_BCI_UART_COMMON_MCU_OTA_FILE_INFO,length);
}
升级请求回复函数 mcu_ota_offset_req()
定义在 mcu_ota_handler.c
文件中,作用是回复模组发送的升级文件偏移请求,商定起始传输文件偏移量。
应用步骤:
打开 SUPPORT_MCU_FIRM_UPDATE
宏定义,开启 MCU 固件升级功能。
在串口接收数据处理函数 data_handle()
中,收到模组发送的升级文件偏移请求,SDK 调用 OTA 处理函数 mcu_ota_proc()
接收处理。
OTA 处理函数 mcu_ota_proc()
判断是升级文件偏移请求指令 0xEC
,调用升级文件偏移函数 mcu_ota_offset_req()
发送 MCU 要求的起始传输文件偏移量。
void data_handle(unsigned short offset)
{
......
#ifdef SUPPORT_MCU_FIRM_UPDATE
case TUYA_BCI_UART_COMMON_MCU_OTA_REQUEST:
case TUYA_BCI_UART_COMMON_MCU_OTA_FILE_INFO:
case TUYA_BCI_UART_COMMON_MCU_OTA_FILE_OFFSET:
case TUYA_BCI_UART_COMMON_MCU_OTA_DATA:
case TUYA_BCI_UART_COMMON_MCU_OTA_END:
total_len = bt_uart_rx_buf[offset + LENGTH_HIGH] * 0x100;
total_len += bt_uart_rx_buf[offset + LENGTH_LOW];
mcu_ota_proc(cmd_type,&bt_uart_rx_buf[offset + DATA_START],total_len);
break;
#endif
......
}
void mcu_ota_proc(uint16_t cmd,uint8_t*recv_data,uint32_t recv_len)
{
......
case TUYA_BCI_UART_COMMON_MCU_OTA_FILE_OFFSET:
mcu_ota_offset_req(recv_data,recv_len);
break;
......
}
static void mcu_ota_offset_req(uint8_t*recv_data,uint32_t recv_len)
{
uint8_t p_buf[5];
uint8_t payload_len = 0;
uint32_t offset;
uint16_t length = 0;
if(mcu_ota_status_get()!=MCU_OTA_STATUS_FILE_INFO)
{
TUYA_OTA_LOG("current ota status is not MCU_OTA_STATUS_FILE_INFO and is : %d !",mcu_ota_status_get());
return;
}
offset = recv_data[0]<<24;
offset += recv_data[1]<<16;
offset += recv_data[2]<<8;
offset += recv_data[3];
if((offset==0)&&(s_dfu_settings.progress.firmware_image_offset_last!=0))
{
s_dfu_settings.progress.firmware_image_crc_last = 0;
s_dfu_settings.progress.firmware_image_offset_last = 0;
s_dfu_settings.write_offset = s_dfu_settings.progress.firmware_image_offset_last;
mcu_flash_write(DFU_SETTING_SAVE_ADDR,(uint8_t*)&s_dfu_settings,sizeof(s_dfu_settings));
}
p_buf[0] = s_dfu_settings.progress.firmware_image_offset_last>>24;
p_buf[1] = s_dfu_settings.progress.firmware_image_offset_last>>16;
p_buf[2] = s_dfu_settings.progress.firmware_image_offset_last>>8;
p_buf[3] = (uint8_t)s_dfu_settings.progress.firmware_image_offset_last;
mcu_ota_status_set(MCU_OTA_STATUS_FILE_OFFSET);
payload_len = 4;
length = set_bt_uart_buffer(length,(unsigned char *)p_buf,payload_len);
bt_uart_write_frame(TUYA_BCI_UART_COMMON_MCU_OTA_FILE_OFFSET,length);
}
升级数据处理函数 mcu_ota_data_req()
定义在 mcu_ota_handler.c
文件中,作用是回复模组发送的升级请求,商定升级最大单包长度。
应用步骤:
打开 SUPPORT_MCU_FIRM_UPDATE
宏定义,开启 MCU 固件升级功能。
在串口接收数据处理函数 data_handle()
中,收到模组发送的OTA升级传输数据包,SDK 调用 OTA 处理函数 mcu_ota_proc()
接收处理。
OTA 处理函数 mcu_ota_proc()
判断是升级数据指令 0xED
,调用升级数据处理函数 mcu_ota_data_req()
回复数据包接收情况。
void data_handle(unsigned short offset)
{
......
#ifdef SUPPORT_MCU_FIRM_UPDATE
case TUYA_BCI_UART_COMMON_MCU_OTA_REQUEST:
case TUYA_BCI_UART_COMMON_MCU_OTA_FILE_INFO:
case TUYA_BCI_UART_COMMON_MCU_OTA_FILE_OFFSET:
case TUYA_BCI_UART_COMMON_MCU_OTA_DATA:
case TUYA_BCI_UART_COMMON_MCU_OTA_END:
total_len = bt_uart_rx_buf[offset + LENGTH_HIGH] * 0x100;
total_len += bt_uart_rx_buf[offset + LENGTH_LOW];
mcu_ota_proc(cmd_type,&bt_uart_rx_buf[offset + DATA_START],total_len);
break;
#endif
......
}
void mcu_ota_proc(uint16_t cmd,uint8_t*recv_data,uint32_t recv_len)
{
......
case TUYA_BCI_UART_COMMON_MCU_OTA_DATA:
mcu_ota_data_req(recv_data,recv_len);
break;
......
}
static void mcu_ota_data_req(uint8_t*recv_data,uint32_t recv_len)
{
TUYA_OTA_LOG("%s",__func__);
uint8_t p_buf[2];
uint8_t payload_len = 0;
uint8_t state = 0;
uint16_t len;
uint8_t p_balloc_buf[256];
uint16_t length = 0;
if((mcu_ota_status_get()!=MCU_OTA_STATUS_FILE_OFFSET)&&(mcu_ota_status_get()!=MCU_OTA_STATUS_FILE_DATA))
{
TUYA_OTA_LOG("current ota status is not MCU_OTA_STATUS_FILE_OFFSET or MCU_OTA_STATUS_FILE_DATA and is : %d !",mcu_ota_status_get());
return;
}
state = 0;
current_package = (recv_data[0]<<8)|recv_data[1];
len = (recv_data[2]<<8)|recv_data[3];
if((current_package!=(last_package+1))&&(current_package!=0))
{
TUYA_OTA_LOG("ota received package number error.received package number : %d",current_package);
state = 1;
}
else if(len>MAX_DFU_DATA_LEN)
{
TUYA_OTA_LOG("ota received package data length error : %d",len);
state = 5;
}
else
{
uint32_t const write_addr = APP_NEW_FW_START_ADR + s_dfu_settings.write_offset;
if(write_addr>=APP_NEW_FW_END_ADR)
{
TUYA_OTA_LOG("ota write addr error.");
state = 1;
}
if(write_addr%CODE_PAGE_SIZE==0)
{
if (mcu_flash_erase(write_addr,4096)!= 0)
{
TUYA_OTA_LOG("ota Erase page operation failed");
state = 4;
}
}
if(state==0)
{
len = (recv_data[2]<<8)|recv_data[3];
memcpy(p_balloc_buf, &recv_data[6], len);
uint8_t ret = mcu_flash_write(write_addr, p_balloc_buf, len);
TUYA_OTA_LOG("ota save len :%d",len);
if (ret != 0)
{
state = 4;
}
else
{
s_dfu_settings.progress.firmware_image_crc_last = crc32_compute(p_balloc_buf, len, &s_dfu_settings.progress.firmware_image_crc_last);
s_dfu_settings.write_offset += len;
s_dfu_settings.progress.firmware_image_offset_last += len;
if((current_package+1)%32==0)
{
mcu_flash_write(DFU_SETTING_SAVE_ADDR,(uint8_t*)&s_dfu_settings,sizeof(s_dfu_settings));
}
}
}
}
p_buf[0] = state;
mcu_ota_status_set(MCU_OTA_STATUS_FILE_DATA);
payload_len = 1;
length = set_bt_uart_buffer(length,(unsigned char *)p_buf,payload_len);
bt_uart_write_frame(TUYA_BCI_UART_COMMON_MCU_OTA_DATA,length);
if(state!=0)
{
TUYA_OTA_LOG("ota error so free!");
mcu_ota_status_set(MCU_OTA_STATUS_NONE);
mcu_ota_init_disconnect();
memset(&s_dfu_settings, 0, sizeof(dfu_settings_t));
mcu_flash_write(DFU_SETTING_SAVE_ADDR,(uint8_t*)&s_dfu_settings,sizeof(s_dfu_settings));
}
else
{
last_package = current_package;
}
}
升级结束处理函数 mcu_ota_end_req()
定义在 mcu_ota_handler.c
文件中,作用是回复模组发送的升级结束通知。
应用步骤:
打开 SUPPORT_MCU_FIRM_UPDATE
宏定义,开启 MCU 固件升级功能。
在串口接收数据处理函数 data_handle()
中,收到模组发送的升级结束通知,SDK 调用 OTA 处理函数 mcu_ota_proc()
接收处理。
OTA 处理函数 mcu_ota_proc()
判断是升级结束指令 0xEE
,调用升级结束处理函数 mcu_ota_end_req()
回复升级完成的结果。
void data_handle(unsigned short offset)
{
......
#ifdef SUPPORT_MCU_FIRM_UPDATE
case TUYA_BCI_UART_COMMON_MCU_OTA_REQUEST:
case TUYA_BCI_UART_COMMON_MCU_OTA_FILE_INFO:
case TUYA_BCI_UART_COMMON_MCU_OTA_FILE_OFFSET:
case TUYA_BCI_UART_COMMON_MCU_OTA_DATA:
case TUYA_BCI_UART_COMMON_MCU_OTA_END:
total_len = bt_uart_rx_buf[offset + LENGTH_HIGH] * 0x100;
total_len += bt_uart_rx_buf[offset + LENGTH_LOW];
mcu_ota_proc(cmd_type,&bt_uart_rx_buf[offset + DATA_START],total_len);
break;
#endif
......
}
void mcu_ota_proc(uint16_t cmd,uint8_t*recv_data,uint32_t recv_len)
{
......
case TUYA_BCI_UART_COMMON_MCU_OTA_END:
mcu_ota_end_req(recv_data,recv_len);
break;
......
}
static void mcu_ota_end_req(uint8_t*recv_data,uint32_t recv_len)
{
if(mcu_ota_status_get()==MCU_OTA_STATUS_NONE)
{
TUYA_OTA_LOG("current ota status is MCU_OTA_STATUS_NONE!");
return;
}
on_data_write_request_sched(NULL);
}
蓝牙通用串口协议提供了 RF 射频测试功能。
设备完成基础功能后,若需要对模组进行 RF 射频测试,或者把模组产测结合到整机产测中,可使用模组的 RF 射频测试功能,触发测试的方式可由 MCU 端自定义。
0x0E
蓝牙产测功能是指 MCU 发起产测,模组内部扫描指定的蓝牙信标: ty_mdev
,若扫描成功,返回信号强度。
应用场景:准备一个蓝牙信标,释放信标信号:ty_mdev
。测试时建议将路由与设备距离控制在较近(0.5米左右),然后通过串口发送该RF射频测试指令,模组会搜索蓝牙信标并返回信号强度值,一般信号强度大于 -70db
认为模组射频工作正常。
一些特殊芯片/模组,目前只有 BK3431Q、BK3432,这类模组不支持扫描信标的方式,测试 RSSI 需要使用以下工具:
RF 射频测试
RF 射频测试函数 bt_rf_test_req()
定义在 protocol.c
文件中,作用是发送 RF 射频测试指令 0x0E
到模组,启动 RF 射频测试。
应用步骤:
打开 TUYA_BCI_UART_COMMON_RF_TEST
宏定义,开启 RF 射频测试功能。
MCU 自行调用 bt_rf_test_req()
函数,发送RF射频测试指令 0x0E
到模组,启动 RF 射频测试。
在串口接收数据处理函数 data_handle()
中,当收到模组发来的产测结果时,MCU 主动调用测试结果处理函数 bt_rf_test_result()
接收并做下一步处理,bt_rf_test_result()
的代码逻辑需要用户自行完成。
#define TUYA_BCI_UART_COMMON_RF_TEST 0x0E //rf radio frequency test
/*****************************************************************************
Function name: bt_rf_test_req
Function description: transmit frequency test request to the module
Input parameters: None
Return parameter: none
Instructions for use:
*****************************************************************************/
void bt_rf_test_req(void)
{
bt_uart_write_frame(TUYA_BCI_UART_COMMON_RF_TEST,0);
}
void data_handle(unsigned short offset)
{
......
#ifdef TUYA_BCI_UART_COMMON_RF_TEST
case TUYA_BCI_UART_COMMON_RF_TEST:
if(my_memcmp((unsigned char *)bt_uart_rx_buf + offset + DATA_START+7,"true",4)==0)
{
bt_rssi = (bt_uart_rx_buf[offset + DATA_START+21]-'0')*10 + (bt_uart_rx_buf[offset + DATA_START+22]-'0');
bt_rssi = -bt_rssi;
bt_rf_test_result(1,bt_rssi);
}
else
{
bt_rf_test_result(0,0);
}
break;
#endif
......
}
/*****************************************************************************
Function name: bt_rf_test_result
Function description: Bluetooth RF test feedback
Input parameter: Result: Bluetooth RF test result;0: failure /1: success
Rssi: Successful test indicates that the Bluetooth signal strength/test failure value is meaningless
Return parameter: none
Instructions: The MCU needs to improve the function itself
*****************************************************************************/
void bt_rf_test_result(unsigned char result,signed char rssi)
{
#error "Please improve the function by yourself and delete the line after completion"
if(result == 0)
{
// The test failed
}
else
{
// The test was successful
// RSSI is the signal strength, which is generally greater than -70dbM and within the normal range of Bluetooth signals
}
}
MCU 主动向模组查询模组当前工作状态。
模组在检测到 MCU 重启或 MCU 断线再上线,会通过报告设备工作状态指令 0x03
主动下发蓝牙工作状态给到 MCU,如果 MCU 需要主动查询蓝牙模组的工作状态,可通过发送查询蓝牙工作状态指令请求模组发送当前工作状态到 MCU。
0x0A
指令作用:用于 MCU 主动查询模组的工作状态。模组收到查询后将通过命令字 0x03
发送模组工作状态。
指令详细介绍,请参考 蓝牙通用串口协议-查询模组工作状态。
该内容对您有帮助吗?
是意见反馈该内容对您有帮助吗?
是意见反馈