- Механизм OTA обновлений прошивки “по воздуху” для ESP32 и ESP-IDF
- Что такое ОТА?
- Способы доставки ОТА-обновлений
- Подготовка разметки flash-памяти
- Код загрузки новой прошивки через HTTPS
- Где взять двоичный файл с прошивкой
- Отправляем уведомление на устройство о том, что нужно обновиться
- Перед обновлением отключите реле, управляющую нагрузкой
- Автоматический откат неудачной прошивки
- Ссылки
Механизм OTA обновлений прошивки “по воздуху” для ESP32 и ESP-IDF
Добрый день, уважаемые читатели! В этой статье я расскажу, как достаточно просто и легко выполняются OTA-обновления на микроконтроллере ESP32 и фреймворке ESP-IDF.
Совсем не сложно провернуть то же самое и на ESP8266 и Arduino. И я, быть может, даже расскажу об этом в следующей статье. Но в данном тексте речь пойдет только об ESP32 и ESP-IDF.
Что такое ОТА?
OTA — это обновление устройств, которое устанавливается при помощи Wi-Fi соединения или мобильного интернета (в случае со смартфонами). Аббревиатура OTA произошла от английского «Over the Air», а еще раньше это называлось «Firmware Over The Air» — что в переводе с английского означает «микропрограмма по воздуху».
Согласитесь, что намного удобнее обновлять “прошивки” умных устройств удаленно, без необходимости физического подключения кабелем к плате микроконтроллера. Особенно если это устройство установлено где-то глубоко в недрах кухонной вытяжки или, например, на чердаке. А особенно удобно обновлять (и добавлять новые функции, кстати) устройства, которые вообще находятся “за тридевять земель” и возможности физического подключения к ним нет вообще.
В общем с какой стороны не глянь – одни плюсы. Но на самом деле это не так, минусы то же имеются. Обо всем этом и поговорим в данной статье.
Способы доставки ОТА-обновлений
Чтобы использовать технологию ОТА-обновлений, необходимо как минимум скомпилировать исходники и “доставить” полученный двоичный файл с прошивкой непосредственно на устройство. Я знаю как минимум два способа доставки бинарного файла на устройства:
- Отправить файл с прошивкой напрямую на устройство, используя предварительно открытый специальный порт на устройстве. Вроде бы удобно, но этот способ можно использовать только в локальной сети. И возникает необходимость постоянно держать открытым порт на устройстве и мониторить его. Насколько я помню, этот способ реализован в Arduino IDE.
- Использовать промежуточный сервер в сети интернет. В этом случае файл с прошивкой предварительно загружается на этот сервер (например хостинг). Затем устройству отправляется прямая ссылка на загруженный файл любым удобным способом – через MQTT, telegram, web-интерфейс. Либо запускаем процесс обновления по расписанию и т.д. и т.п. В этом случае обновление можно “доставить” на целевое устройство в любую точку планеты, где есть интернет.
Следует учитывать, что файл с прошивкой должен быть размещен на таком ресурсе, где он доступен для скачивания по прямой ссылке. Яндекс Диск, Google Disk, Dropbox для этих целей не подойдут, так как ссылка на файл в этом случае ведёт не на сам файл, а на промежуточную страницу!
Есть и ещё способы отправить прошивку на устройство с использованием сторонних сервисов, но я ими не пользовался.
Я использую второй способ (с использованием промежуточного сервера), используя в качестве него виртуальный хостинг, на котором расположен мой сайт. И именно про этот способ пойдет речь в этой статье.
Подготовка разметки flash-памяти
Прежде чем начинать эксперименты с OTA-обновлениями, необходимо соответствующим образом подготовить разметку flash-памяти устройства.
Для OTA-обновлений потребуется как минимум два отдельных раздела с типом app / ota_x под хранение данных прошивок:
- С одного раздела запускается устройство в текущий момент, этот раздел называется активным.
- В другой раздел (неактивный) может записываться новая прошивка при получении её через OTA без риска нарушения работоспособности текущей прошивки.
Размеры этих разделов должны быть не менее, чем это требуется для загрузки всей микропрограммы, которую вы создали. Можно создать и больше ota разделов – например три или пять, максимум 16. Но лично я не вижу в этом особого смысла, всё равно работать будет только одна.
Кроме того, обязательно потребуется создать ещё один раздел otadata с типом data / ota, в котором загрузчик хранит служебные данные об активном разделе. После того, как данные новой прошивки были успешно получены и записаны в неактивный раздел, загрузчик помечает активным только что обновленный раздел и после программного перезапуска пробует загрузиться уже с новой прошивкой.
Если вдруг “что-то пошло не так”, и новая прошивка не работает как требуется, ESP-IDF предоставляет возможность автоматически откатится на предыдущую версию путем отката активного раздела на предыдущий. Более подробно об этом будет рассказано ниже.
Дополнительно (если позволяет размер вашей прошивки и свободное место на flash-памяти) можно создать раздел factory с типом app / factory, в котором содержится “заводская” прошивка. Этот раздел будет использован, если служебный раздел otadata чист и не содержит никаких сведений об активной прошивке. Но это не обязательно – если они отсутствует, то будет использован раздел ota_0.
Пример переключения между разделами ОТА: 1 – состояние после прошивки “кабелем” 2 – состояние после первого OTA обновления 3 – состояние после второго ОТА-обновления 4 – состояние после третьего ОТА-обновления или если второе завершилось не удачно
В рамках данной статьи я не буду касаться тонкостей создания файла разметки разделов, этому нужно посвятить отдельную статью. Для целей проверки OTA вполне достаточно использовать стандартную разметку “Factory app, two OTA definitions“, которая выглядит следующим образом:
Эта разметка подходит для модулей с доступной flash памятью 4МБ (самых распространенных). Как видите, здесь выделено по 1МБ выделено под два OTA-раздела, еще 1МБ под заводскую прошивку, и остальные 1МБ распределены под загрузчик, otadata, nvs и данные физической инициализации. На мой взгляд, это далеко не оптимальная разметка, но для тестовых целей вполне сгодится.
Итак, подготавливаем разделы
Заходим в sdkconfig, находим раздел (Top) → Partition Table → Partition Table и выбираем пункт “Factory app, two OTA definitions“:
Не забываем сохранить конфигурацию перед выходом.
После этого компилируем проект (хотя-бы пустой) и загружаем его в устройство через USB или COM – порт. Загрузчик и таблица разделов записываются только при “физической” прошивке.
“Потом” через ОТА изменить таблицу разделов не получится, равно как и изменить параметры отката неудачной прошивки, так как за все это отвечает именно загрузчик. Поэтому выполняем этот этап максимально тщательно, особенно если физического доступа к устройству после установки на постоянного место работы не планируется.
После того, как новая таблица разделов и настроенный загрузчик записаны на flash, уже таки можно приступать к программированию OTA.
Код загрузки новой прошивки через HTTPS
Как ни страшно звучит OTA с первого взгляда, код для загрузки новой прошивки по заданному URL выглядит достаточно просто:
1. Первым шагом настраиваем самое обычное HTTPS-подключение, но обязательно с TLS-шифрованием (без HTTPS прошивка загружаться отказывается). Через поле cfgHTTPS.url = otaSource; передаем прямую ссылку на двоичный файл с новой версией прошивки.
Приведенный на скриншоте код позволяет в зависимости от настроек в файле конфигурации выбрать либо сертификат в буфере, либо пакет, либо централизованное хранилище.
Шаг 2. Далее, если версия ESP-IDF 5.0.0 или выше, настраиваем следующую структуру конфигурации – esp_https_ota_config_t. Здесь мы указываем ссылку на ранее настроенные параметры HTTPS-подключения.
Шаг 3. Ну и в конце просто запускаем обновление с помощью функции esp_https_ota(). Но обновление не всегда проходит с первой попытки, особенно при загруженных каналах связи, поэтому я добавил цикл для того, чтобы сделать несколько попыток.
Шаг 4. Если esp_https_ota() вернула ESP_OK, перегружаем устройство с помощью банальной esp_restart().
Если мой пример показался вам чересчур сложным, взгляните на пример из справки, тут всё гораздо более просто:
Конечно, OTA API позволяет создавать и более продвинутые сценарии, но и этот простейший сценарий меня устраивает более чем полностью. А зачем платить больше?
Где взять двоичный файл с прошивкой
Я всё время говорю про какой-то там двоичный файл, а где его взять?
Первый способ – скомпилировать проект вручную командой build в platformio, а затем найти его в каталоге
%prj_dir%\.pio\build\%prj_obj%\firmware.bin, где:
- firmware.bin – это и есть тот самый файл с прошивкой
- %prj_dir% – каталог проекта, то есть тот каталог, в котором расположен файл platfromio.ini
- %prj_obj% – название секции устройства в platfromio.ini, для которого производится компиляция, например esp32dev
Затем его нужно любым способом загрузить на доступный вам HTTPS-ресурс и получить прямую ссылку на него.
Второй вариант – воспользоваться батником, который я описывал в предыдущей статье. Он же сам загрузит файл на хостинг по FTP-протоколу.
Отправляем уведомление на устройство о том, что нужно обновиться
Теперь нужно любым удобным вам способом отправить ссылку на файл на ваше устройство. Я делаю это путем отправки ссылки на обновление через MQTT-протокол в топик “%device%/system/ota“. Получив такую команду, mqtt-клиент отправляет payload в функцию otaUpdate(url), краткий текст которой я приводил выше. Вуаля!
Но можно отправить ссылку и любым другим доступным вам способом – например через telegram.
Возможен другой вариант – путь к файлу с прошивкой не изменяется и “зашит” где-то в глубинах вашей прошивки. А в топик вы отправляете просто единичку как признак начала обновления.
Возможен и третий вариант – запускать обновление принудительно по расписанию, например в 00 часов 00 минут. Но тогда вам придется позаботится о том, чтобы как-то отличать новую прошивку от текущей, дабы не выполнять повторное обновление.
В общем – думайте, что вам удобно, то и используйте. Я лишь предложил свои варианты.
Перед обновлением отключите реле, управляющую нагрузкой
ОТА – обновление – штука медленная. Жутко медленная. И иногда не предсказуемая. Устройство может зависнуть или начать спонтанно перезагружаться. Не часто, но такое вполне возможно. Как правило это происходит либо из-за проблем в прошивке, которые вы же сами с допустили, либо из-за очень медленного и загруженного канала связи.
Поэтому после того, как вы получили ссылку на файл обновления – приостановите все критичные процессы. Отключите нагрузку, которой управляет устройство. В общем, примите все необходимые меры, дабы предотвратить неожиданные и аварийные ситуации в случае зависания устройства, например.
Автоматический откат неудачной прошивки
Случается, при очередном обновлении допускаются ошибки, из-за которых после перезапуска устройство отказывается нормально загружаться. Приходится брать кабель, ноутбук и идти / лезть к устройству, дабы перепрошить его суровыми физическими методами.
На этот случай в Espressif предусмотрели механизм отката к последней удачной конфигурации приложения. Для этого активируйте опцию загрузчика (Top) → Bootloader config → Enable app rollback support:
- После включения этой опции в момент записи новой прошивки на flash раздел, в который она была записана, помечается как активный, но не подтвержденный.
- После обновления и перезапуска вы должны подтвердить нормальный запуск путем вызова функции esp_ota_mark_app_valid_cancel_rollback(). Если этого не сделать, то при ещё одной перезагрузке (или при потере питания и следующем включении) загрузчик пометит новый раздел как неисправный и вернется к предыдущему разделу. Сделать это нужно как можно скорее, но после того, как вы удостоверились, что все работает. Я делаю это после того, как устройство успешно подключилось к WiFi и MQTT-серверу.
- Если вы вдруг “поняли”, что что-то пошло не так, то можно вызвать досрочный откат прошивки, вызвав другую API функцию esp_ota_mark_app_invalid_rollback_and_reboot(), которая принудительно помечает текущий ota раздел как неисправный и выполняет перезагрузку. Можно сделать это по таймеру, который запускается автоматически после того, как устройство было перезагружено с обновлением прошивки.
Вот таким нехитрым способом можно выполнить откат, и он меня неоднократно спасал от моих же собственных ошибок.
Только не забудьте, что включить эту опцию удаленно через собственно OTA не возможно, так как эта опция влияет в основном на загрузчик, а при ОТА-обновлениях загрузчик не перезаписывается. Поэтому если вы заходите это сделать – таки придется подключаться кабелем.
Жаль только, что не всегда удается сделать программный сброс при проблемах с загрузкой после обновления и изредка все-таки приходится перегружать неудачно обновившееся устройство по питанию. Но и в этом случае откат прошивки успешно работает. И это гораздо проще, чем перепрошивать устройство кабелем.
Но иногда даже этот защитный механизм не спасает от собственной глупости.
Поэтому главное правило при ОТА-обновлениях – вначале тщательно проверяйте прошивку на тестовых устройствах. Затем на самых малоценных устройствах. И только в последнюю очередь обновляйте критично важные и удаленные устройства.
Соблюдение данного правила сбережет вам силы, время и нервы.