I am trying to trigger OTA update on a webserver hosted on ESP32 externally and without opening the browser address of the webserver.
I have seen some examples where the OTA update action can be achieved if the ESP is connected as client, but I am looking for solutions where we can keep the ESP as the webserver only. Since, this action is assumed to be triggered over network so for that reasons, I will happily accept if any method curl, python/JavaScript http method, node red etc.
This is the server part of code running in ESP32:
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <Update.h>
#include <WiFiManager.h>
WebServer server(80);
const char* serverIndex =
"<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>"
"<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);"
"window.location.href ='/login';"
"return xhr;"
"},"
"success:function(d, s) {"
"console.log('successok !')"
"},"
"error: function (a, b, c) {"
"}"
"});"
"});"
"</script>";
void setup()
{
//redundant parts have been removed intentionally
WiFi.begin(ssid, pass);
int ct =0;
while (!MDNS.begin(host)) { //http://esp32.local //http://shariq-room.local
Serial.println("Error setting up MDNS responder!");
delay(1000);
ct= ct+1000;
if (ct>5000) {
Serial.println("MDNS cannot be set");
break;
}
}
MDNS.addService("http", "tcp", 80);
server.on("/serverIndex", HTTP_GET, []() {
server.sendHeader("Connection", "close");
server.send(200, "text/html", serverIndex);
});
server.on("/update", HTTP_POST, []() {
server.sendHeader("Connection", "close");
server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
ESP.restart();
}, []() {
Serial.println("OTA started ");
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()
{
server.handleClient();
delay(400);
}
- Now, this will allow me to select and chose a bin file , if I open the localIP and process the OTA if "update" button is clicked.
- But the same "act" will fail to trigger it when using either of python script of curl command. The curl command being copied from network section of chrome developer tools.
Python script is here.
##python
import requests
data = open("home_automate_inc_ota_v.0.2a.ino.esp32.bin", 'rb').read()
files = {'file': open("home_automate_inc_ota_v.0.2a.ino.esp32.bin", 'rb')}
#headers = { "Content-Type":"application/binary",}
headers= { 'Accept': 'text/plain'}
print("started")
upload = requests.post("http://192.168.0.7/update", data=data,headers=headers)
#tried below method too
# final_resp = requests.post("http://192.168.0.7/update", files=files, headers=headers)
print("done");
But I will get error on either of the post method like raise ConnectionError(err, request=request) requests.exceptions.ConnectionError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))
The same when tried in curl
curl "http://192.168.0.50/update" ^
-H "Connection: keep-alive" ^
-H "Accept: */*" ^
-H "X-Requested-With: XMLHttpRequest" ^
-H "User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36" ^
-H "Content-Type: multipart/form-data; boundary=----#some hidden webkit data#" ^
-H "Origin: http://192.168.0.50" ^
-H "Referer: http://192.168.0.50/backup" ^
-H "Accept-Language: en-US,en;q=0.9" ^
-H "Cookie: ext_name=#some hidden cookie data#" ^
--data-raw ^"------#some hidden webkit data#^
Content-Disposition: form-data; name=^\^"update^\^"; filename=^\^"home_automate_inc_ota_v.0.2a.ino.esp32.bin^\^"^
Content-Type: application/octet-stream^
^
^
------WebKitFormBoundaryPtpk4VGDdfwqS932--^
^" ^
--compressed ^
--insecure
Gives error as curl: (52) Empty reply from server
Hope I have explained the question well. Please let me know if you need any more clarifications.
Regards, Shariq