如何在 ESP32 中发布 OTA 的 bin 文件,而无需在 Web 服务器中托管的浏览器中手动打开页面
Posted
技术标签:
【中文标题】如何在 ESP32 中发布 OTA 的 bin 文件,而无需在 Web 服务器中托管的浏览器中手动打开页面【英文标题】:How to post bin file for OTA in ESP32 without manually opening the page in the browser hosted in the webserver 【发布时间】:2021-07-13 13:41:02 【问题描述】:我正在尝试在外部托管在 ESP32 上的网络服务器上触发 OTA 更新,并且没有打开网络服务器的浏览器地址。
我已经看到了一些示例,如果 ESP 作为客户端连接,则可以实现 OTA 更新操作,但我正在寻找可以将 ESP 仅作为网络服务器的解决方案。 由于假定此操作是通过网络触发的,因此,如果有任何方法 curl、python/javascript http 方法、node red 等,我将很乐意接受。
这是在 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);
现在,这将允许我选择一个 bin 文件,如果我打开
如果单击“更新”按钮,则本地 IP 并处理 OTA。
但是当使用任一python时,相同的“行为”将无法触发它
curl 命令的脚本。从 chrome 开发者工具的网络部分复制 curl 命令。
Python 脚本在这里。
##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");
但是我会在任何一种 post 方法上遇到错误,例如 引发 ConnectionError(err, request=request) requests.exceptions.ConnectionError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))
在 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
给出错误为 curl: (52) 来自服务器的空回复
希望我已经很好地解释了这个问题。如果您需要更多说明,请告诉我。
问候, 沙里克
【问题讨论】:
至少您需要向curl
添加一个-X POST
选项以将其发送到POST
请求,而不是使用GET
方法。鉴于您的 Web 服务器不查找它们,您也并不真正需要标头 (-H
) 选项。
你好..当我添加一个没有所有这些标题的帖子时,它会返回“OK”并且只是重新启动而不尝试 ota。但我想我们快到了..这就是我尝试的 curl "192.168.0.50/update" ^ -X POST^ --data-raw ^"------WebKitFormBoundary#hidden data#^ Content-Disposition: form-数据;名称=^\^"更新^\^";文件名=^\^"home_automate_inc_ota_v.0.2a.ino.esp32.bin^\^"^ 内容类型:应用程序/八位字节流^ ^ --- ---WebKitFormBoundary#hidden data#--^ ^" ^ --compressed ^ --insecure
如果你真的用curl
发送文件会有所帮助...
嘿,实际上有你的建议,我尝试用 python 发送相同的文件但没有标题,它就像魔术一样工作:p
【参考方案1】:
正如Romkey 建议的那样,我所要做的就是从我的请求中删除标题。 我发现我的 python 代码成功了,但是 curl 命令失败了。 这是脚本。
#!Python
import requests
files = 'file': open(r"<full path of the bin file including dir>home_automate_inc_ota_v.0.2a.ino.esp32.bin", 'rb')
final_resp = requests.post("http://192.168.0.50/update", files=files)
【讨论】:
以上是关于如何在 ESP32 中发布 OTA 的 bin 文件,而无需在 Web 服务器中托管的浏览器中手动打开页面的主要内容,如果未能解决你的问题,请参考以下文章
Arduino ESP32 通过 BLE 接收文件(用于 OTA 更新)