Monday, May 29, 2017

Demo 16: How to update firmware OTA for a batch of Arduino ESP32

1. Introduction
- IoT (Internet of Things) means many devices connect to the Internet. If there is bug in the software of devices, the process to fix bug and re-flash the software will cost much effort, time and money. That is why people use FOTA (firmware update over the air) for IoT devices. You do not need to unplug the device from the network and use hardware flashing tool to re-flash software for it. Everything happens over the air, just create the wireless connection to the device and transfer the software to it chunk by chunk. The device will receive the chunk and write it to a specific address in Flash. After finishing flashing, the device will reboot and run the new software. It is similar to update software for your mobile over the WiFi/3G.
- We will create a demo for FOTA, first we flash the firmware with FOTA functionality and LED blinky on pin GPIO12 by normal way (USB-TTL port). Then we update the firmware with FOTA functionality but LED blinky on pin GPIO14 by FOTA way.
2. Hardware
We connect 2 LEDs on GPIO12 and GPIO14. After first flashing, LED on GPIO12 will blink. But after second flashing, LED on GPIO14 will blink.
3. Software
- Arduino ESP32 core supplied ArduinoOTA library for us to use FOTA functionality. So we just include "ArduinoOTA.h". I will explain the code by the comments in code, so you just read it.
Note: FOTA functionality will use use 2 TCP/Ip connections. First connection is to communicate with ESP32 to control flashing work flow. Second connection is to transfer the firmware chunk by chunk.
- Create an Arduino project with code below, save as esp32otafw1 and flash it to ESP32 by normal way:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include <WiFi.h>
#include <ESPmDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>

/* fill your ssid and password here */
const char* ssid = "dd-wrt";
const char* password = "0000000000";

long lastMsg = 0;
int flag = false;

/* LED is pin GIO12 */
int led = 12;

void setup() {
  
  Serial.begin(115200);
  /* connect to wifi */
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  /* Wait for connection */
  while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
  }
  /* set LED as output */
  pinMode(led, OUTPUT);
  
  /* create a connection at port 3232 */
  ArduinoOTA.setPort(3232);
  /* we use mDNS instead of IP of ESP32 directly */
  ArduinoOTA.setHostname("esp32");

  /* we set password for updating */
  ArduinoOTA.setPassword("iotsharing");

  /* this callback function will be invoked when updating start */
  ArduinoOTA.onStart([]() {
    Serial.println("Start updating");
  });
  /* this callback function will be invoked when updating end */
  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd updating");
  });
  /* this callback function will be invoked when a number of chunks of software was flashed
  so we can use it to calculate the progress of flashing */
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });

  /* this callback function will be invoked when updating error */
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
    else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
    else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
    else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
    else if (error == OTA_END_ERROR) Serial.println("End Failed");
  });
  /* start updating */
  ArduinoOTA.begin();
  Serial.print("ESP IP address: ");
  Serial.println(WiFi.localIP());
}

void loop() {
  /* this function will handle incomming chunk of SW, flash and respond sender */
  ArduinoOTA.handle();

  /* we can not use delay() here, because it will block the ArduinoOTA work
  so we count the time, if it reach we toggle the lED */
  long now = millis();
  if (now - lastMsg > 3000) {
    lastMsg = now;
    if(flag == false){
      digitalWrite(led, HIGH); 
      flag = true;
    }else{
      digitalWrite(led, LOW); 
      flag = false;      
    }
  }
}
After flashing, you will see the LED on GPIO12 blink
- Create an Arduino project, save as esp32otafw2 with code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include <WiFi.h>
#include <ESPmDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>

/* fill your ssid and password here */
const char* ssid = "dd-wrt";
const char* password = "0000000000";

long lastMsg = 0;
int flag = false;

/* LED is pin GIO12 */
int led = 14;

void setup() {
  
  Serial.begin(115200);
  /* connect to wifi */
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  /* Wait for connection */
  while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
  }
  /* set LED as output */
  pinMode(led, OUTPUT);
  
  /* create a connection at port 3232 */
  ArduinoOTA.setPort(3232);
  /* we use mDNS instead of IP of ESP32 directly */
  ArduinoOTA.setHostname("esp32");

  /* we set password for updating */
  ArduinoOTA.setPassword("iotsharing");

  /* this callback function will be invoked when updating start */
  ArduinoOTA.onStart([]() {
    Serial.println("Start updating");
  });
  /* this callback function will be invoked when updating end */
  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd updating");
  });
  /* this callback function will be invoked when a number of chunks of software was flashed
  so we can use it to calculate the progress of flashing */
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });

  /* this callback function will be invoked when updating error */
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
    else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
    else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
    else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
    else if (error == OTA_END_ERROR) Serial.println("End Failed");
  });
  /* start updating */
  ArduinoOTA.begin();
  Serial.print("ESP IP address: ");
  Serial.println(WiFi.localIP());
}

