Перейти к содержимому

OTA обновления прошивки “по воздуху” для ESP32 и платформы ESP-IDF

Добрый день, уважаемые читатели!

В этой статье я расскажу, как достаточно просто и легко выполняются OTA-обновления на микроконтроллере ESP32 и фреймворке ESP-IDF. Совсем не сложно провернуть то же самое и на ESP8266 / ESP32 и для платформы Arduino. Но в данном тексте речь пойдет только об ESP32 и ESP-IDF. Если вы пока ещё не знаете, что такое платформа, ESP-IDF и т.д., то рекомендую вам начать с этой статьи.

 


Что такое ОТА?

OTA — это обновление устройств, которое устанавливается при помощи Wi-Fi соединения или мобильного интернета (в случае со смартфонами). Аббревиатура OTA произошла от английского «Over the Air», а еще раньше это называлось «Firmware Over The Air» — что в переводе с английского означает «микропрограмма по воздуху».

Согласитесь, что намного удобнее обновлять “прошивки” умных устройств удаленно, без необходимости физического подключения кабелем к плате микроконтроллера. Особенно если это устройство установлено где-то глубоко в недрах кухонной вытяжки или, например, на чердаке. А особенно удобно обновлять (и добавлять новые функции, кстати) устройства, которые вообще находятся “за тридевять земель” и возможности физического подключения к ним нет вообще.

В общем с какой стороны не глянь – одни плюсы. Но на самом деле это не так, минусы то же имеются. Обо всем этом и поговорим в данной статье.


Способы доставки ОТА-обновлений

Чтобы использовать технологию ОТА-обновлений, необходимо как минимум скомпилировать исходники и “доставить” полученный двоичный файл с прошивкой непосредственно на устройство. Я знаю как минимум два способа доставки бинарного файла на устройства:

  • Отправить файл с прошивкой напрямую на устройство, используя предварительно открытый специальный порт на устройстве. Вроде бы удобно, но этот способ можно использовать только в локальной сети. И возникает необходимость постоянно держать открытым порт на устройстве и мониторить его. Насколько я помню, этот способ реализован в Arduino IDE.
  • Использовать промежуточный WEB-сервер в сети интернет (по протоколу httpS) или локальный (можно использовать просто http). В этом случае файл с прошивкой предварительно загружается на этот сервер. Затем устройству отправляется прямая ссылка на загруженный файл любым удобным способом – через MQTT, telegram, web-интерфейс. Либо запускаем процесс обновления по расписанию и т.д. и т.п. В этом случае обновление можно “доставить” на целевое устройство в любую точку планеты, где есть интернет.
  • Если на вашем устройстве реализован полноценный telegram-бот (например с помощью готовой библиотеки FastBot), то можно доставлять сообщения на устройство через него. 

Я использую второй способ – WEB-сервер (используя в качестве него виртуальный хостинг, на котором расположен данный сайт). И именно про этот способ пойдет речь в этой статье. Но совсем не обязательно иметь платный хостинг, вполне можно обойтись и локальным сервером, который не так уж и сложно сделать самому. 

Какой WEB-сервер вам необходим?

☝️ Следует учитывать, что файл с прошивкой должен быть размещен на таком ресурсе, где он доступен для скачивания по прямой ссылке. Яндекс Диск, Google Disk, Dropbox и прочие аналогичные сервисы для этих целей не подойдут, так как ссылка на файл в этом случае ведёт не на сам файл, а на промежуточную страницу!

Если вам требуется легко и просто обновлять устройства, которые расположены вне вашей локальной сети (из дома обновить устройство на даче, из офиса – в соседний офис и т.д.) то рассмотрите вопрос использования любого бесплатного или платного хостинга в сети интернет, но он должен иметь поддержку защищенных TLS-соединений. 

Если же вам необходимо обновлять ваши устройства, которые подключены к одному и тому – же роутеру (точке доступа wifi), то проще всего использовать локальные варианты WEB-сервера, как то:

В случае обновления устройств с локального сервера уже нет строгой необходимости использования защищенных соединений, поэтому и настройка такого сервера будет проще и легче. Как и собственно процесс программирования OTA обновлений.

Конечно же, можно использовать локальный сервер плюс какой-нибудь канал VPN – но этот способ сложнее в использовании, да и подходит далеко не во всех случаях.

В данной статье рассмотрен процесс с использованием внешнего сервера (хостинга) с использованием защищенных соединений.


Подготовка разметки 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 выглядит достаточно просто:

// Настраиваем HTTPS
esp_http_client_config_t cfgHTTPS;
memset(&cfgHTTPS, 0, sizeof(cfgHTTPS));
// Ссылка на BIN-файл с обновлением
cfgHTTPS.url = otaSource;
cfgHTTPS.skip_cert_common_name_check = false;
// Указатель на TLS-сертификат
cfgHTTPS.use_global_ca_store = false;
cfgHTTPS.cert_pem = ota_pem_start;

// Настраиваем OTA (только для ESP-IDF >= 5.0.0)
#if ESP_IDF_VERSION_MAJOR >= 5
  esp_https_ota_config_t cfgOTA;
  memset(&cfgOTA, 0, sizeof(cfgOTA));
  cfgOTA.http_config = &cfgHTTPS;
