Your new topic does not fit any of the above??? Check first. Then post here. Thanks.

Moderator: igrr

User avatar
By Anand Lobo
#29907
eduperez wrote:Can an Arduino-based ESP device auto-update itself over-the-air?

I would like that as well. I've posted the OTA sketch below as included in the Arduino IDE example, but I've modified it so that the OTA 'listener' is a function that can be called at any point in the program.

So far, this has worked for me. I would much prefer if the ESP could update itself, though.
So we need to see how the ESP can connect to a publicly visible server and query the contents, and then I assume the 'OTA listener' can be modified to 'pull' the updated firmware and update itself (I hope).

EDIT: If you look under Examples -> ESP8266WebServer -> WebUpdate, this shows the ESP8266 hosting a small webpage where you can upload a freshly compiled firmware and it will update itself. Not exactly what we want, but it's a start. I have successfully tested it. Hopefully it works for you as well.

OTA Update sketch (slight modification of Arduino-included example)
Code: Select all#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>

/////////// STATIC IP CONFIGURATION ///////////
// Configure this for your preferences
// Comment this section out if using DHCP
IPAddress ip(192, 168, 1, 4);
IPAddress dns(8, 8, 8, 8);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
///////////////////////////////////////////////

const char* host = "esp8266-ota";
const char* ssid = "............";
const char* pass = "............";
const uint16_t aport = 8266;

WiFiServer TelnetServer(aport);
WiFiClient Telnet;
WiFiUDP OTA;

void setup() {
   Serial.begin(115200);
   Serial.print("\r\nArduino OTA Test\r\n");

   Serial.printf("Sketch size: %u\r\n", ESP.getSketchSize());
   Serial.printf("Free size: %u\r\n", ESP.getFreeSketchSpace());

   WiFi.config(ip, dns, gateway, subnet); // Comment out this line if using DHCP

   WiFi.begin(ssid, pass);
   if(WiFi.waitForConnectResult() == WL_CONNECTED){
      MDNS.begin(host);
      MDNS.addService("arduino", "tcp", aport);
      OTA.begin(aport);
      TelnetServer.begin();
      TelnetServer.setNoDelay(true);
      Serial.print("IP address: ");
      Serial.println(WiFi.localIP());
   }
}

void loop() {
   CheckOTAUpdate();
}

void CheckOTAUpdate(){
   if (OTA.parsePacket()) {
      IPAddress remote = OTA.remoteIP();
      int cmd  = OTA.parseInt();
      int port = OTA.parseInt();
      int size   = OTA.parseInt();

      Serial.print("\r\nUpdate Start: ip:");
      Serial.print(remote);
      Serial.printf(", port:%d, size:%d bytes\r\n", port, size);
      uint32_t startTime = millis();

      WiFiUDP::stopAll();

      if(!Update.begin(size)){
         Serial.print("Update Begin Error\r\n");
         return;
      }

      WiFiClient client;
      if (client.connect(remote, port)) {

      uint32_t written;
         while(!Update.isFinished()){
            written = Update.write(client);
            if(written > 0) client.print(written, DEC);
         }
         Serial.setDebugOutput(false);

         if(Update.end()){
            client.print("OK\r\n");
            Serial.printf("Update Success: %u ms\r\nRebooting...\r\n", millis() - startTime);
            ESP.restart();
         } else {
            Update.printError(client);
            Update.printError(Serial);
         }
      } else {
         Serial.printf("Connect Failed: %u ms\r\n", millis() - startTime);
      }
   }
   if (TelnetServer.hasClient()){
      if (!Telnet || !Telnet.connected()){
         if(Telnet) Telnet.stop();
         Telnet = TelnetServer.available();
      } else {
         WiFiClient toKill = TelnetServer.available();
         toKill.stop();
      }
   }
   if (Telnet && Telnet.connected() && Telnet.available()){
      while(Telnet.available())
         Serial.write(Telnet.read());
   }
   if(Serial.available()){
      size_t len = Serial.available();
      uint8_t * sbuf = (uint8_t *)malloc(len);
      Serial.readBytes(sbuf, len);
      if (Telnet && Telnet.connected()){
         Telnet.write((uint8_t *)sbuf, len);
         yield();
      }
      free(sbuf);
   }
   delay(1);
}
Last edited by Anand Lobo on Thu Oct 01, 2015 4:28 am, edited 1 time in total.
User avatar
By Samighi11
#29918
EDIT: If you look under Examples -> ESP8266WebServer -> WebUpdate, this shows the ESP8266 hosting a small webpage where you can upload a freshly compiled firmware and it will update itself. Not exactly what we want, but it's a start. I have successfully tested it. Hopefully it works for you as well.


