垂直应用

STM32的云连接应用第三讲

远程固件升级





STM32生态系统第四期:云连接(一)基于STM32云连接应用的概览

STM32生态系统第七期——云连接(二)云平台之间的移植






欢迎收看《STM32生态系统》:基于STM32的云连接应用第三讲,远程固件升级。


FOTA的全称是:Firmware Over-The-Air。就是利用无线技术,利用云服务来实现远程的设备固件更新。


以前产品出厂后,如果需要升级程序,要么是通过预留的调试接口,要么通过串口,USB等普通通信接口利用事先写好的bootloader程序进行更新。总之都需要直接连线到板子上。这样带来的问题就是:不光需要安排人员到现场,升级的时候还需要打开机器外壳,取出主板,连接上位机才能升级。现在物联网产品,已经具备了上网的能力,我们完全可以利用这个功能,实现远程的设备固件更新,不用到现场,不用开盖,设备自动升级。有了这个功能后,可以对已经买出去的产品,修复bug,升级更稳定的软件版本等。



实现远程固件升级的功能,可以节省人力,提高效率,有这么多的好处。那对于我们开发者,在考虑添加这个功能的时候,该考虑哪些方面呢?


首先,FOTA这个功能一定是由两个部分共同实现:节点端的程序和云端的升级和存储服务。节点端就是需要进行固件升级的设备。运行在节点的程序,除了原有的功能以外,还要包括一部分FOTA功能,这部分程序完成与云端服务器通信,从云端下载固件,并更新MCU固件的工作。云端的服务器能够提供固件升级版本管理,升级文件存储等服务。


其次,选择一种升级方式。从存储器的划分角度可以分为:原位升级,乒乓升级,冗余升级;从升级文件的构成可以分为:全片升级,部分升级,差分升级等。


下载固件时,被中断的情况,是否需要考虑,怎么处理?


下载固件时,其他的用户程序功能能否被中断?下载过程被中断怎么处理等等。


采取什么方式来触发本地的升级?就是说,固件从云端下载下来后,是自动更新还是等用户操作?


怎么保证升级过程中的安全?这里的安全性,我们又可以分两个方面考虑:1.如何保证固件不被窃取,对知识产权的保护;2.如何保证下载下来的文件,是真正要下载的文件。保证文件的完整性,以及对程序运行安全的保护。


前面讲到了原位升级,乒乓升级和冗余升级方式,我们来了解一下这三种升级方式。


第一种,固件原位升级。这种方式,程序分为两部分:用户程序只负责接收云端服务器发送过来的固件更新通知,包括新软件版本和下载地址;第二部分程序是bootloader, 这部分程序主要负责从云端服务器下载新固件,并直接更新用户程序。下载完成后,再跳转执行新的用户程序。这种方式对Flash的需求最小,但它的问题是,本地用户程序没有备份,如果下载过程中出错,则用户程序无法运行,直到下载好一个完整的用户程序。最坏的是,如果下载失败,又无法连接网络,程序就没法再运行。并且这种升级方式不支持“后台下载”,即在下载和升级的时候,用户的业务逻辑无法同时运行。


第二种,固件乒乓升级。这种方式,在MCU上同时存在两个用户程序区域,分别存放不同版本的两个程序。MCU运行的时候,就在这两个用户程序区域间切换。在用户程序1的位置运行时,可以将新版本的程序写到用户程序2的位置,然后跳转执行。在用户程序2的位置运行时,可以将新版本的程序写到用户程序1的位置,然后跳转执行。这种方式的好处是支持“后台下载”,下载程序不影响正常程序的运行。比如洗衣机,可以一边洗衣服,一边下载新程序。下载失败也没关系,还可以在原程序区域继续运行。但是它对flash大小的需求就比第一种方式要大,相当于两倍用户程序的大小。


固件乒乓升级的方式中,在两个用户程序区域之间切换执行的操作也可以由一个bootloader来实现,每次都从bootloader启动,然后跳转到用户程序1或用户程序2的位置执行。但这种方式有一个问题,就是用户程序在MCU Flash的这两个执行区域运行时的地址是不一样的,这意味着用户程序编译的时候要设定不一样的ROM地址范围。而因为无法知道设备实际升级的时候,新程序会在哪个区域运行,所以还需要为同一个软件版本准备两套程序,分别是运行在区域1和区域2的,再由设备端的程序选择合适的版本下载。这样一来,整个升级系统的设计就变得复杂,而STM32的双bank启动就可以很完美的解决这个问题。STM32MCU Flash的两个bank可以分别作为两个程序运行区域,切换程序运行区域前,在选项字中设置不同的启动地址来选择不同的bank启动。STM32 MCU复位后,根据选项字的设定,将其对应的bank映射到0x08000000地址,并直接从该bank启动。因为不管是从bank1还是bank2启动执行,都是映射到了地址0x08000000,所以程序运行的地址都是一样的。这样的话就不存在前面所说的问题了。