#endif // ESP_IDF_VERSION_MAJOR

// Несколько попыток обновления
uint8_t tryUpdate = 0;
esp_err_t err = ESP_OK;
do {
  tryUpdate++;
  ESP_LOGI(logTAG, "Start of firmware upgrade from \"%s\", attempt %d", otaSource, tryUpdate);
  
  // Запускаем обновление
  #if ESP_IDF_VERSION_MAJOR < 5
    err = esp_https_ota(&cfgHTTPS);
  #else
    err = esp_https_ota(&cfgOTA);
  #endif // ESP_IDF_VERSION_MAJOR

  // Проверяем результат
  if (err == ESP_OK) {
    ESP_LOGI(logTAG, "Firmware upgrade completed!");
    esp_restart();
  } else {
    ESP_LOGE(logTAG, "Firmware upgrade failed: %d!", err);
    vTaskDelay(pdMS_TO_TICKS(1000));
  };
} while ((err != ESP_OK) && (tryUpdate < CONFIG_OTA_ATTEMPTS));

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().

Enjoy…

Если мой пример показался вам чересчур сложным, взгляните на пример из справки, тут всё гораздо более просто:

esp_err_t do_firmware_upgrade()
{
    esp_http_client_config_t config = {
        .url = CONFIG_FIRMWARE_UPGRADE_URL,
        .cert_pem = (char *)server_cert_pem_start,
    };
    esp_https_ota_config_t ota_config = {
        .http_config = &config,
    };
    esp_err_t ret = esp_https_ota(&ota_config);
    if (ret == ESP_OK) {
        esp_restart();
    } else {
        return ESP_FAIL;
    }
    return ESP_OK;
}

Конечно, 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:

Как это работает?

  1. После включения этой опции в момент записи новой прошивки на flash раздел, в который она была записана, помечается как активный, но не подтвержденный.
  2. После обновления и перезапуска вы должны подтвердить нормальный запуск путем вызова функции esp_ota_mark_app_valid_cancel_rollback(). Если этого не сделать, то при ещё одной перезагрузке (или при потере питания и следующем включении) загрузчик пометит новый раздел как неисправный и вернется к предыдущему разделу. Сделать это нужно как можно скорее, но после того, как вы удостоверились, что все работает. Я делаю это после того, как устройство успешно подключилось к WiFi и MQTT-серверу.
  3. Если вы вдруг “поняли”, что что-то пошло не так, то можно вызвать досрочный откат прошивки, вызвав другую API функцию esp_ota_mark_app_invalid_rollback_and_reboot(), которая принудительно помечает текущий ota раздел как неисправный и выполняет перезагрузку. Можно сделать это по таймеру, который запускается автоматически после того, как устройство было перезагружено с обновлением прошивки.

Вот таким нехитрым способом можно выполнить откат, и он меня неоднократно спасал от моих же собственных ошибок.

Только не забудьте, что включить эту опцию удаленно через собственно OTA не возможно, так как эта опция влияет в основном на загрузчик, а при ОТА-обновлениях загрузчик не перезаписывается. Поэтому если вы захотите это сделать позже – таки придется подключаться кабелем.

Жаль только, что не всегда удается сделать программный сброс при проблемах с загрузкой после обновления и изредка все-таки приходится перегружать неудачно обновившееся устройство по питанию. Но и в этом случае откат прошивки успешно работает. И это гораздо проще, чем перепрошивать устройство кабелем.

Но иногда даже этот защитный механизм не спасает от собственной глупости.

Поэтому главное правило при ОТА-обновлениях – вначале тщательно проверяйте прошивку на тестовых устройствах. Затем на самых малоценных устройствах. И только в последнюю очередь обновляйте критично важные и удаленные устройства.

Соблюдение данного правила сбережет вам силы, время и нервы.

 


Ссылки

  1. ESP-IDF :: Таблица разделов
  2. ESP-IDF :: ESP HTTPS OTA
  3. reOTA :: задача-библиотека для обновления

 

💠 Полный архив статей вы найдете здесь

 


Пожалуйста, оцените статью:
[ 0 из 5, всего 0 оценок ]

5 комментариев для “OTA обновления прошивки “по воздуху” для ESP32 и платформы ESP-IDF”

  1. Вячеслав

    Спасибо за хорошие статьи! Очень хотелось бы статью про OTA на ESP8266 и Platformio.

  2. Владимир

    Спасибо за статью. А не подскажите как сгенерировать и как использовать сертификат и ключ для HTTPS соединения? Пробую как описано в примере …examples\system\ota\simple_ota_example. Получаю ошибку от сервера (example_test.py) ssl.SSLError: [X509: KEY_VALUES_MISMATCH] key values mismatch (_ssl.c:4043). Понимаю что надо сгенерировать сертификат и ключ как описано тут https://github.com/espressif/esp-idf/tree/v5.0.2/examples/system/ota но ума не хватает. Не понятно какой программой это генерируется и нельзя ли попроще?

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *