注册登录

如何在RTL8710 平台上实现Arduino的OTA更新?

    物联网的兴起,让嵌入式开发进入一个新的境界。然而,物联网毕竟是以太网的一部分,如何实现迭代开发,迅速发布并不断更新,增强用户体验变成了很多开发攻城狮的考验。

    通过实战演练Ameba Arduino: RTL8195/RTL8710  OTA(Over the Air)上传程序到 Ameba的过程学习,大家可以体会到如何实现OTA。

    OTA(Over-The-Air)一般而言指的是通过网络更新Firmware,Arduino IDE本身也提供OTA的功能,它的概念如下图:

图片1

    (i) Arduino IDE通过mDNS询问区域网络里提供Arduino IDE OTA服务的装置

    (ii) Ameba回应Ameba有这个服务。这意谓着Ameba正在执行含有mDNS服务的程序码,并且开启特定的TCP port等待连结。

    (iii) 使用者在Arduino IDE上开发程序,完成之后选network port

    (iv) 点选上传程序。此时Arduino IDE会通过TCP将OTA image送至Ameba端,Ameba再将这份image放至特定的位置里,并设定好开机选项,使得下次开机时使用这份image。

    整个流程里,包含了三个部份:mDNS, TCP, 与OTA image的处理。 mDNS的技术细节请参考mDNS的范例。 TCP socket programming只用于传image,并且被包含在OTA API里面,所以这里不会讨论这部份。至于如何处理OTA image,需要了解Ameba Flash memory如何编排,以及开机流程,请参考下节。

Ameba的Flash memory Layout

    Ameba RTL8195A模组的flash memory大小为2MB, 即flash的位置范围为0x00000000~00200000, 但是Ameba RTL8710模组的flash大小为1MB,所以Ameba的Flash memory layout以泛用性考虑上会以1MB大小做为考虑。

以下图为例,Ameba的程序主要占据3个区块:

     - Boot Image:即Bootloader,当Ameba开机后,会将Boot Image放置到Memory执行。它的工作主要是做一些初始化,其中一项工作就是决定当Bootloader执行完之后,要从哪里执行,它会参考位于System data的OTA Address以及Recovery Pin,如果决定是Default Image 2,则它会将这块Image放置到memory,并接着执行之。

    - Default Image 2:这部份主要是使用者开发的程序码,从0x0000B000开始,它的前16 bytes为Image档头,其中0x0000B008~0x0000B00F为Signature,这个Signature主要是用来辨识这个Image是否为正常的Image,或只是无意义的资料。 Signature有两种合法的值,用于区别这份Image是新的或旧的。

    - OTA Image:这部份也是使用者开发的程序码,预设从0x00080000开始,这个位置可以更改。 OTA Image主要用于更新程序码。它与Default Image 2的差异是摆放在Flash的位置不同,以及Signature不同。

除了程序之外,也有一些资料区块:

   - System Data:这个区块里放了系统相关的资讯,从0x00009000开始。其中与OTA相关的资料有2个:

    1. OTA address:从0x00009000开始的4个bytes,它描述了OTA Image的位置,如果OTA address里面是不合法的值(即0xFFFFFFFF),即使Flash memory里面有合法的OTA image,也会因此找不到而认为没有。

    2. Recovery Pin:从0x00009008开始的4个bytes,它的用途是,当Default Image 2与OTA Image都是正常的Image时,其中Signature会显示出其中一块Image是新的,另一块则是旧的,此时Recovery Pin会决定要执行哪一块Image。如果Recovery Pin是不合法的值(即0xFFFFFFFF),则预设会执行新的Image

System Data的资料在使用DAP通过USB上传程序码时,也会一并删除,意谓着OTA address也会被抹除,Ameba在这种情况下会认为没有OTA image。

    - Calibration Data:这个区块放置周边校正的参数,正常情况下请勿删除这里的资料。

图片2

Ameba的开机流程

Ameba的开机流程如下图,说明如下:

图片3

我们分成几种开机的情境讨论:

    (i) 未使用OTA,并使用DAP通过USB上传程序码:

这种是一般接USB线使用开发版上传程序码的情况。 Bootloader会先检查Default Image 2的signature,接着检查OTA address,但会因为OTA address并未设定,因此认为没有OTA image,因此选择Default Image 2来执行

    (ii) 已烧录OTA image,已正确设定OTA addres,但未设定recovery pin:

这种情境通常发生在Ameba已通过OTA更新程序,此时Default Image 2的signature会被设定成旧的,而OTA image的signature会被设定成新的。

Bootloader在检查完Default Image 2之后,在检查OTA 时会发现flash memory里面有合法的OTA image,接着检查recovery pin时因为没有设定recovery pin,因此会选择新的Image执行,也就是执行OTA image。

    (iii) 已烧录OTA image,已正确设定OTA address,并且设定recovery pin:

这种情况一样是Ameba已通过OTA更新程序,一样Default image 2的signature是旧的,而OTA image的signature是新的。

    Bootloader在检查完Default Image 2之后,在检查OTA 时会发现flash memory里面有合法的OTA image,接着检查recovery pin,虽然我们已设定recovery pin,但如果没有将它接到

HIGH (3.3V)的讯号,它一样会执行新的Image,也就是OTA image。但如果使用者想要回复到初始状态,它可以将recovery pin接到HIGH的讯号,这样就会执行旧的Image,也就是Default Image 2

使用范例与测试

使用OTA需要更新DAP firmware至0.7版之后(不包含0.7版),预设出厂是0.7版,请参考网址更新:https://www.amebaiot.com/change-dap-firmware/

我们打开范例 “File” -> “Examples” -> “AmebaOTA” -> “ota_basic”

范例里会使用到网络,所以我们设定欲连上的ssid与密码

图片4

接着有一些参数可以设定

  • MY_VERSION_NUMBER:在第一版里,我们需要设定OTA address以及recovery pin,因为这一次通过USB上传程序码就是第一版,所以我们不需改它

  • OTA_PORT:Arduino IDE会经由mDNS找到Ameba,并且Ameba会告诉Arduino IDE让它知道我们在TCP port 5000接收OTA image

  • RECOVERY_PIN:设定可以回复到初版的pin,这里使用pin 18

图片5

    改完之后我们要确认使用USB上传程序码,我们点选Tools -> Ports, 检查是否使用Serial Port:

图片6

    这边要注意的地方是,不管是串口或是网口 ,Arduino IDE在上传code与显示log使用的是同一个port,为了避免之后使用OTA时,无法从log uart看到log,我们改用其它Serial port terminal (Ex. Tera term or putty)来看log,(原本使用Serial Monitor)。

    然后点选上传,上传完成之后,按下Reset,可以看到底下的Log。这份log有一些可以注意的地方:

    1. 在“===== Enter Image 1====” 与“Enter Image 2 ====” 中间有个log “Flash Image 2:Addr 0xb000”, 代表这次是选择位于0xb000的Default Image 2开机

    2. “Enter Image 2 ====”之后有段log “This is version 1”, 这段log是我们在sketch里面自己加的log,可以看到这是第一版

    3. 连上AP之后,Ameba取得的IP是 “192.168.1.238”,它会启动mDNS,并且等待client连进来

图片7

接着我们将我们的程序码进版,将MY_VERSION_NUMBER改成2

图片8

    接着在“Tools”-> “Port”里面会出现“Network ports”的列表,其中一个是“MyAmeba at 192.168.1.238 (Ameba RTL8195A)” ,其中MyAmeba是我们为Ameba在启用mDNS时设定的装置名称, “192.168.1.238”是Ameba连上AP之后取得的IP,代表Arduino IDE已经从网络取得Ameba的IP,后面的Ameba RTL8195A是装置的型号。

图片9

如果你的Arduino IDE没有找到Ameba的network port,请确认

    - 你的电脑与Ameba是否在同个区域网络里?

    - 重开Arduino IDE试试看, Arduino IDE会重新找寻mDNS服务

    - 在Serial Monitor的log里Ameba是否成功连上AP并且成功启用mDNS

    接着我们点选上传程序码,这次的程序码就不会从USB上传,而是经由网络TCP传送,你会看到底下的log,会看到有client连进来,它的IP是“192.168. 1.226”,这个IP应该与你的电脑的IP是同一个。接着它会读OTA的资讯并尝试下载OTA

图片10

下载OTA成功之后,它会马上重开机,会有底下的log

- 在 “===== Enter Image 1====” 与 “Enter Image 2 ====” 中间有个log “Flash Image 2:Addr 0x80000”, 代表这次是选择位于0x80000的OTA Image开机

“Enter Image 2 ====”之后有段log “This is version 2”, 这段log是我们在sketch里面自己加的log,代表已经进到第2版

图片11

    如果发现之后下载的OTA不是你想要的,或者想要回复到初版,可以将我们在sketch里第一版设定的Recover pin(即pin 18)接到HIGH(3.3V),然后按下Reset

图片12

    此时你应该会发现它又选择位于0xb000的Default Image 2来执行。

    要特别注意一点,如果之后不将Recover Pin接到HIGH,并此时按Reset,它还是会执行OTA image,Recover pin只用于开机时决定要执行哪一块image,并非抹除特定的image。

所以整个包含OTA的开发流程大致如下:

         

图片13

程序码说明:

    1.程序一开始一样先连上WiFi

    2.接着有一段code只有在第一版才会执行

    #if MY_VERSION_NUMBER == 1

      OTA.setOtaAddress(DEFAULT_OTA_ADDRESS);

      OTA.setRecoverPin(RECOVER_PIN);

    #endif

    setOtaAddress会将OTA address写到flash memory的system data里面,目前default值是0x00080000,

    OTA.setOtaAddress(DEFAULT_OTA_ADDRESS);

    接着setRecoverPin会设定recover pin,这里的RECOVERY_PIN是自己定义的值

OTA.setRecoverPin(RECOVER_PIN);

    3.然后是每一版都需要执行的部份,是启用mDNS的服务,这里的OTA API已经将Arduino IDE使用的格式包装起来,使用者只需要设定装置的名称,以及要开放OTA所使用的TCP port即可。这个API里面的mDNS实作内容与AmebaMDNS的范例是一样的。

     OTA.beginArduinoMdnsService("MyAmeba", OTA_PORT);

    4.最后是启用OTA,程序码会在这里等待client连进挨,当OTA完成之后会重新启动Ameba。或者是当它失败时才会往下执行。

    OTA.beginLocal(OTA_PORT)

程序码说明(nonblock的范例)

    在前面的范例ota_basic里面,可以看到整份程序码其实只为了做OTA,而没有做其它事,在一般的使用情境会是,我们启用OTA,并且做其它想做的事(Ex. 点LED灯、操作Servo……),这种需求可以参考另个范例“File” -> “Examples” -> “AmebaOTA” -> “ota_non_block”

这个范例里将上述的流程包装成两个Thread,程序码的流程如下图。

    一开始的main thread会启用wifi_service_thread,它负责连上WiFi,连上AP之后启用另一个ota_thread,它负责OTA的部份。此时原本的main thread可以做自己想做的事。 :

图片14

底下描述程序码的一些设定:

    在main thread的setup()一开始我们呼叫os_thread_create API,第一个参数是Thread的function pointer,第二个参数是要带进wifi_service_thread的参数,这里我们没有要带参数,第三个参数是thread的优先权,因为这个thread我们有适当地交出执行权,所以我们将它的优先权设定成最高,第四个参数是thread使用的记忆体,如果要在这个thread里面加更多功能就要考虑将这里的记忆体加大。

    os_thread_create(wifi_service_thread, NULL, OS_PRIORITY_REALTIME, 2048);

呼叫完之后,OS会在main thread与wifi_service_thread排程并执行 Main thread接着的loop()里面只有每秒印一次log

    wifi_service_thread则是检查WiFi连线状况,如果未连线则尝试连上AP。连上AP之后,如果是第一次连上AP,则新增ota_thread。

    这里只差在第一个参数是ota_thread。 os_thread_create的回传值是thread id,我们将它记

下来避免重复新增ota_thread

    ota_thread_id = os_thread_create(ota_thread, NULL, OS_PRIORITY_REALTIME, 2048);

在这之后,OS会在main thread, wifi_service_thread, 与ota_thread排程并执行

wifi_service_thread之后就是不断检查WiFi的情况,如果断线就尝试连线

ota_thread则是执行OTA的工作,如果OTA失败就重试,如果OTA成功则会重新启动,又回到一开始的地方。


                

            


0条回复

作者
用户头像
文章 0关注 0粉丝 0
相关文章
联系客服