第三种,固件冗余升级。“固件冗余“,顾名思义就是需要额外的空间来存储固件的备份(不作为可运行区域,这是和第二种方式的区别之一)。所以使用固件冗余升级方式时,节点端包含三部分的程序:BootLoader,用户程序和一个用户程序的备份。和第一种方式相比,这里的Bootloader功能更简单,它不需要实现网络功能,仅执行本地固件更新(将程序从备份的位置,拷贝到片内的执行位置),以及上电跳转到用户程序的功能。用户程序除了其他的产品功能外,还要实现网络通信以及固件下载的功能。从云端下载的程序,放在规划好的片上或片外存储器的相应位置。它可以像第二种方式一样,下载不影响正常的程序运行,同时又不需要MCU的flash双bank。这种方式还有一个好处,Bootloader跟网络下载无关,如果不换MCU,应用层的变化都对bootloader没有影响,不需要频繁更新bootloader。如果MCU的存储空间够大,而应用程序又小,也可以将这三部分都放到片上flash中。


接下来,我们就来看看今天要介绍的远程固件更新例程。该例程使用的是STM32F769探索套件,以百度IoT平台以及百度的BOS服务为例来进行演示。要升级的固件保存在百度的BOS服务器上。


STM32F769DK探索板集成的STM32F769NIH6芯片,片上有2M字节Flash和512K字节SRAM。板子上集成了有线的以太网接口和一个WIFI模块接口,并且还支持Arduino接口。所以使用F769DK探索板既可以通过以太网方式连接到云端,也可以通过WIFI模块接口或者Arduino接口扩展的无线模块连接到云端。板子上还通过QSPI接口外接了一个64M字节的NOR Flash,通过DSI接口外接了一个LCD屏。


今天要介绍的这个例程是通过WIFI无线的方式连接到云端的,采用的冗余升级方式。程序完成的功能有:首先与百度IoT平台建立MQTT连接。可以将开发板的状态,软件版本等信息上传到云端,并且接收云端下发的LED控制命令,新固件版本和下载地址。再根据接收到新固件下载地址,自动通过HTTP协议从对应的服务器下载新的固件。下载完成后程序通过LCD屏向用户提示可用的新版本信息,用户可以通过按键启动新固件的升级。下载和升级的过程支持固件完整性校验,一键恢复出厂默认固件。


在MCU端,使用到的外设有串口(连接WIFI模块),QSPI接口(扩展外部Flash),DSI接口连接LCD显示屏。



关于系统的内存划分:


在本例程里使用的是前面介绍的冗余升级的方式。用到外部的QSPI Flash来保存从云端接收到的新固件和程序运行的状态。


QSPI Flash分为三部分:
地址0开始的64K字节范围,用来保存程序运行的状态以及接收到的固件下载地址和版本号等。


地址0x00010000开始的4M字节,用来保存默认的应用程序固件。默认固件可以用来将当前的应用程序恢复到最初的版本。一般可以将第一个稳定的应用程序版本当做默认固件,烧写到QSPI Flash中。


地址0x00410000开始的4M字节,用来保存收到推送后,从云端下载的新固件。


STM32F769 MCU的内部Flash分为两部分:


第一部分,也就是地址0x08000000开始是Bootloader程序,板子复位后,先执行的是Bootloader的程序,之后再跳转到应用程序。在bootloader程序中会根据用户的操作决定是否执行更新用户程序的任务:将QSPI Flash中的固件更新到MCU内部Flash中。


第二部分,从地址0x08010000开始,是用户应用程序。



STM32F769DK远程固件升级例程分为两部分:Bootloader和用户应用程序,各自是独立的工程。工程项目的下载链接可以在本文最后找到。


Bootloader程序主要完成将新的应用程序从QSPI Flash中烧到MCU内部Flash中,并跳转到应用程序运行。功能相对简单。
用户应用程序部分的实现包括接收云端推送,自动下载以及断点续传这部分功能,程序结构相对复杂。MQTT和HTTP客户端的实现也在用户应用程序中。


每次上电启动都从bootloader开始执行,它会检查当前是否需要对MCU的固件进行更新。如果需要更新,则会先检查QSPI flash中固件的完整性,然后开始烧写用户应用程序部分。如果不需要更新,则直接跳转到用户程序执行。



下面,我们来了解一下例程中用户应用程序的软件架构。


