10. ESP8266通過OTA更新固件的實踐

1. OTA是什麼
OTA(Over The Air),通常也稱爲FOTA(Firmware Over The Air),意思是硬件中的固件通過無線升級版本。衆所周知,硬件固件的部署,以及固件版本升級頗爲繁瑣,很多時候得通過人工完成,成本很高。所以OTA是一個頗爲實用、也是很實惠的功能。
手環等設備,其實也有這類問題,一般都是通過APP升級-> ->藍牙固件升級的方式。

2. 樂鑫對OTA的支持
ESP8266的廠商樂鑫提供了FOTA的功能,AT固件和NONSDK的都支持。
根據官方資料,樂鑫的FOTA是通過HTTP協議實現,且支持自建FOTA下載服務(WEB服務器)。
但樂鑫對常用功能和傳感器的支持力度較少,需要不停造輪子、找輪子,效率相對較低。
在樂鑫以外,esp-open-rtos等也可以支持OTA功能,該有的功能都已經具備。
加之對mqtt、傳感器的驅動支持更好,因此esp-open-rtos是個不錯的選擇,
本文基於esp-open-rtos的OTA實現了在線升級的簡單例子,並對原理進行說明。
關於esp-open-rtos詳細,可參照前文http://blog.csdn.net/ydogg/article/details/75194902

3. Esp-open-rtos的OTA簡介
Esp-open-rtos的OTA實現也很簡潔:
通過tftp協議在線下載固件,利用rboot進行新舊固件的切換和硬件重啓。

其中固件下載支持client和server兩種模式,可根據具體情況選擇一種,也可同時使用。
Client模式下,是從tftp服務器中下載固件;
Server模式下,是esp8266作爲tftp服務,接收tftp客戶端發送的新固件。

總的來說,Client模式更加靈活些,可以通過各種事件進行觸發(如mqtt通知),且硬件側的負載更輕。
本文基於Client模式進行說明。

4. OTA環境準備
主要是tftp的安裝,下面命令以CentOS爲例:
yum install tftp-server tftp

默認情況下tftp是託管給xinit,其配置文件位於/etc/xinit.d/tftp,內容如下:

service tftp
{
    socket_type     = dgram
    protocol        = udp
    wait            = yes
    user            = root
    server          = /usr/sbin/in.tftpd
    server_args     = -s /var/lib/tftpboot -c
    disable         = yes
    per_source      = 11
    cps         = 100 2
    flags           = IPv4
}

server_args中 –s指明瞭tftp服務所使用的磁盤路徑;如果需要上傳,需要增加-c選項。
在OTA只需要下載,因此可以不開啓-c。(直接複製新固件到/var/lib/tftpboot下即可)

測試:
# cp mybin/xxx.bin /var/lib/tftpboot
# tftp -m binary <yourip> -c get xxx.bin
如果能成功取得xxx.bin,說明tftp服務正常。
另外, tftp的端口爲69,如有防火牆,注意相關設置需要放開。

5. OTA代碼準備
在esp-open-rtos/examples/ota_basic下,有OTA的示例代碼,
但因爲有固件驗證等功能,略複雜,理解方便起見,精簡爲如下代碼:

#include <string.h>
#include "espressif/esp_common.h"
#include "esp/uart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "esp8266.h"
#include "ssid_config.h"

#include "ota-tftp.h"
#include "rboot-api.h"

/* TFTP client will request this image filenames from this server */
#define TFTP_IMAGE_SERVER "192.168.31.192"
#define TFTP_IMAGE_FILENAME1 "fm1.bin"
#define TFTP_IMAGE_FILENAME2 "fm2.bin"

void download_and_restart(char* filename)
{
    if( !filename ) {
        return;
    }

    // 獲取當前固件的slot序號
    rboot_config conf = rboot_get_config();
    int slot = (conf.current_rom + 1) % conf.count;

    // 只有1個slot時不能進行OTA
    // 2個的情況下(一般有2個),新固件會被放到非當前(空閒)的那個slot下
    if(slot == conf.current_rom) {
        printf("FATAL ERROR: Only one OTA slot is configured!¥n");
        return;
    }


    // 下載指定的固件文件到非當前(空閒)slot
    printf("Downloading %s to slot %d...¥n", filename, slot);
    int res = ota_tftp_download(TFTP_IMAGE_SERVER, TFTP_PORT, filename, 1000, slot, NULL);
    printf("ota_tftp_download %s result %d¥n", filename, res);

    if( res != 0 ) {
        return;
    }

    // vertify固件,檢查基本參數,不做也可以
    uint32_t length = 0;
    bool valid = rboot_verify_image(conf.roms[slot], &length, NULL);
    if( !valid ) {
        printf("Not valid after all :(¥n");
        return;
    }

    printf("Rebooting into slot %d...¥n", slot);
    // 設置新的當前slot
    rboot_set_current_rom(slot);
    // 重啓
    sdk_system_restart();
}

void update_fm_task(void *pvParameters)
{
    printf("update task starting...¥n");
    while(1) {
        // 模擬觸發,task在delay 20秒後開始更新固件(默認網絡正常)
        // 根據業務,可以通過mqtt通知或者http調用等方式進行觸發
        vTaskDelay(20000 / portTICK_PERIOD_MS);
        download_and_restart(TFTP_IMAGE_FILENAME2);
    }
}

