mesh DFU
- 升级过程:
- 完整流程:
以前nRF SDK DFU的实现是通过nRF51 Dongle配合主机nRF connect工具,且借助Secure DFU的后台式更新速率较快(见另一篇笔记),现在的nRF mesh DFU分角色,全都由DK充当:一个带有serial串口交互的发送设备(source)和若干个待升级设备(relay或target)角色,从而实现整个网络所有设备全都批量升级,这对未来的物联网是一次崭新的创意点。这篇笔记会基于nrf SDK15.3+mesh3.2实现两个部分:
a)Configuring and Perfoming DFU over Mesh
b)Integrating DFU process into the application
升级过程:
按照官方教程《Configuring and Perfoming DFU over Mesh》安全升级需要使用nrfutil工具,但原先nrf SDK DFU的nrfutil不能直接使用,两者版本不兼容,否则会提示没有”--mesh””genpkg””--application-id”,也就是注意nrf5 SDK DFU和nrf5 mesh DFU的python工具有些微不同,要用到pc-util/mesh_dfu,pc-util/mesh_dfu仓库地址为:
https://github.com/NordicSemiconductor/pc-nrfutil/tree/mesh_dfu,安装前必须要把仓库完整下载下来,然后按照仓库pc-util/mesh_dfu提示来安装。(注意会覆盖原来的工具,可以备份,做法可看帖子)。
完整流程:
-
产生安全秘钥private_key,同时修改JSON对象配置
-
产生device_page,生成tools\dfu\bin\device_page_nrf52832_xxAA_s132_6.1.1.hex
-
生成升级包.zip(注意用mesh版本的nrfutil指令)
-
烧录协议栈bin\softdevice\s132_nrf52_6.1.1_softdevice.hex,烧录前需整片或整扇区擦除,由flash工作机理决定。
-
烧录引导程序mesh_bootloader_serial_gccarmemb_nrf52832_xxAA.hex实现IAP自编程,目录Bin\bootloader\gccarmemb\mesh_bootloader_serial_gccarmemb_nrf52832_xxAA.hex
-
烧录应用部分。当烧录应用时,可以烧录dfu例程examples\dfu\build\dfu_nrf52832_xxAA_s132_6.1.1_Debug\dfu_nrf52832_xxAA_s132_6.1.1.hex编译出来的固件做演示或者烧录用户自己mesh程序(要带dfu功能)比如examples\light_switch\server(DFU)\build\light_switch_server_nrf52832_xxAA_s132_6.1.1_dfu_Debug\light_switch_server_nrf52832_xxAA_s132_6.1.1_dfu.hex做实际项目。本文为演示,烧录模板例程,用户普通mesh程序怎么添加dfu见下一章节。
-
烧录刚才第二步生产的device_page,(跟以前nrf SDK DFU用mergehex指令合并这4大块为一个整体不同)4大块全部完成后让芯片复位:
-
可以检查一下device_page和bootloader(这步可做可不做):
-
串口启动升级过程。敲击命令nrfutilmesh dfu serial -pkg blinky_dfu_test.zip -p COM54 -b 115200 -fc --mesh:
注意:默认dfu是dual-bank方式,所以进度条提示100%完成时还要等40s左右等它新固件搬移复制到旧固件位置来(见上图),否则会出现flash有两个固件但bootloader依然引导旧程序运行并且还能进行下一次升级的现象,RTT终端提示 NRF_MESH_EVT_DFU_END才算完成,并且板子上4个灯会全部开始闪烁而不是官方教程说的仅1个灯亮,官方教程还有一处小错误,就是第6步烧录dfu模板例程时给定的路径是错的,按本文来,官方教程合计两处小错误。
2.添加 DFU功能到mesh程序:
按照官方教程《Integrating DFU process into the application》指导来做。目录examples\dfu为带dfu功能的模板,可以参考它来给自己的mesh程序添加DFU,具体做法比如light_switch/server在main.c添加充当target或relay角色相应处理代码:
static nrf_mesh_evt_handler_t m_evt_handler;
static bool fw_updated_event_is_for_me(const nrf_mesh_evt_dfu_t * p_evt)
{
switch (p_evt->fw_outdated.transfer.dfu_type)
{
case NRF_MESH_DFU_TYPE_APPLICATION:
return (p_evt->fw_outdated.current.application.app_id == p_evt->fw_outdated.transfer.id.application.app_id &&
p_evt->fw_outdated.current.application.company_id == p_evt->fw_outdated.transfer.id.application.company_id &&
p_evt->fw_outdated.current.application.app_version < p_evt->fw_outdated.transfer.id.application.app_version);
case NRF_MESH_DFU_TYPE_BOOTLOADER:
return (p_evt->fw_outdated.current.bootloader.bl_id == p_evt->fw_outdated.transfer.id.bootloader.bl_id &&
p_evt->fw_outdated.current.bootloader.bl_version < p_evt->fw_outdated.transfer.id.bootloader.bl_version);
case NRF_MESH_DFU_TYPE_SOFTDEVICE:
return false;
default:
return false;
}
}
static const uint32_t * optimal_bank_address(void)
{
/* The incoming transfer has to fit on both sides of the bank address: First it needs to fit
* above the bank address when we receive it, then it needs to fit below the bank address when
* we install it. We want to put the bank address in the middle of the available application
* code area, to maximize the potential transfer size we can accept. */
const uint32_t * p_start;
uint32_t dummy;
ERROR_CHECK(mesh_stack_persistence_flash_usage(&p_start, &dummy));
uint32_t middle_of_app_area = (CODE_START + (intptr_t) p_start) / 2;
/* The bank can't start in the middle of the application code, and should be page aligned: */
return (const uint32_t *) ALIGN_VAL(MAX(middle_of_app_area, CODE_END), PAGE_SIZE);
}
static void mesh_evt_handler(const nrf_mesh_evt_t* p_evt)
{
switch (p_evt->type)
{
case NRF_MESH_EVT_DFU_FIRMWARE_OUTDATED:
case NRF_MESH_EVT_DFU_FIRMWARE_OUTDATED_NO_AUTH:
if (fw_updated_event_is_for_me(&p_evt->params.dfu))
{
const uint32_t * p_bank = optimal_bank_address();
__LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Requesting DFU transfer with bank at 0x%p\n", p_bank);
ERROR_CHECK(nrf_mesh_dfu_request(p_evt->params.dfu.fw_outdated.transfer.dfu_type,
&p_evt->params.dfu.fw_outdated.transfer.id,
p_bank));
hal_led_mask_set(LEDS_MASK, false); /* Turn off all LEDs */
}
else
{
ERROR_CHECK(nrf_mesh_dfu_relay(p_evt->params.dfu.fw_outdated.transfer.dfu_type,
&p_evt->params.dfu.fw_outdated.transfer.id));
}
break;
case NRF_MESH_EVT_DFU_START:
//hal_led_mask_set(LEDS_MASK_DFU_RUNNING, true);
__LOG(LOG_SRC_APP, LOG_LEVEL_INFO,"NRF_MESH_EVT_DFU_START\n");
break;
case NRF_MESH_EVT_DFU_END:
//hal_led_mask_set(LEDS_MASK, false); /* Turn off all LEDs */
//hal_led_mask_set(LEDS_MASK_DFU_ENDED, true);
__LOG(LOG_SRC_APP, LOG_LEVEL_INFO,"NRF_MESH_EVT_DFU_END\n");
break;
case NRF_MESH_EVT_DFU_BANK_AVAILABLE:
//hal_led_mask_set(LEDS_MASK, false); /* Turn off all LEDs */
__LOG(LOG_SRC_APP, LOG_LEVEL_INFO,"NRF_MESH_EVT_DFU_BANK_AVAILABLE\n");
ERROR_CHECK(nrf_mesh_dfu_bank_flash(p_evt->params.dfu.bank.transfer.dfu_type));
break;
default:
break;
}
}
新建DFU文件夹,添加文件nrf_mesh_dfu.c,同时不妨添加serial串口操作功能让其有source角色功能,新建Serial文件夹,添加mesh/serial/src下全部文件(共14个),添加路径和宏定义NRF_MESH_SERIAL_ENABLE:
…/…/…/mesh/serial/api
…/…/…/mesh/serial/include
在主函数mesh_init加入代码:
#if NRF_MESH_SERIAL_ENABLE
ERROR_CHECK(nrf_mesh_serial_init(NULL));
#endif
Start函数加入代码:
#if NRF_MESH_SERIAL_ENABLE
__LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Enabling serial interface...\n");
ERROR_CHECK(nrf_mesh_serial_enable());
#endif