用户应用程序的软件分层如下图所示,上层程序可以通过接口函数调用下层的服务:


底层驱动部分,包括STM32F7Cube HAL库,STM32F769DK BSP板级驱动和Wifi驱动
  

中间件部分,包括TCP/IP协议栈(Lwip),mbedTLS,MQTT客户端的C实现(Paho),JSON解析器(cJSON)和FreeRTOS
  

应用层实现了,连接百度IoT平台的MQTT应用,HTTP客户端以及远程固件更新的应用逻辑


在实际下载过程中,可能会遇到由于网络不稳定或者设备断电等因素造成的文件下载中断,文件部分内容丢失等问题。为了解决这些问题,保证下载文件的完整性,并且避免由于一小部分文件传输出错,而需要重新传输整个文件的情况,我们将原始的BIN文件划分成了多个数据块,并增添了数据长度,校验和等信息。



下载过程中,每下载完一个数据块马上进行校验,校验不通过就重新下载这个数据块,直到成功下载。BIN文件全部下载完成后,再对整个文件进行一次校验。这些添加的信息仅在HTTP下载的过程中用到,最终烧写到QSPI Flash中的是去掉了这些冗余信息的原始BIN文件。


下图中是升级文件格式的具体定义:


第一部分是长度为20字节的文件头,里面包括对整个文件的描述信息:识别码,bin文件的实际长度(不包括添加的冗余信息),数据块的大小和版本信息等。


接下来是分为多个相同大小区块的数据区,每个数据区块开头也有一段描述数据,包括这块数据的起始地址,长度,校验和等。数据块的大小由用户根据所使用的QSPI flash的规格定义好。


最后一部分是结束标志,包含了整个bin文件的校验和。


用户可以通过FOTABinConverter工具(随例程软件包提供)在原始的BIN文件上添加这些信息,生成后缀为cvt的文件,放在百度BOS服务器上供节点端下载。


例程的运行过程可以通过这张流程图来描述。


在这个流程图中,最左边是设备端,也就是STM32F769探索版。下面我说设备端都是指STM32F769探索版。另外还有baidu IoT hub和BOS服务器, PC上的控制端指的就是运行在PC上的MQTT.fx软件。在这个例程中,OTA云端服务器提供的文件存储和推送功能由:Baidu IoT hub, BOS存储服务器,MQTT.fx这三部分协同完成。


下面,我们来说说这个工作流程。


上电后,设备首先会初始化WIFI模块,连接热点。这里在图中没有画出。


然后设备端连接百度IoT hub,连接成功后,向其订阅设备影子主题,并开始向云端发送设备的状态信息,包括当前运行的版本号。这个时候,如果通过MQTT.fx向IoT hub发送新固件的版本号和下载地址,IoT hub收到新消息后会将其推送到设备端。收到信息后,设备端的软件会先进行一个简单的判断,收到的固件版本号是否比当前QSPI flash中存储的固件的版本更新,如果是,就开始自动在后台通过HTTP协议进行下载。


下载完成后,用户可以根据LCD上的提示启动本地的固件更新:将QSPI flash中的固件,烧写到MCU中。


更新成功后,自动运行新的程序。新程序和IoT Hub连接成功后,会将当前运行程序的版本上报给服务器。


图中从连接IoT Hub到接收云端下发新固件信息(版本和下载地址),都是通过MQTT协议完成的。从云端下载新固件是通过HTTP协议完成的。


另外,你们还可以看到PC和BOS之间还有两步操作。”上传新固件“和“获取下载地址”,这两步是需要事先完成的。事先将固件上传到百度的BOS服务器,获得下载的地址,然后才可以通过MQTT.fx进行推送。



通过WIFI连接百度天工的硬件环境搭建很简单。


STM32F769I-DISCO开发板上已经集成了ST-LINK,所以不需要额外调试工具,只需要一根micro的USB线给板子提供电源就可以开始使用了。当然,如果需要更新程序的话,还需要一台带USB接口的电脑。


所以我们需要的硬件环境包括:


一块STM32F769I-DISCO开发板,一个ESP-01wifi模块


一根micro USB线(供电)


一个WIFI热点


一台可以上网的电脑(查看消息和进行推送)



软件方面需要:


STM32F769 云端固件升级的软件包(包括一个Bootloader的程序和一个应用程序,固件包在本文最后给出下载地址)
IAR Embedded Workbench for ARM


STM32CubeProgrammer(可以用来烧录MCU片上和片外QSPI Flash)


FOTABinConverter(用来转换bin文件格式,随例程软件包一起提供)