void loop() {
  /* this function will handle incomming chunk of SW, flash and respond sender */
  ArduinoOTA.handle();

  /* we can not use delay() here, because it will block the ArduinoOTA work
  so we count the time, if it reach we toggle the lED */
  long now = millis();
  if (now - lastMsg > 3000) {
    lastMsg = now;
    if(flag == false){
      digitalWrite(led, HIGH); 
      flag = true;
    }else{
      digitalWrite(led, LOW); 
      flag = false;      
    }
  }
}
- In order to transfer the new firmware to ESP32 OTA we will use a python tool called "espota.py" in  
"where_you_install_Arduino/Arduino/hardware/espressif/esp32/tools"
This tool has arguments:
"python espota.py -i ESP_IP_address -I Host_IP_address -p ESP_port -P Host_port [-a password] -f sketch.bin"
Note: in order to get "sketch.bin", from Arduino menu bar, choose  Sketch - Export Compiled Binary
and then
from Arduino menu bar, choose Sketch - Show Sketch Folder - you will see a ".bin" file there. So get the absolute path to this ".bin" file for later usage.
- In order to update for multiple ESP32 clients I created a small Python script "espotabatch.py", you just change a little bit to adapt your need. This script will be put in the same folder with "espota.py"
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import subprocess
import time

#this list contains array of esp32 clients, 
# and each client contains mDNS name and the path to .bin file
#I only have 1 ESP so I duplicate mDNS entry for testing
esps = [
  #mDNS name of ESP   #absolute path to ".bin" file
    ['esp32.local', 'fw1.bin'],
    ['esp32.local', 'fw2.bin']
]
ip_of_sender = '192.168.1.103'
esp_respond_sender_port = '3232'
sender_to_esp_port = '3232'
update_password = 'iotsharing'

for esp in esps:
    cmd = 'python espota.py -i '+esp[0]+' -I '+ip_of_sender+ ' -p '+sender_to_esp_port+' -P '+esp_respond_sender_port+' -a '+update_password+' -f '+esp[1]
    print (cmd)
    p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    for line in p.stdout.readlines():
        print line,
    retval = p.wait()
    time.sleep(10)
- Now open Terminal in the path:
"where_you_install_Arduino/Arduino/hardware/espressif/esp32/tools"
and type: "python espotabatch.py"
4. Result
If you see ERROR here, do not worry, no problem.
Figure: FOTA progress

4 comments:

Jagadeesh N said...

hi,
I am facing the following error,could you please help me to solve.

user@user-bltsp00058:~$ cd Arduino/hardware/espressif/esp32/tools
user@user-bltsp00058:~/Arduino/hardware/espressif/esp32/tools$ python espotabatch.py
python espota.py -i esp32.local -I 192.168.43.29 -p 3232 -P 3232 -a iotsharing -f ota2.ino.esp32thing.bin
09:41:45 [ERROR]: Listen Failed
python espota.py -i esp32.local -I 192.168.43.29 -p 3232 -P 3232 -a iotsharing -f ota2.ino.esp32thing.bin
09:41:55 [ERROR]: Listen Failed
user@user-bltsp00058:~/Arduino/hardware/espressif/esp32/tools$ python espotabatch.py
python espota.py -i esp32.local -I 192.168.43.29 -p 3232 -P 3232 -a iotsharing -f ota2.ino.esp32thing.bin
09:52:22 [ERROR]: Listen Failed

Regards,
Jagadeesh

iotsharing dotcom said...

it seems the connection was not established. please chrck the tutorial how to debug to see if connection is established

Ho Le said...

Hi,
I have a problem when ending the process, but could not know why.

Terminal:

huyhl-fw@huyhlfw-desktop:/opt/arduino-1.8.3/hardware/espressif/esp32/tools$ python espotabatch.py
python espota.py -i esp32.local -I 192.168.1.4 -p 3232 -P 3232 -a iotsharing -f esp32_otafw1.ino.esp32.bin
Sending invitation to esp32.local
Authenticating...OK
Uploading.............................................................................................................................................................................................................................................................................................................................................

23:20:02 [ERROR]: 1460


Arduino IDE serial:

ets Jun 8 2016 00:22:57

rst:0x1 (POWERON_RESET),boot:0x13 (Sets Jun 8 2016 00:22:57

rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0010,len:4
load:0x3fff0014,len:588
load:0x40078000,len:0
load:0x40078000,len:10472
entry 0x40078a28
..ESP IP address: 192.168.1.17
Start updating sketch
Progress: 0%
Progress: 0%
Progress: 0%
Progress: 0%
Progress: 1%
Progress: 1%
Progress: 1%
Progress: 2%
Progress: 2%
Progress: 2%
Progress: 3%
Progress: 3%
Progress: 3%
Progress: 3%
Progress: 4%
Progress: 4%
Progress: 4%
Progress: 5%
Progress: 5%
Progress: 5%
Progress: 6%
Progress: 6%
Progress: 6%
Progress: 6%
...
Progress: 98%
Progress: 98%
Progress: 98%
Progress: 98%
Progress: 99%
[E][WiFiClient.cpp:223] write(): 104
[E][Updater.cpp:245] end(): premature end: res:0, pos:479232/485936

Error[4]: End Failed

Please help!
Regards,
Huy.

iotsharing dotcom said...

Hi

1. Try separate power for ESP32 instead of using usb power.
2. Try un-plug all modules that connected to ESP32.

Reards,