یکی از ویژگیهای فوقالعاده هر میکروکنترلر مجهز به WiFi مانند ESP32، توانایی بهروزرسانی سیستمافزار آن بهصورت بیسیم است. این به عنوان برنامه نویسی Over-The-Air (OTA) شناخته می شود.
برنامه نویسی OTA در ESP32 چیست؟
برنامه نویسی OTA امکان آپدیت/آپلود کردن یک برنامه جدید به ESP32 را با استفاده از Wi-Fi به جای اینکه کاربر برای انجام به روز رسانی ESP32 را از طریق USB به رایانه متصل کند، می دهد.
عملکرد OTA در صورت عدم دسترسی فیزیکی به ماژول ESP بسیار مفید است . این به کاهش زمان صرف شده برای به روز رسانی هر ماژول ESP در زمان تعمیر و نگهداری کمک می کند.
یکی از ویژگیهای مهم OTA این است که یک مکان مرکزی میتواند یک بهروزرسانی را به چندین ESP که شبکه مشترک مشترکی دارند ارسال کند.
تنها عیب این است که باید با هر طرحی که آپلود می کنید یک کد اضافی برای OTA اضافه کنید تا بتوانید در آپدیت بعدی از OTA استفاده کنید.
راه های پیاده سازی OTA در ESP32
دو راه برای پیاده سازی قابلیت OTA در ESP32 وجود دارد.
- OTA اولیه – بهروزرسانیهای هوایی از طریق Arduino IDE ارسال میشوند.
- Web Updater OTA – به روز رسانی های هوایی از طریق یک مرورگر وب ارسال می شود.
هر کدام مزایای خاص خود را دارند. شما می توانید هر کدام را با توجه به نیاز پروژه خود پیاده سازی کنید.
آموزش زیر اجرای OTA به روز رسانی وب را پوشش می دهد. اگر می خواهید در مورد Basic OTA بیشتر بدانید، لطفاً این آموزش را بررسی کنید.
3 مرحله ساده برای استفاده از OTA Web Updater با ESP32
- آپلود سریال روتین OTA اولین قدم این است که طرح حاوی روال OTA را به صورت سریال آپلود کنید. این یک مرحله اجباری است، به طوری که بتوانید بهروزرسانیها/آپلودهای بعدی را از طریق هوا انجام دهید.
- دسترسی به وب سرور طرح OTA یک وب سرور در حالت STA ایجاد می کند که از طریق مرورگر وب قابل دسترسی است. پس از ورود به وب سرور، می توانید طرح های جدید را آپلود کنید.
- آپلود طرح جدید از طریق هوا اکنون، می توانید با ایجاد و آپلود فایل کامپایل شده bin. از طریق یک وب سرور، طرح های جدید را در ESP32 آپلود کنید.

