如何在 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 更新)

ESP32 OTA 无需向设备刷写代码

IoT如何实现 ESP32 固件的 OTA 在线升级更新

MQTT 订阅 / OTA 更新深度睡眠 / ESP32 / FreeRTOS

使用以太网校验和的 esp32 OTA 失败

esp32 串口,蓝牙等其他方式OTA