OTA with ESP32 as AP

rsappia
Posts: 3
Joined: Wed Jul 31, 2019 8:32 pm

OTA with ESP32 as AP

Postby rsappia » Wed Jul 31, 2019 8:48 pm

Hi there, I am having the following use case scenario:

The ESP32 is placed within a case that cannot be easily opened. Therefore, in case I need to update its firmware, OTA is a must. The second point, the ESP32 will be working without a Wifi network in reach. I thought it would be possible to setup the OTA code to set the ESP32 as AP so it can provide its own wifi net and I could just upload my bin file without needing nothing else than a laptop and the ESP32..
I managed to configure the ESP32 as AP, I can access its webpage but when I try to upload the file, I got always the message that the page hasnt been found ("serverIndex/Update Not found").

I have used the code shown here: https://randomnerdtutorials.com/esp32- ... ogramming/
and replaced the first part where the wifi is started to be set as AP.

  1.  /* OTAWebUpdater.ino Example from ArduinoOTA Library
  2.  * Rui Santos
  3.  * Complete Project Details https://randomnerdtutorials.com
  4.  */
  5.  
  6. #include <WiFi.h>
  7. #include <WiFiClient.h>
  8. #include <WebServer.h>
  9. #include <ESPmDNS.h>
  10. #include <Update.h>
  11.  
  12. const char* host = "esp32";
  13. const char* ssid = "REPLACE_WITH_YOUR_SSID";
  14. const char* password = "REPLACE_WITH_YOUR_PASSWORD";
  15.  
  16. WebServer server(80);
  17.  
  18. /*
  19.  * Login page
  20.  */
  21. const char* loginIndex =
  22.  "<form name='loginForm'>"
  23.     "<table width='20%' bgcolor='A09F9F' align='center'>"
  24.         "<tr>"
  25.             "<td colspan=2>"
  26.                 "<center><font size=4><b>ESP32 Login Page</b></font></center>"
  27.                 "<br>"
  28.             "</td>"
  29.             "<br>"
  30.             "<br>"
  31.         "</tr>"
  32.         "<td>Username:</td>"
  33.         "<td><input type='text' size=25 name='userid'><br></td>"
  34.         "</tr>"
  35.         "<br>"
  36.         "<br>"
  37.         "<tr>"
  38.             "<td>Password:</td>"
  39.             "<td><input type='Password' size=25 name='pwd'><br></td>"
  40.             "<br>"
  41.             "<br>"
  42.         "</tr>"
  43.         "<tr>"
  44.             "<td><input type='submit' onclick='check(this.form)' value='Login'></td>"
  45.         "</tr>"
  46.     "</table>"
  47. "</form>"
  48. "<script>"
  49.     "function check(form)"
  50.     "{"
  51.     "if(form.userid.value=='admin' && form.pwd.value=='admin')"
  52.     "{"
  53.     "window.open('/serverIndex')"
  54.     "}"
  55.     "else"
  56.     "{"
  57.     " alert('Error Password or Username')/*displays error message*/"
  58.     "}"
  59.     "}"
  60. "</script>";
  61.  
  62. /*
  63.  * Server Index Page
  64.  */
  65.  
  66. const char* serverIndex =
  67. "<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>"
  68. "<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>"
  69.    "<input type='file' name='update'>"
  70.         "<input type='submit' value='Update'>"
  71.     "</form>"
  72.  "<div id='prg'>progress: 0%</div>"
  73.  "<script>"
  74.   "$('form').submit(function(e){"
  75.   "e.preventDefault();"
  76.   "var form = $('#upload_form')[0];"
  77.   "var data = new FormData(form);"
  78.   " $.ajax({"
  79.   "url: '/update',"
  80.   "type: 'POST',"
  81.   "data: data,"
  82.   "contentType: false,"
  83.   "processData:false,"
  84.   "xhr: function() {"
  85.   "var xhr = new window.XMLHttpRequest();"
  86.   "xhr.upload.addEventListener('progress', function(evt) {"
  87.   "if (evt.lengthComputable) {"
  88.   "var per = evt.loaded / evt.total;"
  89.   "$('#prg').html('progress: ' + Math.round(per*100) + '%');"
  90.   "}"
  91.   "}, false);"
  92.   "return xhr;"
  93.   "},"
  94.   "success:function(d, s) {"
  95.   "console.log('success!')"
  96.  "},"
  97.  "error: function (a, b, c) {"
  98.  "}"
  99.  "});"
  100.  "});"
  101.  "</script>";
  102.  
  103. /*
  104.  * setup function
  105.  */
  106. void setup(void) {
  107.   Serial.begin(115200);
  108.  
  109.   // Connect to WiFi network
  110.   //WiFi.begin(ssid, password);
  111.  WiFi.softAP(ssid, password);
  112.   Serial.println("");
  113.  
  114.   IPAddress IP = WiFi.softAPIP();
  115.   Serial.print("AP IP address: ");
  116.   Serial.println(IP);
  117.  
  118.   /*use mdns for host name resolution*/
  119.   if (!MDNS.begin(host)) { //http://esp32.local
  120.     Serial.println("Error setting up MDNS responder!");
  121.     while (1) {
  122.       delay(1000);
  123.     }
  124.   }
  125.   Serial.println("mDNS responder started");
  126.   /*return index page which is stored in serverIndex */
  127.   server.on("/", HTTP_GET, []() {
  128.     server.sendHeader("Connection", "close");
  129.     server.send(200, "text/html", loginIndex);
  130.   });
  131.   server.on("/serverIndex", HTTP_GET, []() {
  132.     server.sendHeader("Connection", "close");
  133.     server.send(200, "text/html", serverIndex);
  134.   });
  135.   /*handling uploading firmware file */
  136.   server.on("/update", HTTP_POST, []() {
  137.     server.sendHeader("Connection", "close");
  138.     server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
  139.     ESP.restart();
  140.   }, []() {
  141.     HTTPUpload& upload = server.upload();
  142.     if (upload.status == UPLOAD_FILE_START) {
  143.       Serial.printf("Update: %s\n", upload.filename.c_str());
  144.       if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { //start with max available size
  145.         Update.printError(Serial);
  146.       }
  147.     } else if (upload.status == UPLOAD_FILE_WRITE) {
  148.       /* flashing firmware to ESP*/
  149.       if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
  150.         Update.printError(Serial);
  151.       }
  152.     } else if (upload.status == UPLOAD_FILE_END) {
  153.       if (Update.end(true)) { //true to set the size to the current progress
  154.         Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
  155.       } else {
  156.         Update.printError(Serial);
  157.       }
  158.     }
  159.   });
  160.   server.begin();
  161. }
  162.  
  163. void loop(void) {
  164.   server.handleClient();
  165.   delay(1);
  166. }

