Механизм OTA позволяет устройству ESP32 по радиоканалу (например через Wi-Fi или Bluetooth) обновить саму себя (собственное программное обеспечение, firmware) на основе данных, полученных во время работы текущей версии firmware.
OTA требует конфигурирования таблицы разделов на устройстве с как минимум двумя разделами "OTA app slot" (например ota_0 и ota_1) и разделом данных OTA "OTA Data Partition" (см. [2]).
Рабочие функции OTA записывают новый образ приложения firmware в любой слотов OTA app, который в настоящее время не используется для загрузки. Как только новый записанный образ был проверен, раздел данных OTA обновляется, чтобы указать что этот новый раздел должен использоваться при следующей загрузке.
[Раздел OTA Data]
Раздел данных OTA (который имеет тип data и подтип ota) должен быть включен в таблицу разделов любого проекта, который использует функции OTA.
Для заводских настроек загрузки раздел данных OTA не должен содержать никаких данных (все его байты очищены и находятся в состоянии 0xFF). В этом случае программный загрузчик ESP-IDF загрузит приложение из раздела factory app, если он присутствует в таблице разделов. Если в таблице разделов нет раздела factory app, или в него не записано приложение, то будет загружено приложение из первого доступного слота OTA (обычно ota_0).
Примечание: под программным загрузчиком понимается загрузчик второй стадии, исходный код и проект которого можно найти среди примеров ESP-IDF (подробнее см. [3]). После первого обновления OTA раздел данных OTA обновится, чтобы указать, какой из разделов OTA app slot должен использоваться для следующей загрузки.
Раздел данных OTA занимает два сектора SPI flash (0x2000 байт), чтобы защититься от проблем с пропаданием питания в момент их записи. Эти секторы независимо друг от друга стираются и записываются одинаковыми данными, и если они не совпадают, то поле счетчика используется для определения того, какой сектор был записан последним (см. описание mcuboot [9]).
[Откат версии приложения]
Откат версии приложения (app rollback) это возврат на предыдущую версию приложения. Основное назначение отката - сохранить устройство работоспособным после обновления. Эта функция позволяет вернуть обратно предыдущую рабочую версию приложения в случае, когда в новом приложении обнаружились критические ошибки. Когда разрешен процесс отката, и обновление OTA предоставляет новую версию приложения, могут произойти три вещи:
• Приложение работает нормально, функция esp_ota_mark_app_valid_cancel_rollback() помечает работающее приложение состоянием ESP_OTA_IMG_VALID. На загрузку этого приложения нет ограничений. • Приложение имеет критические ошибки, и его дальнейшая работа невозможна. Требуется откат на предыдущее приложение, тогда функция esp_ota_mark_app_invalid_rollback_and_reboot() помечает работающее приложение состоянием ESP_OTA_IMG_INVALID и сбрасывает устройство. Это сбойное приложение не будет выбрано загрузчиком, и он загрузит рабочее приложение предыдущей версии. • Если установлена опция CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE, и произошел сброс без вызова любой из этих функций, то произойдет откат приложения.
Замечание: состояние записывается не в двоичный образ приложения, а в раздел данных otadata. Этот раздел содержит счетчик ota_seq, который указывает на слот (ota_0, ota_1, ...), из которого будет выбрано приложение для загрузки.
Состояния App OTA. В таблице ниже перечислены состояния, которые выбирают загружаемое приложение.
Таблица 1. Состояния приложения (App OTA State), которые используются для обновления и отката.
Состояние
Ограничения для bootloader в выборе загружаемого приложения
ESP_OTA_IMG_VALID
Нет ограничения. Приложение будет выбрано для загрузки.
ESP_OTA_IMG_UNDEFINED
ESP_OTA_IMG_INVALID
Приложение не будет выбрано.
ESP_OTA_IMG_ABORTED
ESP_OTA_IMG_NEW
Если установлена опция CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE, то это приложение будет выбрано только один раз. Загрузчик немедленно поменяет состояние на ESP_OTA_IMG_PENDING_VERIFY.
ESP_OTA_IMG_PENDING_VERIFY
Если установлена опция CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE, то это приложение не будет выбрано для загрузки, и его состояние поменяется на ESP_OTA_IMG_ABORTED.
Если опция CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE не разрешена (по умолчанию), то использование функций esp_ota_mark_app_valid_cancel_rollback() и esp_ota_mark_app_invalid_rollback_and_reboot() не является обязательным, и состояния ESP_OTA_IMG_NEW и ESP_OTA_IMG_PENDING_VERIFY не используются.
Kconfig-опция CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE позволяет отслеживать первую загрузку нового приложения. В этом случае приложение должно подтвердить свою работоспособность путем вызова функции esp_ota_mark_app_valid_cancel_rollback(), иначе после перезагрузки произойдет откат. Эта опция позволит Вам управлять работоспособностью приложения на этапе загрузки. Таким образом, новое приложение получит только одну попытку успешной загрузки.
Процесс отката. Ниже приведено описание того, что происходит при откате приложения, когда разрешена опция CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE:
• Новое приложение было успешно загружено, функция esp_ota_set_boot_partition() делает его раздел загружаемым и устанавливает состояние ESP_OTA_IMG_NEW. Это состояние означает, что приложение новое, и его поведение должно отслеживаться при первой загрузке. • Перезагрузка вызовом функции esp_restart(). • Загрузчик проверяет, установлено ли состояние ESP_OTA_IMG_PENDING_VERIFY, и если оно установлено, то поменяет его на ESP_OTA_IMG_ABORTED. • Загрузчик выберет новое приложение для загрузки, чтобы состояние не было установлено в ESP_OTA_IMG_INVALID или ESP_OTA_IMG_ABORTED. • Загрузчик проверяет выбранное приложение, установлено ли у него состояние ESP_OTA_IMG_NEW, и если оно установлено, то запишет его в значение ESP_OTA_IMG_PENDING_VERIFY. Это состояние означает, что приложению нужно подтвердить свою работоспособность. Если это не произошло, и произойдет перезагрузка, то состояние ESP_OTA_IMG_PENDING_VERIFY перезапишется в ESP_OTA_IMG_ABORTED (см. выше), и это приложение больше не сможет загрузиться, т. е. произойдет откат на предыдущее рабочее приложение. • Запустится новое приложение, и оно должно выполнить самотестирование. • Если самотестирование прошло успешно, то Вы должны вызвать функцию esp_ota_mark_app_valid_cancel_rollback(), потому что приложение ожидает подтверждения своей работоспособности (состояние ESP_OTA_IMG_PENDING_VERIFY). • Если самотестирование не прошло, то вызов функции esp_ota_mark_app_invalid_rollback_and_reboot() произведет откат к предыдущей рабочей версии приложения, в то время как ошибочное приложение помечается состоянием ESP_OTA_IMG_INVALID. • Если приложение не было подтверждено, то состояние остается ESP_OTA_IMG_PENDING_VERIFY, и следующая загрузка поменяет состояние на ESP_OTA_IMG_ABORTED, что предотвратит повторную загрузку этого приложения. В результате произойдет откат к предыдущему рабочему приложению.
Неожиданный сброс. Если во время первой загрузки произойдет пропадание питания или неожиданный сбой, то произойдет откат приложения. Поэтому рекомендуется провести процедуру самотестирования как можно быстрее, чтобы предотвратить откат из-за пропадания питания.
Откат может произойти только на разделах OTA. Раздел factory не будет подвергаться откату.
Загрузка ошибочных/сбойных приложений. Возможна загрузка приложения, которое было ранее помечено как ESP_OTA_IMG_INVALID или ESP_OTA_IMG_ABORTED:
• Определите раздел последнего ошибочного (invalid/aborted) приложения вызовом esp_ota_get_last_invalid_partition(). • Передайте полученный раздел в функцию esp_ota_set_boot_partition(), это обновит otadata. • Перезапустите устройство вызовом esp_restart(). Bootloader загрузит указанное приложение.
Чтобы определить, должно ли быть запущено самотестирование во время запуска (startup) приложения, вызовите функцию esp_ota_get_state_partition(). Если её результат будет ESP_OTA_IMG_PENDING_VERIFY, то требуется самотестирование и последующее подтверждение работоспособности приложения.
Где устанавливаются состояния. Краткое описание, в каком месте устанавливаются состояния:
ESP_OTA_IMG_VALID. Это состояние устанавливается функцией esp_ota_mark_app_valid_cancel_rollback().
ESP_OTA_IMG_UNDEFINED. Это состояние установится функцией esp_ota_set_boot_partition(), если не была разрешена опция CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE.
ESP_OTA_IMG_NEW. Установится функцией esp_ota_set_boot_partition(), если была разрешена опция CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE.
ESP_OTA_IMG_ABORTED. Установится, если не было подтверждения о работоспособности приложения, и произошла перезагрузка (если разрешена опция CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE).
ESP_OTA_IMG_PENDING_VERIFY. Установится, в загрузчике, если разрешена опция CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE, и у выбранного приложения состояние ESP_OTA_IMG_NEW.
[Anti-rollback]
Антиоткат (anti-rollback) предотвращает откат приложения с версией безопасности ниже, чем запрограммировано в eFuse чипа.
Эта функция работает, если установлена опция CONFIG_BOOTLOADER_APP_ANTI_ROLLBACK. В загрузчике, когда он выбирает загружаемое приложение, добавляется дополнительная проверка версии безопасности, которая установлена на чипе и в образе приложения. Версия безопасности в загружаемом firmware должна быть больше или равной версии в чипе.
Опции CONFIG_BOOTLOADER_APP_ANTI_ROLLBACK и CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE используют совместно. В этом случае откат возможен только на версии безопасности, которая равна или выше, чем версия в чипе.
Типовая схема anti-rollback:
• Выпущен релиз нового firmware с предыдущей версией безопасности. В этом новом firmware устранены некоторые уязвимости. • После того, как разработчик убедился, что это firmware рабочее, он может увеличить версию безопасности и выпустить новый релиз firmware. • Загружается новое приложение. • Чтобы его выбирал загрузчик, запускается функция esp_ota_set_boot_partition(). Если версия безопасности нового приложения меньше, чем версия чипа, то новое приложение будет стерто. Обновление на новое firmware невозможно. • Перезагрузка. • В загрузчике будет выбрано приложение, версия безопасности которого больше или равна версии чипа. Если otadata находится в начальном состоянии, и одно firmware было загружено через канал serial, и у этой прошивки версия безопасности выше, чем у чипа, то версия безопасности efuse немедленно будет обновлена в загрузчике. • Загрузится новое приложение. Затем приложение должно выполнить действия по диагностике своей работоспособности, и если проверки выполнены полностью, то Вы должны вызвать функцию esp_ota_mark_app_valid_cancel_rollback(), чтобы пометить приложение состоянием ESP_OTA_IMG_VALID, и обновить версию безопасности на чипе. Обратите внимание, что если была вызвана функция esp_ota_mark_app_invalid_rollback_and_reboot(), то rollback может не произойти, поскольку в устройстве может не быть любого загружаемого приложения. Затем будет возвращена ошибка ESP_ERR_OTA_ROLLBACK_FAILED, и приложение останется в состоянии ESP_OTA_IMG_PENDING_VERIFY. • Следующее обновление приложение возможно, если работающее приложение в состоянии ESP_OTA_IMG_VALID.
Рекомендация: если Вы хотите избежать накладных расходов на download/erase в случае, когда у приложения на сервере версия безопасности ниже, чем у работающего приложения, то нужно получить значение new_app_info.secure_version из первого пакета образа, и сравнить его с версией безопасности efuse. Используйте функцию esp_efuse_check_secure_version (new_app_info.secure_version), и если все в порядке, то продолжайте загрузку, иначе отмените процесс загрузки.
...
bool image_header_was_checked =false;
while (1)
{
int data_read = esp_http_client_read(client, ota_write_data, BUFFSIZE);
...
if (data_read >0)
{
if (image_header_was_checked ==false)
{
esp_app_desc_t new_app_info;
if (data_read >sizeof(esp_image_header_t)
+sizeof(esp_image_segment_header_t)
+sizeof(esp_app_desc_t))
{
// сравнение текущей версии с загружаемойif (esp_efuse_check_secure_version(new_app_info.secure_version) ==false)
{
ESP_LOGE(TAG, "Это новое приложение не может быть загружено, потому что");
ESP_LOGE(TAG, " secure version у него меньше, чем сохранено в efuse.");
http_cleanup(client);
task_fatal_error();
}
• Количество бит в поле secure_version ограничено, их всего 32. Это значит, что можно только 32 раза делать anti-rollback. Можно уменьшить длину этого поля efuse с помощью опции CONFIG_BOOTLOADER_APP_SEC_VER_SIZE_EFUSE_FIELD. • Anti-rollback работает только если схема кодирования efuse установлена в NONE (подробнее про схемы кодирования см. [4]). • Разделы Factory и Test в схеме anti-rollback не поддерживаются, поскольку нет раздела с SubType, установленным в factory или test.
Версия безопасности (security_version):
• В образе приложения версия безопасности сохраняется в структуре esp_app_desc. Число установлено в значение CONFIG_BOOTLOADER_APP_SECURE_VERSION. • В ESP32 версия безопасности сохранена в EFUSE_BLK3_RDATA4_REG (когда бит eFuse запрограммирован в 1, его нельзя будет вернуть обратно в 0). Количество бит, установленных в этом регистре является значением security_version из приложения.
[Обновления Secure OTA без Secure boot]
Верификация подписанных обновлений OTA может быть выполнена даже без разрешения аппаратной поддержки безопасной загрузки (hardware secure boot). Этого можно достичь установкой CONFIG_SECURE_SIGNED_APPS_NO_SECURE_BOOT и CONFIG_SECURE_SIGNED_ON_UPDATE_NO_SECURE_BOOT. Дополнительную информацию см. в разделе "Проверка подписи приложения без Hardware Secure Boot" статьи [5].
[OTA Tool (otatool.py)]
Компонент app_update предоставляет утилиту otatool.py (скрипт Python) для выполнения операций, связанных с разделами OTA на целевом устройстве. С помощью этого инструмента могут быть выполнены следующие операции:
• Чтение содержимого раздела otadata (read_otadata). • Стирание раздела otadata, что эффективно сбросит устройство к состоянию заводского приложения factory app (erase_otadata). • Переключение разделов OTA (switch_ota_partition). • Стирание раздела OTA (erase_ota_partition). • Запись в раздел OTA (write_ota_partition). • Чтение содержимого раздела OTA (read_ota_partition).
Примечание: раздел otadata содержит данные, их какого раздела загружать приложение (например ota_0 или ota_1), подробности см. в [3]. Раздел OTA это хранилище образа данных приложения (ota_0, ota_1, ...).
Эта утилита может либо импортироваться и использоваться из другого скрипта Python, либо запускаться из скрипта командной строки (shell script) для пользователей, которые выполнять эти операции программно. Вышеупомянутые действия упрощаются либо с помощью Python API утилиты, либо с помощью интерфейса командной строки соответственно.
Python API. Прежде всего убедитесь, что был импортирован модуль otatool.
importsys
importos
# Получение значение пути до рабочего окружения ESP-IDF:
idf_path = os.environ["IDF_PATH"]
# otatool.py находится в каталоге $IDF_PATH/components/app_update
otatool_dir = os.path.join(idf_path, "components", "app_update")
sys.path.append(otatool_dir) # позволит для Python найти модуль otatool
fromotatoolimport*# импорт всех имен в модуле otatool
Стартовой точкой для использования Python API утилиты otatool.py будет создание объекта OtatoolTarget:
# Создание partool.py целевого устройства, подключенного к последовательному
command-args - эти аргументы необходимы для выполнения основной команды (parttool.py), в основном относится к target device. subcommand - указывается выполняемая операция. subcommand-args - эти аргументы специфичны для выбранной операции.
Примеры:
# Стирание otadata, сброс устройства в состояние загрузки factory app:
otatool.py --port "/dev/ttyUSB1" erase_otadata
Позиционные аргументы:
{read_otadata,erase_otadata,switch_ota_partition,read_ota_partition,write_ota_partition,erase_ota_partition}
запустите otatool -h для дополнительной подсказки
read_otadata чтение раздела otadata
erase_otadata стирание раздела otadata
switch_ota_partition переключить раздел otadata
read_ota_partition прочитать содержимое раздела ota
write_ota_partition записать содержимое раздела ota
erase_ota_partition стереть содержимое раздела ota
Опциональные аргументы:
-h, --help отобразить этот текст подсказки и выйти
--quiet, -q подавляет сообщения stderr
--esptool-args ESPTOOL_ARGS [ESPTOOL_ARGS ...]
дополнительные основные аргументы для esptool
--esptool-write-args ESPTOOL_WRITE_ARGS [ESPTOOL_WRITE_ARGS ...]
дополнительные аргументы субкоманды для esptool write_flash
--esptool-read-args ESPTOOL_READ_ARGS [ESPTOOL_READ_ARGS ...]
дополнительные аргументы субкоманды для esptool read_flash
--esptool-erase-args ESPTOOL_ERASE_ARGS [ESPTOOL_ERASE_ARGS ...]
дополнительные аргументы субкоманды для esptool erase_region
--port PORT, -p PORT порт для подключения к устройству, откуда читается таблица разделов
--baud BAUD, -b BAUD используемая скорость порта подключения
--partition-table-offset PARTITION_TABLE_OFFSET, -o PARTITION_TABLE_OFFSET
смещение, откуда считывается таблица разделов
--partition-table-file PARTITION_TABLE_FILE, -f PARTITION_TABLE_FILE
файл (CSV/binary), откуда будет прочитана таблица разделов; если опция определена,
то переопределяет устройство, подключенное к указанному порту, в качестве
источника таблицы разделов
Начинает запись обновления OTA в указанный раздел. Этот раздел будет стерт до указанного размера записываемого образа.
esp_ota_write
Запись в раздел данных обновления OTA. Эта функция может быть вызвана несколько раз по мере получения данных OTA. Данные записываются в раздел последовательными порциями.
esp_ota_write_with_offset
Запись в раздел данных обновления OTA с указанным смещением. Эта функция может быть вызвана для не упорядоченной записи данных в раздел. Если разрешено шифрование flash, то размер записываемых блоков данных должен быть нацело выровнен на 16 байт.
esp_ota_end
Завершает обновление OTA и проверяет только что записанный образ приложения.
esp_ota_abort
Обрывает процесс обновления OTA с освобождением дескриптора OTA и связанной с ним памяти.
sp_ota_set_boot_partition
Конфигурирует данные OTA для нового загружаемого раздела.
esp_ota_get_boot_partition
Получает информацию раздела того приложения, которое в настоящий момент сконфигурировано для загрузки.
esp_ota_get_running_partition
Получает информацию раздела работающего в настоящий момент приложения.
esp_ota_get_next_update_partition
Вернет следующий OTA-раздел приложения, который должен быть записан новым firmware.
esp_ota_get_partition_description
Возвратит структуру esp_app_desc для раздела приложения. Эта структура включает версию приложения.
esp_ota_get_app_partition_count
Вернет количество OTA-разделов, находящихся в таблице разделов flash.
esp_ota_mark_app_valid_cancel_rollback
Эта функция будет вызвана для того, что текущее запущенное приложение работает корректно.
esp_ota_mark_app_invalid_rollback_and_reboot
Эта функция вызывается для возврата (roll back) к предыдущему рабочему приложению (откат с перезагрузкой).
esp_ota_get_last_invalid_partition
Вернет последний раздел с ошибочным состоянием (ESP_OTA_IMG_INVALID или ESP_OTA_IMG_ABORTED).
esp_ota_get_state_partition
Возвратит состояние указанного раздела.
esp_ota_erase_last_boot_app_partition
Сотрет раздел предыдущего загружаемого приложения и соответствующие данные otadata для выбора этого раздела при загрузке.
esp_ota_check_rollback_is_possible
Проверка приложений на слотах, которые могут быть загружены в случае отката.
Примечание: полное описание макросов, типов данных, API-функций OTA и их параметров см. в документации [1].
[Алгоритм отладки OTA]
Рис. 1. Как искать источник проблем при отказах OTA.
[Словарик]
OTA Over The Air, технология загрузки (обновления) по радиоканалу.