透過網路 OTA 上傳程式碼到 Ameba
材料準備
- Ameba x 1
OTA
(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。
Ameba的Flash memory layout
以下圖為例,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:這個區塊放置週邊校正的參數,正常情況下請勿刪除這裡的資料。
Ameba的開機流程
我們分成幾種開機的情境討論:
(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
使用範例與測試
範例裡會使用到網路,所以我們設定欲連上的ssid與密碼
接著有一些參數可以設定
- 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
改完之後我們要確認使用USB上傳程式碼,我們點選Tools -> Ports, 檢查是否使用Serial Port:
這邊要注意的地方是,不管是Serial port或是network port ,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連進來
接著我們將我們的程式碼進版,將MY_VERSION_NUMBER改成2
接著在 “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是裝置的型號。
如果你的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
下載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版
如果發現之後下載的OTA不是你想要的,或者想要回復到初版,可以將我們在sketch裡第一版設定的Recover pin(即pin 18)接到HIGH(3.3V),然後按下Reset
此時你應該會發現它又選擇位於0xb000的Default Image 2來執行。
要特別注意一點,如果之後不將Recover Pin接到HIGH,並此時按Reset,它還是會執行OTA image,Recover pin只用於開機時決定要執行哪一塊image,並非抹除特定的image。
所以整個包含OTA的開發流程大致如下:
程式碼說明
接著有一段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);
然後是每一版都需要執行的部份,是啟用mDNS的服務,這裡的OTA API已經將Arduino IDE使用的格式包裝起來,使用者只需要設定裝置的名稱,以及要開放OTA所使用的TCP port即可。這個API裡面的mDNS實作內容與AmebaMDNS的範例是一樣的。
OTA.beginArduinoMdnsService("MyAmeba", OTA_PORT);
最後是啟用OTA,程式碼會在這裡等待client連進捱,當OTA完成之後會重新啟動Ameba。或者是當它失敗時才會往下執行。
OTA.beginLocal(OTA_PORT)
程式碼說明(non block的範例)
這個範例裡將上述的流程包裝成兩個Thread,程式碼的流程如下圖。
一開始的main thread會啟用wifi_service_thread,它負責連上WiFi,連上AP之後啟用另一個ota_thread,它負責OTA的部份。此時原本的main thread可以做自己想做的事。:
底下描述程式碼的一些設定:
在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成功則會重新啟動,又回到一開始的地方。