آموزش آپدیت بی سیم کد ESP32 با روش OTA

یکی از بهترین ویژگیهای برد ESP32 این است که میتوان فریمور (Firmware) آن را بهصورت بیسیم آپدیت کرد. این نوع برنامهنویسی با نام Over-The-Air (OTA) شناخته میشود.
OTA در ESP32 چیست؟
برنامهنویسی OTA به شما اجازه میدهد تا بدون نیاز به اتصال USB، یک برنامه جدید را از طریق Wi-Fi روی برد ESP32 آپلود کنید.
قابلیت OTA زمانی بسیار کاربردی است که دسترسی فیزیکی به ماژول ESP وجود ندارد. علاوه بر این، زمان مورد نیاز برای بهروزرسانی هر ماژول ESP در هنگام نگهداری و تعمیرات را کاهش میدهد.
یکی از مزیتهای مهم OTA این است که میتوان از یک نقطه مرکزی، بهروزرسانی را برای چندین برد ESP32 در همان شبکه ارسال کرد.
تنها ایراد این روش آن است که برای استفاده از OTA در آپدیتهای بعدی، باید کد مربوط به OTA را در هر اسکچ (Sketch) که آپلود میکنید، قرار دهید.
روشهای اجرای OTA در ESP32
بهطور کلی دو روش برای پیادهسازی قابلیت OTA در ESP32 وجود دارد:
- Basic OTA – در این روش آپدیتها از طریق Arduino IDE انجام میشوند.
- Web Updater OTA – در این روش آپدیت از طریق مرورگر وب انجام میشود.
هر دو روش مزایا و کاربردهای خاص خود را دارند و بسته به پروژه میتوانید هرکدام را انتخاب کنید.
در این آموزش، بهصورت گامبهگام با نحوهی اجرای Basic OTA و Web Updater در ESP32 آشنا میشوید.
مراحل استفاده از Basic OTA در ESP32
برای فعالسازی و استفاده از برنامهنویسی OTA در برد ESP32 سه مرحله اصلی وجود دارد:
- نصب Python نسخه 2.7.x
- آپلود اولیه فریمور OTA از طریق پورت سریال
- آپلود اسکچ جدید از طریق وایفای (Over-The-Air)
مرحله 1: نصب Python نسخه 2.7.x
برای استفاده از قابلیت OTA ابتدا باید نرمافزار Python نسخه 2.7.x را روی سیستم خود نصب کنید (در صورتی که از قبل نصب نشده باشد).

نسخهی مورد نیاز را میتوانید از سایت رسمی پایتون دانلود کنید:
Download Python 2.7.x for Windows (MSI Installer)
پس از دانلود، نصبکننده را اجرا کرده و مراحل نصب را تا پایان طی کنید.

در هنگام نصب، حتماً گزینهی “Add python.exe to Path” را در بخش Customize Python 2.7.x فعال کنید تا پایتون به مسیر سیستم شما اضافه شود.

مرحله 2: آپلود فریمور Basic OTA از طریق پورت سریال
از آنجایی که تصویر کارخانهای ESP32 بهصورت پیشفرض قابلیت OTA Upgrade را ندارد، باید ابتدا فریمور OTA را از طریق پورت سریال روی برد آپلود کنید.
این مرحله ضروری است تا بتوانید در مراحل بعدی، آپدیتهای بعدی را بهصورت بیسیم (Over-The-Air) انجام دهید.
افزونه ESP32 در Arduino IDE شامل کتابخانهی OTA و همچنین یک نمونه کد آماده با نام BasicOTA است. برای باز کردن آن کافی است مسیر زیر را دنبال کنید:
File > Examples > ArduinoOTA > BasicOTA

