请问下,t5附属固件的ota升级,升级固件能不能先从云端完全下载到设备后,设备再传给待升级的设备进行升级
也就是t5有没有能保存完整固件的分区呢
T5_3.13.6 附属固件ota问题
T5_3.13.6 附属固件ota问题
Re: T5_3.13.6 附属固件ota问题
我看t5有一个ota分区,大小为2056K,我的附属固件升级文件大小为1500kb,请问我能先存放在这个分区里面吗,如果可以,我如何操作到这个分区呢,还是只能根据配置的分区首地址读写
Re: T5_3.13.6 附属固件ota问题
您好!针对您的问题逐一解答:
- T5 OTA 默认流程:流式边下载边烧录,不支持预下载完整固件
TuyaOS OTA 采用流式升级机制,在 tuya_svc_upgrade.c 中通过 HTTP 分片下载,每片仅 1KB 或 16KB(OTA_DOWMLOAD_BUF_SIZE),每片下载完成后立即回调写入 Flash,不存在完整的固件预缓存区。
- T5 Flash 分区布局
T5(BK7258)Flash 分区由芯片 SDK 的 tkl_flash_get_one_type_info() 提供,核心分区:
- TUYA_FLASH_TYPE_APP — APP 主固件运行区
- TUYA_FLASH_TYPE_OTA — OTA 缓存/管理区
- TUYA_FLASH_TYPE_APP_BIN — 固件镜像大小信息区
- TUYA_FLASH_TYPE_BTL0/BTL1 — 双备份 Bootloader
具体 OTA 分区大小需查看 T5/BK7258 芯片 Flash Layout 文件。
- 如何实现「先完整下载再升级」
如果业务场景需要先下载完整固件再升级(如设备转发场景),需要自定义实现:
① 禁用默认 OTA 流程,手动下载完整固件到 OTA 缓存区(需确保分区足够大)
② 下载完成后,通过 tal_ota_start_notify() + tal_ota_data_process() 分段写入目标分区
③ 写入完成后调用 tal_ota_end_notify(TRUE) 触发重启
关键接口参考 components/tal_system/include/tal_ota.h:
Code: Select all
// 查询最大允许固件大小
OPERATE_RET tal_ota_get_ability(UINT32_T *image_size, TUYA_OTA_TYPE_E *type);
// 启动 OTA(可指定目标路径 TUYA_OTA_PATH_AIR/SEC_A/SEC_B)
OPERATE_RET tal_ota_start_notify(UINT32_T image_size, TUYA_OTA_TYPE_E type, TUYA_OTA_PATH_E path);
// 分段写入数据
OPERATE_RET tal_ota_data_process(TUYA_OTA_DATA_T *pack, UINT32_T* remain_len);
// 结束并可选重启
OPERATE_RET tal_ota_end_notify(BOOL_T reset);
- 附属固件(Attach OTA)
附属固件通过 DEV_ATTACH_MOD_1 等通道扩展,数据通常通过 UART/总线转发给外部 MCU,下载机制与主固件相同,均为流式。示例代码参考 example_attach_ota.c。
请问您使用附属固件 OTA 的具体场景是什么?是模块内部附属固件升级,还是需要通过 T5 模块转发给其他设备?
Re: T5_3.13.6 附属固件ota问题
目前对接了一个蓝牙设备,需要把ota功能做到t5这边,现在想以附属固件升级的形式对接
目前的想法是,能不能先完整将ota固件下载下来,然后再通过串口发给蓝牙设备
Re: T5_3.13.6 附属固件ota问题
看了下您的需求,总结一下:T5 做 OTA 中继,先完整下载固件到本地,再通过串口转发给蓝牙设备。
1. TuyaOS OTA 默认是流式架构,不支持预缓存
TuyaOS OTA()采用的是流式边下载边烧录机制:
- 云端 CDN 分片下发,每片 1KB 或 16KB
- 每收到一片,立即调用 写入 Flash
- 不存在完整的固件预缓存区
2. 附属固件 OTA 的数据流
附属固件(扩展 OTA 通道,channel >= 10)走 → ,数据同样以流式推送给应用层的 回调,每来一片推一片。
文档里有 MCU 串口转发 OTA 的示例代码:
Code: Select all
OPERATE_RET get_file_data_cb(IN CONST FW_UG_S *fw, IN CONST UINT_T total_len,
IN CONST UINT_T offset, IN const BYTE_T *data,
IN CONST UINT_T len, OUT UINT_T *remain_len,
IN PVOID_T pri_data) {
tkl_uart_write(UART_NUM_0, data, len); // 每片直接转发
*remain_len = 0;
return OPRT_OK;
}
这是每片立即转发的模式,不能满足您「先完整下载再转发」的需求。
3. 如何实现先完整下载再转发
您说的 OTA 分区 2056K 够用(固件 1500KB),可以这样实现:
① 注册一个扩展 OTA 通道(如 channel=10),在 里声明
② 中收到您的 channel 时,调用 并注册一个自定义
③ 在 中,先不转发,把所有数据片缓存到 OTA 分区(通过 + 循环调用 ,实际上 SDK 本身就是这么存的,只是以片为单位自动写入 OTA 分区的连续地址)
④ 下载完成后,从 OTA 分区读取完整固件,再通过 UART 转发给蓝牙设备
关键约束:OTA 分区必须在 T5 芯片内置 Flash 上,TKL 层接口 可以读取 OTA 分区数据。转发给蓝牙设备时,蓝牙芯片的 OTA 协议需要您自行实现(涂鸦蓝牙芯片推荐走 BLE Notify/Indicate 推送固件片)。
4. 整体方案建议
Code: Select all
云端 → T5(Wi-Fi) → 内置Flash OTA分区(2056K) → UART → 蓝牙设备
↑
tuya_svc_upgrade 写入
优点:T5 只做透传,不关心蓝牙固件内容
注意:蓝牙 OTA 协议需要和蓝牙芯片原厂确认;T5 本身是 Wi-Fi 芯片不含 BLE 能力,靠串口和蓝牙芯片通信
总结:SDK 原生不提供「先完整下载再处理」的开关,但借助 OTA 分区作为缓存区 + 自定义 ,可以实现您的需求。
Re: T5_3.13.6 附属固件ota问题
如果我的需求是借助 OTA 分区作为缓存区,能提供一份简单的写入ota分区和从ota分区读取的demo代码吗
您好!针对借助 OTA 分区作为固件缓存区的需求,以下给出完整的操作流程和 demo 代码。
■ 第一步:获取 OTA 分区的起始地址和大小
Code: Select all
#include "tal_flash.h" // 组件层 Flash API
int get_ota_partition_info(UINT32_T *start_addr, UINT32_T *part_size, UINT32_T *block_size)
{
TUYA_FLASH_BASE_INFO_T info = {0};
OPERATE_RET ret;
ret = tal_flash_get_one_type_info(TUYA_FLASH_TYPE_OTA, &info);
if (ret != OPRT_OK || info.partition_num == 0) {
TAL_PR_ERR("get OTA partition info failed: %d", ret);
return ret;
}
*start_addr = info.partition[0].start_addr;
*part_size = info.partition[0].size;
*block_size = info.partition[0].block_size; // 通常为 4096(4KB sector)
TAL_PR_INFO("OTA partition: start=0x%08X, size=%u KB, block=%u B",
*start_addr, *part_size / 1024, *block_size);
return OPRT_OK;
}
■ 第二步:写入 OTA 分区(先擦再写)
Flash 写入前必须按 sector(通常 4096B)对齐擦除,否则写入无效:
Code: Select all
#define OTA_SECTOR_SIZE 4096 // T5/BK7258 Flash sector 大小
OPERATE_RET ota_cache_write(UINT32_T ota_start, UINT32_T offset,
const UINT8_T *data, UINT32_T len)
{
UINT32_T abs_addr = ota_start + offset;
OPERATE_RET ret;
// 写前必须按 4KB 对齐擦除
UINT32_T erase_addr = abs_addr & ~(OTA_SECTOR_SIZE - 1);
UINT32_T erase_size = ((len + OTA_SECTOR_SIZE - 1) / OTA_SECTOR_SIZE) * OTA_SECTOR_SIZE;
ret = tal_flash_erase(erase_addr, erase_size);
if (ret != OPRT_OK) {
TAL_PR_ERR("flash erase failed at 0x%08X: %d", erase_addr, ret);
return ret;
}
ret = tal_flash_write(abs_addr, data, len);
if (ret != OPRT_OK) {
TAL_PR_ERR("flash write failed at 0x%08X: %d", abs_addr, ret);
}
return ret;
}
■ 第三步:从 OTA 分区读取数据
Code: Select all
OPERATE_RET ota_cache_read(UINT32_T ota_start, UINT32_T offset,
UINT8_T *buf, UINT32_T len)
{
return tal_flash_read(ota_start + offset, buf, len);
}
■ 完整使用示例(附属固件 OTA → 缓存到 OTA 分区 → 串口转发蓝牙设备)
Code: Select all
static UINT32_T s_ota_start = 0;
static UINT32_T s_ota_size = 0;
// 初始化:获取分区信息并整块擦除
void ota_cache_init(void)
{
UINT32_T block = 0;
get_ota_partition_info(&s_ota_start, &s_ota_size, &block);
TAL_PR_INFO("OTA cache: start=0x%08X size=%uKB", s_ota_start, s_ota_size/1024);
// 整块擦除,后续按偏移直接写无需再擦
tal_flash_erase(s_ota_start, s_ota_size);
}
// 在 get_file_cb 中逐片写入 OTA 分区
OPERATE_RET get_file_data_cb(IN CONST FW_UG_S *fw, IN CONST UINT_T total_len,
IN CONST UINT_T offset, IN const BYTE_T *data,
IN CONST UINT_T len, OUT UINT_T *remain)
{
// 整块擦除后直接写,offset 处无需再对齐擦除
tal_flash_write(s_ota_start + offset, data, len);
TAL_PR_DEBUG("OTA cache [%u/%u]", offset + len, total_len);
if (offset + len >= total_len) {
TAL_PR_INFO("Firmware fully cached (%u bytes), start forwarding...", total_len);
ota_forward_via_uart(total_len);
}
*remain = 0;
return OPRT_OK;
}
// 从缓存区读出,通过 UART 逐片转发给蓝牙设备
void ota_forward_via_uart(UINT32_T fw_size)
{
#define UART_CHUNK 512
UINT8_T buf[UART_CHUNK];
UINT32_T sent = 0;
while (sent < fw_size) {
UINT32_T chunk = (fw_size - sent > UART_CHUNK) ? UART_CHUNK : (fw_size - sent);
tal_flash_read(s_ota_start + sent, buf, chunk);
// 按您的蓝牙 OTA 协议封包后发送
// tal_uart_write(TAL_UART_ID_2, buf, chunk);
sent += chunk;
}
TAL_PR_INFO("OTA forward done: %u bytes", fw_size);
}
■ 关键注意事项
- 动态查询分区地址:不要硬编码地址,通过 动态获取,不同版本的分区布局可能不同;
Code: Select all
tal_flash_get_one_type_info(TUYA_FLASH_TYPE_OTA, &info) - 擦除粒度:T5/BK7258 的 Flash sector 为 4KB,整块擦除时 addr 和 size 必须是 4096 的整数倍;
- 避免 OTA 分区竞争:TuyaOS 默认的云端 OTA 也使用该分区,缓存期间需确保云端没有下发 OTA 任务,否则双方会互相覆盖;
- 头文件:应用层只需 (组件层接口),不需要直接 include tkl_flash.h。
Code: Select all
#include "tal_flash.h"
如有问题欢迎继续追问!
Re: T5_3.13.6 附属固件ota问题
如果用tal的接口,我看了sdk里面只有tal_flash.h,并没有tal_flash.c,看不到具体的实现
但是我发现sdk里面有tkl_flash.c有tkl_flash_get_one_type_info接口,但ota的定义为
#define OTA_START 0x7c9000
#define OTA_SIZE (8 * 1024)
看着也不太对?
Re: T5_3.13.6 附属固件ota问题
您好!关于这两个困惑,逐一解释:
■ 1. tal_flash.c 在哪?
tal_flash.c 确实存在,路径为:
components/tal_driver/src/os/tal_flash.c
SDK 发布包通常将 components 组件层预编译为静态库(.a),所以在 SDK 目录里看不到 .c 源文件,只能看到 .h 头文件。但接口是完整可用的,只要 #include "tal_flash.h" 即可直接调用。
tal_flash_get_one_type_info() 内部实现(第133行)如下:
Code: Select all
OPERATE_RET tal_flash_get_one_type_info(TUYA_FLASH_TYPE_E type, TUYA_FLASH_BASE_INFO_T* info)
{
TKL_FLASH_DESC_T* flash_desc = tkl_flash_desc_get();
if (flash_desc->get_one_type_info == NULL) {
return OPRT_NOT_SUPPORTED;
}
return flash_desc->get_one_type_info(type, info);
}
它内部会自动转调 T5 平台 TKL 层,您无需直接调用 tkl_ 层函数。
■ 2. #define OTA_START 0x7c9000 / OTA_SIZE (8*1024) 是什么?
您在 tkl_flash.c 里看到的这两个宏是 BK7258 平台层(TKL 层)内部的硬件宏定义,仅供 tkl_flash.c 内部处理分区表时使用,并不是 OTA 存储区的实际容量。
T5 的 OTA 分区实际大小约为 2MB(您之前看到的 2056KB 才是正确的分区大小)。那个 8KB 是 TKL 内部元数据区,不代表整个 OTA 分区。
■ 正确的应用层用法(仅使用 tal_ 层接口):
Code: Select all
#include "tal_flash.h"
void get_ota_partition_info(void)
{
TUYA_FLASH_BASE_INFO_T info;
memset(&info, 0, sizeof(info));
if (tal_flash_get_one_type_info(TUYA_FLASH_TYPE_OTA, &info) == OPRT_OK) {
if (info.partition_num > 0) {
uint32_t ota_start = info.partition[0].start_addr;
uint32_t ota_size = info.partition[0].size;
TAL_PR_INFO("OTA partition: start=0x%08X, size=%u bytes",
ota_start, ota_size);
// T5 预期输出:size=2097152 (约2MB)
}
}
}
TUYA_FLASH_TYPE_OTA 是枚举值(值=5),定义在 tuya_cloud_types.h。
函数返回后,info.partition[0].start_addr 才是可以安全读写的 OTA 分区物理地址,info.partition[0].size 才是实际约 2MB 的分区大小。
■ 小结:
- tal_flash.h 对应的 tal_flash.c 已预编译进 .a,接口完整可用
- #define OTA_START/OTA_SIZE 是 TKL 平台内部宏,应用层不要直接使用
- 应用层统一通过 tal_flash_get_one_type_info(TUYA_FLASH_TYPE_OTA, &info) 获取运行时地址