I tried this App without much success. it works as in receive the file, but it blows up for me.

However, even with that, I can demonstrate the process to self update. This version .1 so we can continue to work on it.

if the ESP can call a local (not internet) server, with http://somemachine/index.html, in turn the URL can ask a PHP file to perform the upload. You could pass your current version (eventually) to index.html, it would send it to the PHP file and if the PHP file has a new version of the .bin file, it will then call CURL to upload the sketch.

Several things that need to be done for this to work.
a) the sketch has to be made so it works (for all of us)
b) how to compile a sketch with OTA built in (I assume OTA has to be built in to work)
c) some kind of date or versioning so you know the version you have is newer

if you can provide all of these, then it could be self updating.

Index.html (for a lack of a better name)
Code: Select all<html>
   <head>
      <title>The jQuery Example</title>
      <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
      
      <script type="text/javascript" language="javascript">
         $(document).ready(function() {
               $.get(
                  "./selfupdate.php",
                 
                  function(data) {
                     $('#stage').html(data);
                  }
               );
           
         });
      </script>
 
   </head>
   Upload will start in 5 seconds


selfupdate.php (sleeps for 5 seconds, then then curls the file)

Code: Select all<?php
    sleep(5);
   $target_url = 'http://esp8266-webupdate.local/update';
        //This needs to be the full path to the file you want to send.
   $file_name_with_full_path = realpath('./WebUpdate.cpp.bin');
        /* curl will accept an array here too.
         * Many examples I found showed a url-encoded string instead.
         * Take note that the 'key' in the array will be the key that shows up in the
         * $_FILES array of the accept script. and the at sign '@' is required before the
         * file name.
         */
   $post = array('image'=>'@'.$file_name_with_full_path);
 
        $ch = curl_init();
   curl_setopt($ch, CURLOPT_URL,$target_url);
   curl_setopt($ch, CURLOPT_POST,1);
   curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
   $result=curl_exec ($ch);
   curl_close ($ch);
   echo $result;
echo "Done";
?>


Here is my log file. After 5 seconds, the new file is received. As you can see it is from my Mac Web server. then it crashes I guess. You can simulate this using the "curl -F " example in the sketch itself. (curl -F "image=@Webupdate.cpp.bin" esp8266-webupdate.local/update ). The manual process works, so my CURL PHP might be a bit off.

Code: Select allReady! Open http://esp8266-webupdate.local in your browser
Update: /Library/WebServer/Documents/ajax/WebUpdate.cpp.bin
lmac.c 662

 ets Jan  8 2013,rst cause:4, boot mode:(3,6)

wdt reset
load 0x4010f000, len 1264, room 16
tail 0
chksum 0x42
csum 0x42
~ld

Booting Sketch...
Ready! Open http://esp8266-webupdate.local in your browser
User avatar
By Anand Lobo
#29961 Damn, that looks nice.
Wish I had time to learn PHP and stuff, that would be really useful. I just about manage with the Arduino C code and that's a struggle.

So, for the more accomplished coders out there, I'm assuming the basic steps needed for auto-OTA updates:
    - ping server on local network (and later, internet) where update is stored
    - check version number (could be stored as a static value in the program, or EEPROM, and read in from the filename on the server to be compared to the stored value?
    - if file number is newer, begin update process using new firmware
User-adjustable variables would be the server IP, file name/path, stuff like that?
What would be cool, is if this functionality could theoretically be encapsulated in a single header file/library, and using something like the SimpleTimer library it could be set to check for updates on a schedule. I look forward to how this works out.