تنظیمات شبکه برای اتصال ESP32
قبل از آپلود کد، باید دو متغیر زیر را با اطلاعات شبکهی Wi-Fi خود (SSID و رمز عبور) ویرایش کنید تا برد بتواند به شبکه متصل شود:
const char* ssid = ".........."; const char* password = "..........";
سپس میتوانید اسکچ را روی برد ESP32 آپلود کنید.
پس از آپلود کد، Serial Monitor را در نرخ 115200 باز کنید و دکمه EN روی برد را فشار دهید. اگر همه چیز درست انجام شده باشد، در سریال مانیتور باید آیپی (IP Address) اختصاص داده شده به ESP32 را توسط روتر خود مشاهده کنید. این آدرس را یادداشت کنید زیرا در مرحله بعد مورد استفاده قرار میگیرد.

مرحله 3: آپلود اسکچ جدید بهصورت Over-The-Air
در این مرحله، شما میتوانید کد جدید خود را بهصورت بیسیم (OTA) روی ESP32 آپلود کنید.
توجه کنید که در هر بار آپلود، باید بخش کد OTA را در اسکچ خود نگه دارید. در غیر این صورت، قابلیت OTA از بین میرود و دیگر نمیتوانید آپدیتهای بعدی را بیسیم انجام دهید. بنابراین، پیشنهاد میشود همیشه کد OTA را با کد جدید ترکیب کنید.
مثال: ترکیب کد OTA با برنامه چشمکزن (Blink)
در این مثال، برنامهی سادهی Blink با کد OTA ادغام شده است تا LED روی برد ESP32 بدون تأخیر چشمک بزند. فراموش نکنید که SSID و رمز عبور شبکه را با اطلاعات واقعی خود جایگزین کنید.
#include <WiFi.h>
#include <ESPmDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
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
void setup() {
pinMode(led, OUTPUT);
Serial.begin(115200);
Serial.println("Booting");
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.println("Connection Failed! Rebooting...");
delay(5000);
ESP.restart();
}
// Port defaults to 3232
// ArduinoOTA.setPort(3232);
// Hostname defaults to esp3232-[MAC]
// ArduinoOTA.setHostname("myesp32");
// No authentication by default
// ArduinoOTA.setPassword("admin");
// Password can be set with it's md5 value as well
// MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
// ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
ArduinoOTA
.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH)
type = "sketch";
else // U_SPIFFS
type = "filesystem";
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
Serial.println("Start updating " + type);
})
.onEnd([]() {
Serial.println("\nEnd");
})
.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
})
.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
else if (error == OTA_END_ERROR) Serial.println("End Failed");
});
ArduinoOTA.begin();
Serial.println("Ready");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
void loop() {
ArduinoOTA.handle();
//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() استفاده نشده تا برنامه در هنگام درخواست OTA متوقف نشود و همیشه آمادهی دریافت آپدیت باشد. اتصال به پورت OTA در Arduino IDE
پس از کپی کردن کد در Arduino IDE، مسیر زیر را باز کنید:
Tools > Port
در اینجا باید پورتی مشابه عبارت زیر را ببینید:
esp32-xxxxxx at your_esp_ip_address

اگر آن را مشاهده نکردید، IDE را یکبار ریاستارت کنید. سپس پورت مربوط به ESP32 را انتخاب کرده و روی دکمه Upload کلیک کنید.
در عرض چند ثانیه، کد جدید بهصورت بیسیم روی برد آپلود میشود و LED داخلی برد شروع به چشمک زدن خواهد کرد.

آپلود بی سیم با Web Updater OTA
در این بخش، یاد میگیرید چگونه با استفاده از Web Updater OTA، برنامهها را از طریق مرورگر وب روی برد ESP32 آپلود کنید. این روش یکی از راحتترین و پیشرفتهترین روشهای آپدیت فریمور ESP32 بهصورت بیسیم است.
- آپلود کد OTA از طریق پورت سریال: در ابتدا باید اسکچی را که شامل کد OTA است، بهصورت سریال روی برد ESP32 آپلود کنید. این مرحله برای فعالسازی آپدیتهای بعدی از طریق Wi-Fi ضروری است.
- دسترسی به وبسرور: کد OTA بهصورت خودکار یک وبسرور در حالت STA ایجاد میکند که میتوانید از طریق مرورگر به آن متصل شوید. پس از ورود، قادر خواهید بود فایلهای جدید را آپلود کنید.
- آپلود اسکچ جدید بهصورت OTA: پس از اتصال به وبسرور، فایل کامپایلشده با پسوند
.binرا از طریق مرورگر روی ESP32 آپلود کنید.

مرحله 1: آپلود کد OTA از طریق پورت سریال
از آنجایی که تصویر کارخانهای ESP32 قابلیت OTA را بهصورت پیشفرض ندارد، لازم است ابتدا فریمور OTA را از طریق پورت سریال آپلود کنید. بدون انجام این مرحله، امکان آپدیتهای بعدی از طریق Wi-Fi وجود نخواهد داشت.
افزونه ESP32 در Arduino IDE شامل کتابخانه OTA و یک مثال آماده به نام OTAWebUpdater است. برای دسترسی به آن مسیر زیر را دنبال کنید:
File > Examples > ArduinoOTA > OTAWebUpdater
رابط کاربری پیشفرض Web Updater بسیار ساده و ابتدایی است، بنابراین در این آموزش از نسخهای اصلاحشده با رابط کاربری زیباتر استفاده میکنیم.
برای شروع، برد ESP32 را به رایانه وصل کنید و اسکچ زیر را آپلود کنید.
تنظیمات شبکه Wi-Fi
قبل از آپلود، حتماً اطلاعات شبکه خود را در دو متغیر زیر وارد کنید تا ESP32 بتواند به شبکه متصل شود:
const char* ssid = "---"; const char* password = "----";
پس از ویرایش مقادیر بالا، کد را روی برد آپلود کنید.
کد اصلی شامل بخشهایی برای ایجاد سرور وب، مدیریت ورود کاربر، و بارگذاری فایل فریمور است. با اجرای این کد، یک وبسرور روی پورت 80 راهاندازی میشود که از طریق آدرس IP برد قابل دسترسی است.
#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);
} ورود به رابط وب آپدیت ESP32
پس از اجرای برنامه، در Serial Monitor آدرس IP تخصیص دادهشده به ESP32 را مشاهده خواهید کرد. این آدرس را در مرورگر خود وارد کنید (مثلاً: http://192.168.1.25) تا صفحه ورود باز شود.
نام کاربری و رمز عبور پیشفرض هر دو admin هستند.
پس از ورود، صفحهای با دکمهی انتخاب فایل و نوار پیشرفت بارگذاری برای آپلود فریمور جدید نمایش داده میشود.
آپلود فریمور جدید از طریق مرورگر
برای آپلود برنامه جدید، کافی است فایل .bin تولیدشده در Arduino IDE را انتخاب کرده و روی دکمه Update کلیک کنید. فرآیند آپلود آغاز شده و درصد پیشرفت در نوار پایین صفحه نمایش داده میشود.
پس از اتمام، ESP32 بهصورت خودکار ریاستارت میشود و نسخه جدید برنامه اجرا خواهد شد.
مزایای استفاده از Web Updater در ESP32
- بدون نیاز به کابل USB یا اتصال فیزیکی به برد
- امکان آپدیت همزمان چندین برد ESP32 در یک شبکه
- رابط کاربری ساده و قابل دسترسی از طریق مرورگر
- مناسب برای پروژههای اینترنت اشیا (IoT) با نصب در مکانهای دور
مرحله 2: دسترسی به وب سرور ESP32
در این مرحله، برنامه OTA Web Updater یک وبسرور در حالت STA ایجاد میکند که از طریق مرورگر میتوان به آن متصل شد و کدهای جدید را بهصورت بیسیم (Over-The-Air) روی ESP32 آپلود کرد.
برای دسترسی به وب سرور، مانیتور سریال را روی 115200 baud باز کنید و دکمه EN روی برد ESP32 را فشار دهید. اگر همهچیز درست باشد، آدرس IP که از روتر دریافت کرده روی سریال نمایش داده میشود.

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

نام کاربری و رمز پیشفرض به شکل زیر است:
- User ID: admin
- Password: admin
در صورت تمایل به تغییر اطلاعات ورود، کافی است در کد برنامه، بخش زیر را ویرایش کنید:
if(form.userid.value=='admin' && form.pwd.value=='admin')
بعد از ورود، به صفحهای با آدرس /serverIndex هدایت میشوید. در این صفحه، میتوانید فایل جدید برنامه را روی ESP32 آپلود کنید.
نکته مهم این است که فایل آپلودی باید با فرمت .bin (باینری کامپایلشده) باشد. در مرحله بعد یاد میگیرید چگونه این فایل را در Arduino IDE تولید کنید.
مرحله 3: آپلود کد جدید بهصورت بیسیم (OTA Upload)
در این مرحله، زمان آن رسیده تا کد جدید را از طریق مرورگر روی ESP32 آپلود کنید.
یادتان باشد که برای هر بار آپلود OTA، باید کد مربوط به OTA را در برنامهی جدیدتان نیز قرار دهید. در غیر این صورت، بعد از آپلود، دیگر امکان آپدیت بیسیم نخواهید داشت.
بهعنوان مثال، در این آموزش یک کد سادهی چشمکزن (Blink) را به برنامه OTA اضافه میکنیم. قبل از آپلود، حتما مقادیر SSID و Password را با اطلاعات شبکه وایفای خودتان جایگزین کنید.
کد کامل زیر، نمونهای از Web Updater OTA است که امکان آپلود فایل باینری و چشمکزدن LED داخلی را فراهم میکند:
#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 = "----";
// Variables for LED blinking
const int led = 2;
unsigned long previousMillis = 0;
const long interval = 1000;
int ledState = LOW;
WebServer server(80);
/* Style and HTML omitted for brevity — identical to the tutorial */
void setup(void) {
pinMode(led, OUTPUT);
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
Serial.println(WiFi.localIP());
if (!MDNS.begin(host)) { while (1) { delay(1000); } }
server.on("/", HTTP_GET, []() { server.send(200, "text/html", loginIndex); });
server.on("/serverIndex", HTTP_GET, []() { server.send(200, "text/html", serverIndex); });
server.on("/update", HTTP_POST, []() {
server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
ESP.restart();
}, []() {
HTTPUpload& upload = server.upload();
if (upload.status == UPLOAD_FILE_START) { Update.begin(UPDATE_SIZE_UNKNOWN); }
else if (upload.status == UPLOAD_FILE_WRITE) { Update.write(upload.buf, upload.currentSize); }
else if (upload.status == UPLOAD_FILE_END) { Update.end(true); }
});
server.begin();
}
void loop(void) {
server.handleClient();
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
ledState = !ledState;
digitalWrite(led, ledState);
}
}
توجه کنید که در این برنامه از تابع delay() استفاده نشده است، چون delay باعث توقف کامل برنامه میشود و در نتیجه ممکن است درخواست OTA در حین توقف از دست برود.
نحوه ساخت فایل باینری (.bin) در Arduino IDE
برای اینکه بتوانید کد را از طریق OTA روی ESP32 آپلود کنید، باید ابتدا از برنامه خود فایل .bin (کامپایلشده) بسازید.
برای این کار:
- از منوی بالا به مسیر Sketch > Export Compiled Binary بروید.
- بعد از کامپایل موفق، فایل .bin در پوشه Sketch ذخیره میشود.
- برای باز کردن آن پوشه، به مسیر Sketch > Show Sketch Folder بروید.
آپلود فایل باینری روی ESP32 از طریق مرورگر

اکنون که فایل .bin آماده است، مرورگر را باز کنید و به آدرس /serverIndex بروید. سپس روی دکمه Choose File… کلیک کرده و فایل .bin را انتخاب کنید. بعد روی Update بزنید.

در عرض چند ثانیه، فایل جدید آپلود و نصب میشود. در صورت موفقیت، LED داخلی برد ESP32 شروع به چشمکزدن خواهد کرد.