Is there any hint you could provide me? may be a working example?

Thanks in advance!

ESP_Sprite
Posts: 2832
Joined: Thu Nov 26, 2015 4:08 am

Re: OTA with ESP32 as AP

Postby ESP_Sprite » Thu Aug 01, 2019 2:53 am

Well, if anything, you seem to be checking on "/update", with a lower-case u.

rsappia
Posts: 3
Joined: Wed Jul 31, 2019 8:32 pm

Re: OTA with ESP32 as AP

Postby rsappia » Thu Aug 01, 2019 3:59 am

ESP_Sprite wrote:
Thu Aug 01, 2019 2:53 am
Well, if anything, you seem to be checking on "/update", with a lower-case u.
Hi, thanks for answering. If you mean this from my comment, it was just illustrative, I didn't copy paste the error msg (:

boarchuz
Posts: 127
Joined: Tue Aug 21, 2018 5:28 am

Re: OTA with ESP32 as AP

Postby boarchuz » Thu Aug 01, 2019 2:29 pm

Do you mean that it's trying to upload to "http://x.x.x.x/serverIndex/update" (rather than "http://x.x.x.x/update")?
You'll have to figure out how to stop it going to the relative uri instead. Try removing the trailing / from your url (ie. go to "http://x.x.x.x/serverIndex" rather than "http://x.x.x.x/serverIndex/") or remove it from the ajax url (ie. "/update" -> "update").

machiel
Posts: 2
Joined: Wed Oct 09, 2019 1:29 pm

Re: OTA with ESP32 as AP

Postby machiel » Wed Oct 09, 2019 1:55 pm

I struggled with the same issue, need for an OTA firmware update functionality without registration to an existing WiFi network. And therefore, make use of the Access point configuration in the ESP.

Downsize of this setup is the lack of internet access. It’s not possible to make use of external web sources. In this case, the HTML has a reference to an external jquery (https://ajax.googleapis.com/ajax/libs/j ... ery.min.js) internet source that is not available with an Access Point configuration.
Therefore, I extended the code with an embedded jQuery 3.2.1 file. To keep is simple and not making the example unnecessarily large and complicated, I didn’t make use of spiff or an asynchronous webserver. For better performance and larger projects, I recommend ESPAsyncWebServer.


URL: https://www.dropbox.com/s/w3jvjdgs5o8wd ... P.ino?dl=0

Code: Select all

/* OTAWebUpdater.ino Example from ArduinoOTA Library
 * Rui Santos
 * Complete Project Details https://randomnerdtutorials.com
 * 20191009 Update: M.Mastenbroek - Added embedded jquery
 */
 
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <Update.h>

const char* sw_version = "20191009";
const char* host = "esp32";
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";


/*	jquery.min.js version 3.2.1

	size of original js file:  85 KB
	size in this source file: 148 KB
	size on ESP:               30 KB
 
 */
#define jquery_min_js_v3_2_1_gz_len 30178
PROGMEM const char jquery_min_js_v3_2_1_gz[]  = {
0x1f,0x8b,   .................... 0x01,0x00
};

WebServer server(80);
 
/*
 * Login page
 */
const char* loginIndex =
 "<form name='loginForm'>"
    "<table width='20%' bgcolor='A09F9F' align='center'>"
        "<tr>"
            "<td colspan=2>"
                "<center><font size=4><b>ESP32 Login Page</b></font></center>"
                "<br>"
            "</td>"
            "<br>"
            "<br>"
        "</tr>"
        "<td>Username:</td>"
        "<td><input type='text' size=25 name='userid'><br></td>"
        "</tr>"
        "<br>"
        "<br>"
        "<tr>"
            "<td>Password:</td>"
            "<td><input type='Password' size=25 name='pwd'><br></td>"
            "<br>"
            "<br>"
        "</tr>"
        "<tr>"
            "<td><input type='submit' onclick='check(this.form)' value='Login'></td>"
        "</tr>"
    "</table>"
"</form>"
"<script>"
    "function check(form)"
    "{"
    "if(form.userid.value=='admin' && form.pwd.value=='admin')"
    "{"
    "window.open('/serverIndex')"
    "}"
    "else"
    "{"
    " alert('Error Password or Username')/*displays error message*/"
    "}"
    "}"
"</script>";
 
/*
 * Server Index Page
 */
 
const char* serverIndex =
"<script src='/jquery.min.js'></script>"
"Because the lack of an asynchronous webserver in this Arduino sketch like 'ESPAsyncWebServer', <br/>"
"both file 'serverIndex' and 'jquery.min.js' can't be read from the webserver at the same time. <br/><br/>"
"Your web browser probably requests those two files simultaneously and therefore <br/>"
"the javascript file failed to load. By a refresh of this page, the browser cash has already <br/>"
"load 'serverIndex' file, the web browser will do a second attempt to only read the javascript file. <br/>"
"This second attempt, with an idle webserver, will be processed.<br/><br/>"
"Long story short, press F5 (refresh web browser) before uploading your firmware. <br/><br/>"
"<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>"
   "<input type='file' name='update'>"
        "<input type='submit' value='Update'>"
    "</form>"
 "<div id='prg'>progress: 0%</div>"
 "<script>"
  "$('form').submit(function(e){"
  "e.preventDefault();"
  "var form = $('#upload_form')[0];"
  "var data = new FormData(form);"
  " $.ajax({"
  "url: '/update',"
  "type: 'POST',"
  "data: data,"
  "contentType: false,"
  "processData:false,"
  "xhr: function() {"
  "var xhr = new window.XMLHttpRequest();"
  "xhr.upload.addEventListener('progress', function(evt) {"
  "if (evt.lengthComputable) {"
  "var per = evt.loaded / evt.total;"
  "$('#prg').html('progress: ' + Math.round(per*100) + '%');"
  "}"
  "}, false);"
  "return xhr;"
  "},"
  "success:function(d, s) {"
  "console.log('success!')"
 "},"
 "error: function (a, b, c) {"
 "}"
 "});"
 "});"
 "</script>";

// Callback for the embedded jquery.min.js page
void onJavaScript(void) {
    Serial.println("onJavaScript(void)");
		server.setContentLength(jquery_min_js_v3_2_1_gz_len);
		server.sendHeader(F("Content-Encoding"), F("gzip"));
    server.send_P(200, "text/javascript", jquery_min_js_v3_2_1_gz, jquery_min_js_v3_2_1_gz_len);
}

/*
 * setup function
 */
void setup(void) {
  Serial.begin(115200);
 
  // Connect to WiFi network
  //WiFi.begin(ssid, password);
  WiFi.softAP(ssid, password);
  Serial.println();
  Serial.print("software version: ");
  Serial.println(sw_version);
  Serial.print("host: ");
  Serial.println(host);
  Serial.print("ssid: ");
  Serial.println(ssid);
  Serial.print("password: ");
  Serial.println(password);
  IPAddress IP = WiFi.softAPIP();
  Serial.print("url: http://");
  Serial.print(IP);
  Serial.println("/ (user: admin, pswd: admin)");
 
  /*use mdns for host name resolution*/
  if (!MDNS.begin(host)) { //http://esp32.local
    Serial.println("Error setting up MDNS responder!");
    while (1) {
      delay(1000);
    }
  }
  Serial.println("mDNS responder started");

  /*return javascript jquery */
  server.on("/jquery.min.js", HTTP_GET, onJavaScript);
  /*return index page which is stored in serverIndex */
  server.on("/", HTTP_GET, []() {
    Serial.println("server.on(/)");
    server.sendHeader("Connection", "close");
    server.send(200, "text/html", loginIndex);
  });
  server.on("/serverIndex", HTTP_GET, []() {
    Serial.println("server.on(/serverIndex)");
    server.sendHeader("Connection", "close");
    server.send(200, "text/html", serverIndex);
  });
  /*handling uploading firmware file */
  server.on("/update", HTTP_POST, []() {
    Serial.println("server.on(/update)");
    server.sendHeader("Connection", "close");
    server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
    ESP.restart();
  }, []() {
    HTTPUpload& upload = server.upload();
    if (upload.status == UPLOAD_FILE_START) {
      Serial.printf("Update: %s\n", upload.filename.c_str());
      if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { //start with max available size
        Update.printError(Serial);
      }
    } else if (upload.status == UPLOAD_FILE_WRITE) {
      /* flashing firmware to ESP*/
      if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
        Update.printError(Serial);
      }
    } else if (upload.status == UPLOAD_FILE_END) {
      if (Update.end(true)) { //true to set the size to the current progress
        Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
      } else {
        Update.printError(Serial);
      }
    }
  });
  server.begin();
}
 
void loop(void) {
  server.handleClient();
  delay(1);
}

TomWS1
Posts: 20
Joined: Wed May 01, 2019 2:50 pm

Re: OTA with ESP32 as AP

Postby TomWS1 » Thu Oct 10, 2019 11:56 am

@machiel, nice contribution! I've wondered how to include jquery into an embedded webserver. Thanks for showing us a way.

machiel
Posts: 2
Joined: Wed Oct 09, 2019 1:29 pm

Re: OTA with ESP32 as AP

Postby machiel » Thu Oct 10, 2019 1:14 pm

For the record, In the code I published, jQuery is already included / embedded. You only have do download the code by clicking on the DropBox link. The code I paste in the form is incomplete, because I reached the max size ☹. Therefore I removed the binary part of the code.

Code: Select all

#define jquery_min_js_v3_2_1_gz_len 30178
PROGMEM const char jquery_min_js_v3_2_1_gz[]  = {
0x1f,0x8b,   .................... 0x01,0x00
};
Nevertheless, if you mean how to make an embedded file yourself. My suggestion is to read this post. I used this information myself to create this webserver.

https://tinkerman.cat/post/embed-your-w ... are-image/

Who is online

Users browsing this forum: Baidu [Spider] and 9 guests