MQTT.fx (为了演示的方便,这里使用了免费的MQTT客户端来显示从设备端接收到的数据和进行新固件信息的推送,下载链接http://mqttfx.bceapp.com/)


百度云IOT物管理服务(提供MQTT相关服务)和BOS(存放待下载的MCU固件)

在运行本例程之前,需要在云端做一些准备工作。首先,要在百度天工IoT平台上建立好对应的MQTT服务。


在本例程里需要用到物模型,所以在创建项目时,需要选择“设备型“项目。并创建对应的物模型,添加下图中列出的属性。具体的创建方法,请参考百度天工IoT平台的使用说明。



建立好云端MQTT服务后,我们得到了云端MQTT服务器的地址,MQTT连接的用户名和密码,以及主题的名称。需要在代码的baidu_iotclient_conf.h文件中修改这些内容。


我们还需要另一个可以与MCU中烧录的应用程序区别开的程序作为新版本固件,来演示远程固件更新的功能。


例程代码里已经做了一个简单的开关:只要定义宏OTATEST_VERSION,重新再编译一遍。新的程序就会增加一个点灯的操作,并且将软件版本从1.1改为2.2.1。新的程序运行时,还可以看到LED2一直在闪烁。



其次,需要预先将要下载的新固件放到百度的BOS上。前面我们说过保存在云端的固件文件并不是bin文件,而是通过FOTABinConverter工具转换过的cvt文件。FOTABinConverter工具随软件包提供,使用方法请见该例程的使用文档。


现在我们已经完成了以下工作:建立好了云端的MQTT服务,并将连接服务器的参数适配到固件代码中;生成的新版本固件,经过格式转换后上传到百度云端的BOS服务器。


接下来我们还要对板子做一个初始化的准备工作:使用STM32CubeProgrammer烧写MCU片上flash和片外的板载QSPI Flash。先全片擦除MCU片上的flash和外部QSPI Flash,再分别将用户程序和bootloader烧写到MCU flash的APP区域(0x08010000起始位置)和bootloader区域(0x08000000起始位置)。


如果需要用到“一键恢复出厂固件”功能,就还需要烧录一个默认用户程序到QSPI Flash的默认固件区域(QSPI Flash 0x00010000起始的位置,remap到STM32的地址空间是0x90010000),并将该默认用户程序的大小和校验和的值,写到QSPI FLASH INFO区(QSPI Flash 0x0起始的位置,remap到STM32的地址空间是0x90000000)。这两个值在用户恢复出厂设置,即把默认用户程序烧写回片上MCU flash时,用来检查存储在QSPI Flash上程序的完整性。


上电复位运行程序,Bootloader程序先运行,然后自动跳转到用户程序。开发板通过wifi模块连接到热点,自动获取IP地址并开始连接百度云IoT平台,连接成功后,每10秒向云端发送一次状态。包括:LED的状态,软件版本和运行状态。


保持STM32F769DK板正常运行,并已连接到百度IOT服务器。此时,如果通过MQTT.fx向百度IoT平台发布新的固件信息(包括版本信息和下载地址)到主题: $baidu/iot/shadow/{deviceName}/update。 STM32F769DK板子收到推送的新固件信息后,便会自动下载新固件。


下载完成后,提示用户当前有新版本可用。当用户按下蓝色USER按键后,程序复位。开始执行bootloader程序。


bootloader程序会将新的固件从QSPI FLASH烧写到MCU片上的FLASH中。然后跳转执行。


在烧写固件前, 程序会检查固件的完整性。如果发现固件被损坏,则不烧写。直接跳转执行老的程序,并且会在QSPI的INFO区做相应记录,重新从云端下载好的固件来覆盖已被损坏的固件。


如果在运行例程时出现连接服务器失败的情况,请检查百度云物联网平台的根证书和当前例程使用的是否一致。可以在百度云物联网平台下载根证书,然后根据新的根证书内容,替换certs.h文件中BAIDU_CA_CRT的定义。


相关代码及文档下载

例程代码及文档可在STMCU.COM.CN下载:


利用MQTT及云存储实现STM32远程无线升级(代码)


利用MQTT及云存储实现STM32远程无线升级


利用MQTT及云存储实现STM32远程无线升级例程的开发文档



        



例程的相关代码以及更加详细的操作指南和开发说明,可在www.stmcu.com.cn网站的”设计资源--本地化资源”板块下载。


在本例程中,仅考虑了固件从云端服务器下载过程的安全性(加密传输和完整性检查),关于如何验证所下载的固件来自可靠的发布源,如何保证固件在设备端存储,更新和运行时的安全,将在下一节的安全远程固件升级中进行介绍。








微信扫一扫