مرحله 1: روتین OTA را به صورت سریال آپلود کنید
تصویر کارخانه در ESP32 قابلیت OTA Upgrade ندارد. بنابراین، اولین قدم این است که سیستم عامل OTA را از طریق رابط سریال روی ESP32 بارگذاری کنید.
این یک مرحله اجباری است که در ابتدا سفتافزار را بهروزرسانی کنید تا بتوانید بهروزرسانیها/آپلودهای بعدی را از طریق هوا انجام دهید.
افزونه ESP32 برای Arduino IDE با یک کتابخانه OTA و نمونه OTAWebUpdater ارائه می شود. می توانید از طریق File > Examples >ArduinoOTA > OTAWebUpdater یا از طریق github به آن دسترسی داشته باشید .
رابط کاربری این بهروزرسانی پیشفرض OTA Web بسیار زشت است، بنابراین، ما کد را تغییر دادیم تا خنکتر و بهتر به نظر برسد . برای شروع، ESP32 خود را به رایانه خود وصل کنید و طرح زیر را آپلود کنید.
قبل از اینکه برای آپلود طرح حرکت کنید، باید تغییراتی ایجاد کنید تا برای شما کار کند. شما باید دو متغیر زیر را با اعتبار شبکه خود تغییر دهید تا ESP32 بتواند با شبکه موجود ارتباط برقرار کند.
const char* ssid = "---";
const char* password = "----";
پس از اتمام کار، پیش بروید و طرح را امتحان کنید.
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <Update.h>
const char* host = "esp32";
const char* ssid = "---";
const char* password = "----";
WebServer server(80);
/* Style */
String style =
"<style>#file-input,input{width:100%;height:44px;border-radius:4px;margin:10px auto;font-size:15px}"
"input{background:#f1f1f1;border:0;padding:0 15px}body{background:#3498db;font-family:sans-serif;font-size:14px;color:#777}"
"#file-input{padding:0;border:1px solid #ddd;line-height:44px;text-align:left;display:block;cursor:pointer}"
"#bar,#prgbar{background-color:#f1f1f1;border-radius:10px}#bar{background-color:#3498db;width:0%;height:10px}"
"form{background:#fff;max-width:258px;margin:75px auto;padding:30px;border-radius:5px;text-align:center}"
".btn{background:#3498db;color:#fff;cursor:pointer}</style>";
/* Login page */
String loginIndex =
"<form name=loginForm>"
"<h1>ESP32 Login</h1>"
"<input name=userid placeholder='User ID'> "
"<input name=pwd placeholder=Password type=Password> "
"<input type=submit onclick=check(this.form) class=btn value=Login></form>"
"<script>"
"function check(form) {"
"if(form.userid.value=='admin' && form.pwd.value=='admin')"
"{window.open('/serverIndex')}"
"else"
"{alert('Error Password or Username')}"
"}"
"</script>" + style;
/* Server Index Page */
String 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' id='file' onchange='sub(this)' style=display:none>"
"<label id='file-input' for='file'> Choose file...</label>"
"<input type='submit' class=btn value='Update'>"
"<br><br>"
"<div id='prg'></div>"
"<br><div id='prgbar'><div id='bar'></div></div><br></form>"
"<script>"
"function sub(obj){"
"var fileName = obj.value.split('\\\\');"
"document.getElementById('file-input').innerHTML = ' '+ fileName[fileName.length-1];"
"};"
"$('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) + '%');"
"$('#bar').css('width',Math.round(per*100) + '%');"
"}"
"}, false);"
"return xhr;"
"},"
"success:function(d, s) {"
"console.log('success!') "
"},"
"error: function (a, b, c) {"
"}"
"});"
"});"
"</script>" + style;
/* setup function */
void setup(void) {
Serial.begin(115200);
// Connect to WiFi network
WiFi.begin(ssid, password);
Serial.println("");
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
/*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 index page which is stored in serverIndex */
server.on("/", HTTP_GET, []() {
server.sendHeader("Connection", "close");
server.send(200, "text/html", loginIndex);
});
server.on("/serverIndex", HTTP_GET, []() {
server.sendHeader("Connection", "close");
server.send(200, "text/html", serverIndex);
});
/*handling uploading firmware file */
server.on("/update", HTTP_POST, []() {
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);
}
مرحله 2: دسترسی به وب سرور
طرح OTA Web Updater یک وب سرور در حالت STA ایجاد می کند که می توان از طریق مرورگر وب به آن دسترسی داشت و از آن برای آپلود طرح های جدید در ESP32 از طریق هوا استفاده کرد.
برای دسترسی به وب سرور، مانیتور سریال را با نرخ باود 115200 باز کنید. و دکمه EN را در ESP32 فشار دهید. اگر همه چیز درست باشد، آدرس IP پویا به دست آمده از روتر شما را خروجی می دهد.

بعد، یک مرورگر را بارگذاری کنید و آن را به آدرس IP نشان داده شده در مانیتور سریال نشان دهید. ESP32 باید یک صفحه وب را ارائه دهد که در آن اطلاعات ورود به سیستم را درخواست می کند.

شناسه کاربری و رمز عبور را وارد کنید:
شناسه کاربری : ادمین
رمز عبور : admin
در صورتی که می خواهید شناسه کاربری و رمز عبور را تغییر دهید، کد زیر را در طرح خود تغییر دهید.
"if(form.userid.value=='admin' && form.pwd.value=='admin')"
پس از ورود به سیستم، به صفحه /serverIndex هدایت خواهید شد . این صفحه به شما امکان میدهد طرحهای جدیدی را در ESP32 خود از طریق هوا آپلود کنید. طرح جدید در حال آپلود باید در .bin
قالب باینری مطابقت شده باشد. نحوه انجام این کار را در مرحله بعدی خواهیم دید.
هشدار:
عملکرد ورود به سیستم از صفحه /serverIndex محافظت نمی کند. این مشکل می تواند به کاربران اجازه دهد بدون ورود به سیستم وارد سیستم شوند.
مرحله 3: طرح جدید را در هوا آپلود کنید
اکنون، بیایید یک طرح جدید را از طریق هوا آپلود کنیم.
یاد آوردن! شما باید کد OTA Web Updater را در هر طرحی که آپلود می کنید اضافه کنید. در غیر این صورت، قابلیت OTA را از دست خواهید داد و نمیتوانید آپلودهای بعدی را از طریق هوا انجام دهید. بنابراین، توصیه می شود کد بالا را تغییر دهید تا کد جدید خود را در آن قرار دهید.
به عنوان مثال، یک طرح ساده Blink را در کد به روز رسانی وب OTA قرار می دهیم. به یاد داشته باشید که متغیرهای SSID و رمز عبور را با اعتبار شبکه خود تغییر دهید.
تغییرات در برنامه Web Updater OTA بالا با رنگ آبی مشخص شده است.
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <Update.h>
const char* host = "esp32";
const char* ssid = "---";
const char* password = "----";
//variabls for blinking an LED with Millis
const int led = 2; // ESP32 Pin to which onboard LED is connected
unsigned long previousMillis = 0; // will store last time LED was updated
const long interval = 1000; // interval at which to blink (milliseconds)
int ledState = LOW; // ledState used to set the LED
WebServer server(80);
/* Style */
String style =
"<style>#file-input,input{width:100%;height:44px;border-radius:4px;margin:10px auto;font-size:15px}"
"input{background:#f1f1f1;border:0;padding:0 15px}body{background:#3498db;font-family:sans-serif;font-size:14px;color:#777}"
"#file-input{padding:0;border:1px solid #ddd;line-height:44px;text-align:left;display:block;cursor:pointer}"
"#bar,#prgbar{background-color:#f1f1f1;border-radius:10px}#bar{background-color:#3498db;width:0%;height:10px}"
"form{background:#fff;max-width:258px;margin:75px auto;padding:30px;border-radius:5px;text-align:center}"
".btn{background:#3498db;color:#fff;cursor:pointer}</style>";
/* Login page */
String loginIndex =
"<form name=loginForm>"
"<h1>ESP32 Login</h1>"
"<input name=userid placeholder='User ID'> "
"<input name=pwd placeholder=Password type=Password> "
"<input type=submit onclick=check(this.form) class=btn value=Login></form>"
"<script>"
"function check(form) {"
"if(form.userid.value=='admin' && form.pwd.value=='admin')"
"{window.open('/serverIndex')}"
"else"
"{alert('Error Password or Username')}"
"}"
"</script>" + style;
/* Server Index Page */
String 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' id='file' onchange='sub(this)' style=display:none>"
"<label id='file-input' for='file'> Choose file...</label>"
"<input type='submit' class=btn value='Update'>"
"<br><br>"
"<div id='prg'></div>"
"<br><div id='prgbar'><div id='bar'></div></div><br></form>"
"<script>"
"function sub(obj){"
"var fileName = obj.value.split('\\\\');"
"document.getElementById('file-input').innerHTML = ' '+ fileName[fileName.length-1];"
"};"
"$('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) + '%');"
"$('#bar').css('width',Math.round(per*100) + '%');"
"}"
"}, false);"
"return xhr;"
"},"
"success:function(d, s) {"
"console.log('success!') "
"},"
"error: function (a, b, c) {"
"}"
"});"
"});"
"</script>" + style;
/* setup function */
void setup(void) {
pinMode(led, OUTPUT);
Serial.begin(115200);
// Connect to WiFi network
WiFi.begin(ssid, password);
Serial.println("");
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
/*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 index page which is stored in serverIndex */
server.on("/", HTTP_GET, []() {
server.sendHeader("Connection", "close");
server.send(200, "text/html", loginIndex);
});
server.on("/serverIndex", HTTP_GET, []() {
server.sendHeader("Connection", "close");
server.send(200, "text/html", serverIndex);
});
/*handling uploading firmware file */
server.on("/update", HTTP_POST, []() {
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);
//loop to blink without delay
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;
// if the LED is off turn it on and vice-versa:
ledState = not(ledState);
// set the LED with the ledState of the variable:
digitalWrite(led, ledState);
}
}
در برنامه بالا، از delay() برای چشمک زدن LED استفاده نکرده ایم، زیرا ESP32 برنامه شما را در حین () delay متوقف می کند. اگر یک صفحه وب درخواست شود در حالی که آردوینو متوقف شده و منتظر است تا delay() بگذرد، برنامه شما آن درخواست را از دست خواهد داد.
یک فایل .bin در Arduino IDE ایجاد کنید
برای آپلود طرح جدید در ESP32، ابتدا باید .bin
فایل باینری کامپایل شده طرح شما را تولید کنیم.
برای انجام این کار، به Sketch > Export compiled Binary بروید

هنگامی که طرح با موفقیت انجام شد، فایل bin. در پوشه Sketch ایجاد می شود. به Sketch > Show Sketch Folder بروید .

طرح جدید را از طریق هوا در ESP32 آپلود کنید
هنگامی که فایل .bin تولید شد، اکنون آماده آپلود طرح جدید در ESP32 از طریق هوا هستید.
صفحه /serverIndex را در مرورگر خود باز کنید. روی Choose File کلیک کنید… فایل .bin ایجاد شده را انتخاب کنید و سپس روی Update کلیک کنید .

در عرض چند ثانیه، طرح جدید آپلود خواهد شد.

و باید چشمک زدن LED روی برد را ببینید.

ترجمه از https://lastminuteengineers.com/esp32-ota-web-updater-arduino-ide/
آخرین دیدگاهها