Технология программирования "по воздуху" (Over the Air, OTA) доступна для микроконтроллеров ESP32 и ESP8266, имеющих поддержку WiFi. Данная технология позволяет обновлять их прошивку или загружать в них новую программу беспроводным способом, без всякого физического контакта с ними. Эта технология особенно полезна в случаях, когда модуль жестко закреплен в каком-нибудь устройстве и подключение кабеля к нему затруднено или физически невозможно. Технология беспроводного программирования OTA широко востребована в различных проектах интернета вещей (IoT) и применяется для удаленного обновления программного обеспечения подключенных к сети интернет устройств.
Ранее на нашем сайте мы рассматривали беспроводное программирование платы ESP8266 NodeMCU, в этой же статье мы рассмотрим программирование модуля ESP32 "по воздуху" (Over-the-air) с помощью Arduino IDE.
Необходимые компоненты
- Модуль ESP32 (купить на AliExpress) (Реклама: ООО "АЛИБАБА.КОМ (РУ)" ИНН: 7703380158).
- Arduino IDE.
Как работает программирование "по воздуху" (OTA)
Для того чтобы иметь возможность программирования модуля ESP32 "по воздуху" (Over-the-air) сначала в него необходимо через последовательный порт загрузить специальный скетч, который содержит HTML код программы OTA Web Updater. Когда этот скетч будет загружен в модуль ESP32, он создаст веб-сервер, с помощью которого можно будет выбрать и загрузить новый скетч через веб-браузер.
Для загрузки этого специального скетча, содержащего OTA Web Updater, подключите модуль ESP32 к компьютеру/ноутбуку с помощью кабеля, откройте Arduino IDE, выберите в ней тип платы (Board Type) ESP32 Dev Kit и выберите COM порт, к которому подключен модуль.
Затем откройте пример File > Examples >ArduinoOTA> OTAWebUpdater.
Измените в нем имя и пароль для сети Wi-Fi на их значения для своей сети Wi-Fi.
1 2 |
const char* ssid = "Your WiFi Name"; const char* password = "Password"; |
Загрузите код этого примера в модуль ESP32. После успешной загрузки кода в модуль откройте окно монитора последовательной связи (serial monitor). Измените в нем бодовую скорость (Baud Rate) и нажмите кнопку сброса (Reset button) на модуле ESP32 – после этого в окне монитора последовательной связи вы должны увидеть IP адрес модуля ESP32.
Затем откройте браузер в вставьте в его адресную строку IP адрес модуля ESP32. После этого в браузере у вас должно открыться окно веб-сервера технологии OTA (программирования "по воздуху") как показано на рисунке ниже.
По умолчания имя пользователя (Username) и пароль (Password) в этом окне равны admin.
После ввода имени пользователя и пароля у вас откроется новая вкладка в браузере, с ее помощью вы сможете загружать новый код программы в модуль.
В данной программе мы загрузим в модуль простую программу мигания светодиодом. Учтите, что каждая программа, которую вы будет загружать в модуль ESP32 "по воздуху", должна содержать в себе специальный код технологии OTA. Поэтому добавьте код программы мигания светодиодом в код примера OTA, который вы открыли ранее. Полный код необходимой программы вы можете посмотреть в конце данной статьи.
1 2 3 4 5 6 7 8 |
void loop(void) { server.handleClient(); delay(1); digitalWrite(led, HIGH); delay(600); digitalWrite(led, LOW); delay(600); } |
Сохраните код вашей программы под новым именем и в Arduino IDE выберите пункт Sketch > Export compiled Binary. После этого создастся новый бинарный файл (с расширением .bin) в каталоге, в котором находится ваш скетч.
После этого вернитесь на веб-страницу OTA, на ней нажмите на кнопку ‘Choose File’ и выберите свой файл с расширением .bin. После этого нажмите на кнопку ‘Update’ чтобы загрузить ваш скетч в модуль ESP32. На этом загрузка программы в модуль ESP32 "по воздуху" будет закончена.
Исходный код программы (скетча)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
#include <WiFi.h> #include <WiFiClient.h> #include <WebServer.h> #include <ESPmDNS.h> #include <Update.h> const char* host = "esp32"; const char* ssid = "Galaxy-M20"; const char* password = "ac312129"; //variabls to blink without delay: const int led = 2; WebServer server(80); /* * Login page */ const char* loginIndex = "<form name='loginForm'>" "<table width='20%' bgcolor='A09F9F' align='center'>" "<tr>" "<td colspan=2>" "<center><font size=4><b>ESP32 Login Page</b></font></center>" "<br>" "</td>" "<br>" "<br>" "</tr>" "<td>Username:</td>" "<td><input type='text' size=25 name='userid'><br></td>" "</tr>" "<br>" "<br>" "<tr>" "<td>Password:</td>" "<td><input type='Password' size=25 name='pwd'><br></td>" "<br>" "<br>" "</tr>" "<tr>" "<td><input type='submit' onclick='check(this.form)' value='Login'></td>" "</tr>" "</table>" "</form>" "<script>" "function check(form)" "{" "if(form.userid.value=='admin' && form.pwd.value=='admin')" "{" "window.open('/serverIndex')" "}" "else" "{" " alert('Error Password or Username')/*displays error message*/" "}" "}" "</script>"; /* * Server Index Page */ 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);" "return xhr;" "}," "success:function(d, s) {" "console.log('success!')" "}," "error: function (a, b, c) {" "}" "});" "});" "</script>"; /* * 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); digitalWrite(led, HIGH); delay(600); digitalWrite(led, LOW); delay(600); } |
Здрасте.
Юзаю ElegantOTA.
Загрузил пример, который выводит сообщение: "Привет, ElegantOTA".
(что бы прошить в МК код, который умеет по воздуху тянуть файлы )
Добавляю к IP - "/update".
Попадаю на страницу обновления firmware и FILESYSTEM.
Гружу ради примера чужие firmware и FILESYSTEM.
Загружаются - без проблем.
(Тот кто создавал эти файлы то-же юзал ElegantOTA)
FILESYSTEM - содержит WEB интерфейс, и возможность изменения данных.
firmware - содержит прошивку работы с подключенной периферией...
Да вот незадача:
После перезагрузки МК - я всё так же вижу текст "Привет, ElegantOTA"
И ни как не вижу тот самый WEB-интерфейс...
Ни как не могу понять в чем дело
Добрый день. К сожалению точного ответа на ваш вопрос я не знаю. Но, может быть, вам попробовать сначала выполнить аппаратный сброс модуля ESP32?
Приветствую. Нужна помощь. В прошивку зашито обновление с адресом на виртуальном хосте.
Работало нормально пока не сдох хостер, переехали. На новом хосте Обновление заработало только после отключения HSTS. Проработало 2 дня и "no firmware url"... К json и бинарнику доступ 755, в браузере джейсон открывается. Хостеры говорят у нас нет проблем, ответы сервера 200 = все хорошо.
Вопрос - ЧЁ, мать его, НЕ ТАК?
Добрый вечер. К сожалению, не могу вам дать какой нибудь дельный совет по вашей проблеме
может пригодится кому. Итак, работали с программистом у меня дома - обновление работало. Он уехал к себе, забрал устройство с собой - вот тут-то и кончилась работа обновления... Сегодня (ангел в ухо напел) дай, думаю попробую. Взял второй экземпляр и попробовал обновить - летает! Короче толи провайдер инета у него такой, толи фаервол какой-то хитрый... Тестируйте инет...
Понятно. Спасибо вам за конструктивный комментарий для сайта
Добрый день А как правильно соединить скетч из этой статьи и скейч для обновления по воздуху? И если я правильно понимаю в принципе это всё сделать можно с телефона , через arduino IDE и раз данный wi-fi???
Добрый вечер. Он же уже объединен в приведенном исходном тексте программы. А телефон вы для чего хотите использовать?
Ой, я прочитал и понял что не дописал вопрос.
Хотел использовать для блокировки лодочного мотора, (планирую скейтч из статьи обнаружения блютуз устройств, смартчасов) так скажем защита от пьяных друзей и ТД. И суть вопроса была чтоб мог тот скейч по воздуху обновлять с телефона на случай замены или добавления блютуз адресов.
Ну если вы установите на смартфон arduino ide или его аналог (пример можно посмотреть в этой статье - arduinoplus.ru/kak-zaprogrammirovat-arduino-cherez-android-telefon/?ysclid=lcow73d1r9476709003) и будете со смартфона WiFi на модуль ESP32 раздавать, то наверно можно
как перезагрузить без кнопки перезагрузки
Это можно сделать программно командой ESP.restart();. Пример можно посмотреть здесь - techtutorialsx.com/2017/12/03/esp32-arduino-software-reset/
Абалденно! Только от вас узнал об этой возможности. Огромная благодарность!!!
Да не за что. Стараемся ))