ESP32: обновление по радиоканалу (OTA) |
![]() |
Добавил(а) microsin | ||||||||||||||||||||||||||||||||||||||||||||||||||||
Механизм 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. На загрузку этого приложения нет ограничений. Замечание: состояние записывается не в двоичный образ приложения, а в раздел данных otadata. Этот раздел содержит счетчик ota_seq, который указывает на слот (ota_0, ota_1, ...), из которого будет выбрано приложение для загрузки. Состояния App OTA. В таблице ниже перечислены состояния, которые выбирают загружаемое приложение. Таблица 1. Состояния приложения (App OTA State), которые используются для обновления и отката.
Если опция 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. Это состояние означает, что приложение новое, и его поведение должно отслеживаться при первой загрузке. Неожиданный сброс. Если во время первой загрузки произойдет пропадание питания или неожиданный сбой, то произойдет откат приложения. Поэтому рекомендуется провести процедуру самотестирования как можно быстрее, чтобы предотвратить откат из-за пропадания питания. Откат может произойти только на разделах OTA. Раздел factory не будет подвергаться откату. Загрузка ошибочных/сбойных приложений. Возможна загрузка приложения, которое было ранее помечено как ESP_OTA_IMG_INVALID или ESP_OTA_IMG_ABORTED: • Определите раздел последнего ошибочного (invalid/aborted) приложения вызовом esp_ota_get_last_invalid_partition(). Чтобы определить, должно ли быть запущено самотестирование во время запуска (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_INVALID. Установится функцией esp_ota_mark_app_invalid_rollback_and_reboot(). 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 устранены некоторые уязвимости. Рекомендация: если Вы хотите избежать накладных расходов на 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(); } image_header_was_checked = true; esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle); } } esp_ota_write( update_handle, (const void *)ota_write_data, data_read); } } Ограничения: • Количество бит в поле secure_version ограничено, их всего 32. Это значит, что можно только 32 раза делать anti-rollback. Можно уменьшить длину этого поля efuse с помощью опции CONFIG_BOOTLOADER_APP_SEC_VER_SIZE_EFUSE_FIELD. Версия безопасности (security_version): • В образе приложения версия безопасности сохраняется в структуре esp_app_desc. Число установлено в значение CONFIG_BOOTLOADER_APP_SECURE_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 содержит данные, их какого раздела загружать приложение (например ota_0 или ota_1), подробности см. в [3]. Раздел OTA это хранилище образа данных приложения (ota_0, ota_1, ...). Эта утилита может либо импортироваться и использоваться из другого скрипта Python, либо запускаться из скрипта командной строки (shell script) для пользователей, которые выполнять эти операции программно. Вышеупомянутые действия упрощаются либо с помощью Python API утилиты, либо с помощью интерфейса командной строки соответственно. Python API. Прежде всего убедитесь, что был импортирован модуль otatool. import sys import os # Получение значение пути до рабочего окружения 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 from otatool import * # импорт всех имен в модуле otatool Стартовой точкой для использования Python API утилиты otatool.py будет создание объекта OtatoolTarget: # Создание partool.py целевого устройства, подключенного к последовательному
# порту /dev/ttyUSB1 target = OtatoolTarget("/dev/ttyUSB1") Созданный объект можно теперь использовать для выполнения операций на целевом устройстве (target device): # Стирание otadata, сброс устройства на factory app: target.erase_otadata() # Стирание содержимого OTA-раздела 0 приложения (OTA app slot 0): target.erase_ota_partition(0) # Переключение на загрузку с OTA-раздела 1: target.switch_ota_partition(1) # Чтение OTA-раздела ota_3 и сохранение его содержимого в файл ota_3.bin: target.read_ota_partition("ota_3", "ota_3.bin") Раздел OTA, с которым производится действие, задается либо по номеру слота приложения (app slot), либо по имени раздела. Дополнительную информацию по Python API можно получить комментариев в теле утилиты (otatool.py). Интерфейс командной строки. Утилита otatool.py имеет следующую структуру команд: otatool.py [command-args] [subcommand] [subcommand-args] command-args - эти аргументы необходимы для выполнения основной команды (parttool.py), в основном относится к target device. Примеры: # Стирание otadata, сброс устройства в состояние загрузки factory app: otatool.py --port "/dev/ttyUSB1" erase_otadata # Стирание содержимого OTA app slot 0: otatool.py --port "/dev/ttyUSB1" erase_ota_partition --slot 0 # Переключение раздела загрузки на app slot 1: otatool.py --port "/dev/ttyUSB1" switch_ota_partition --slot 1 # Чтение OTA-раздела ota_3 и сохранение его содержимого в файл ota_3.bin: otatool.py --port "/dev/ttyUSB1" read_ota_partition --name=ota_3 --output=ota_3.bin Дополнительную информацию можно получить указанием аргумента –help: # Отображений возможных субкоманд и описаний аргументов основной команды: otatool.py --help # Показать описания аргументов определенной субкоманды: otatool.py [subcommand] --help Сквозной пример OTA-обновления firmware см. по ссылке [8]. При запуске otatool.py без опций командной строки выводится следующий текст подсказки: $ otatool.py usage: ESP-IDF OTA Partitions Tool [-h] [--quiet] [--esptool-args ESPTOOL_ARGS [ESPTOOL_ARGS ...]] [--esptool-write-args ESPTOOL_WRITE_ARGS [ESPTOOL_WRITE_ARGS ...]] [--esptool-read-args ESPTOOL_READ_ARGS [ESPTOOL_READ_ARGS ...]] [--esptool-erase-args ESPTOOL_ERASE_ARGS [ESPTOOL_ERASE_ARGS ...]] [--port PORT] [--baud BAUD] [--partition-table-offset PARTITION_TABLE_OFFSET] [--partition-table-file PARTITION_TABLE_FILE] {read_otadata,erase_otadata,switch_ota_partition,read_ota_partition, write_ota_partition,erase_ota_partition} ... Позиционные аргументы: {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), откуда будет прочитана таблица разделов; если опция определена, то переопределяет устройство, подключенное к указанному порту, в качестве источника таблицы разделов [Справочник по API-функциям OTA] Заголовочный файл: components/app_update/include/esp_ota_ops.h. Таблица 2. Общее описание функций OTA.
Примечание: полное описание макросов, типов данных, API-функций OTA и их параметров см. в документации [1]. [Алгоритм отладки OTA] Рис. 1. Как искать источник проблем при отказах OTA. [Словарик] OTA Over The Air, технология загрузки (обновления) по радиоканалу. [Ссылки] 1. ESP32 Over The Air Updates (OTA) site:docs.espressif.com. |