void user_init(void)
{
    // 設置波特率爲
    uart_set_baud(0, 115200);

    // 打印當前固件的slot號
    rboot_config conf = rboot_get_config();
    printf("¥r¥n¥r¥nOTA Basic demo.¥r¥nCurrently running on flash slot %d / %d.¥r¥n¥r¥n",
           conf.current_rom, conf.count);

    // 打印當前固件的地址信息
    printf("Image addresses in flash:¥r¥n");
    for(int i = 0; i <conf.count; i++) {
        printf("%c%d: offset 0x%08x¥r¥n", i == conf.current_rom ? '*':' ', i, conf.roms[i]);
    }

    // 當前fm信息(現在是fm1的代碼)
    printf("fm1 is runnning!¥n");

    // wifi設定,
    struct sdk_station_config config = {
        .ssid = "your_wifi_ssid",
        .password = "your_pass",
    };
    sdk_wifi_set_opmode(STATION_MODE);
    sdk_wifi_station_set_config(&config);

    // 啓動下載task
    xTaskCreate(&update_fm_task, "update_fm _task", 2048, NULL, 2, NULL);
}

Makefile內容如下:

PROGRAM=otatest
EXTRA_COMPONENTS=extras/rboot-ota
include ../../common.mk

執行:
make
cp ./firmware/otatest.bin /var/lib/tftpboot/fm1.bin

另外需要做成固件2,變更上面的第37行和第84行:
download_and_restart(TFTP_IMAGE_FILENAME2);
printf("fm1 is runnning!¥n");
->
download_and_restart(TFTP_IMAGE_FILENAME1);
printf("fm2 is runnning!¥n");

執行:
make
cp ./firmware/otatest.bin /var/lib/tftpboot/fm2.bin

6 固件下載
將fm1.bin下載到esp8266(具體硬件是nodemcu dev1.0),
注意nodemcu的SPI Mode是DIO。

寫入成功後,首先是fm1運行,20s後,如果網絡正常,會被更新爲fm2。
Fm2運行後20s再次更新成fm1,如此循環往復。fm1->fm2->fm1…
如果首先下載的是fm2,則上述過程則會反過來。fm2->fm1->fm2…

寫入參數如下圖:
這裏寫圖片描述

通過串口,可看到如下日誌如下

SP-Open-SDK ver: 0.0.1 compiled @ Jul 26 2017 12:56:25
phy ver: 273, pp ver: 8.3

OTA Basic demo.
Currently running on flash slot 0 / 2. >>>>>>>2slot,當前是0(寫入地址是0x2000

Image addresses in flash:
*0: offset 0x00002000
 1: offset 0x00202000
fm1 is runnning!                      >>>>>>>第一次是fm1運行   
… 
mode : sta(5c:cf:7f:a3:13:83)
add if0
TFTP client task starting...         >>>>>>>下載線程   
… 

connected with iotwifi, channel 7
dhcp client start...
ip:192.168.31.170,mask:255.255.255.0,gw:192.168.31.1 >>>下載線程
Downloading fm2.bin to slot 1...       >>>>>>>fm2被下載到slot1        
ota_tftp_download fm2.bin result 0

Rebooting into slot 1... >>>>>>>從slot1重啓,切換爲fm2
… 

rBoot v1.4.0 - [email protected]
Flash Size:   32 Mbit
Flash Mode:   DIO
Flash Speed:  40 MHz
rBoot Option: Big flash
rBoot Option: RTC data

Booting rom 1.
system param error 

ESP-Open-SDK ver: 0.0.1 compiled @ Jul 26 2017 12:56:25
phy ver: 273, pp ver: 8.3

OTA Basic demo.
Currently running on flash slot 1 / 2. >>>>>>>2slot,當前是1  

Image addresses in flash:
 0: offset 0x00002000
*1: offset 0x00202000
fm2 is runnning!                     >>>>>>>fm2運行!固件更新成功。
mode : sta(5c:cf:7f:a3:13:83)
add if0
TFTP client task starting...
scandone
add 0
aid 2
cnt 

connected with iotwifi, channel 7
dhcp client start...
ip:192.168.31.170,mask:255.255.255.0,gw:192.168.31.1
Downloading fm1.bin to slot 0...       >>>>>>>fm1被下載到slot0
ota_tftp_download fm1.bin result 0
----
Rebooting into slot 0...               >>>>>>>從slot0重啓,重新切換爲fm1

7. 其他問題
目前切換slot時,啓動會提示system param error,並dump信息。
經過確認esp-open-rtos的代碼後,原因是啓動時checksum,導致打印警告信息,
具體原因還沒有查清,但固件本身的功能沒有受到影響。

如要使用Server模式更新固件,在wifi連接成功並取得IP後,調用如下代碼即可:
ota_tftp_init_server(YOUR_TFTP_PORT);

參考url:
樂鑫:http://espressif.com/zh-hans/support/download/documents
esp-open-rtos:https://github.com/SuperHouse/esp-open-rtos/wiki/OTA-Update-Configuration

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章