Перейти к содержанию

Howto: обновить прошивку (OTA)

Контроллер использует двухсекционную OTA: новый образ пишется в app1 (если активен app0) и наоборот. После апплая bootloader пробует загрузиться с новой секции. Если boot-validation падает — автоматический rollback на предыдущую рабочую версию.

1. Подготовить артефакты

После pio run в .pio/build/esp32dev/:

  • firmware.bin — сам бинарь (~1.5 МБ)
  • manifest.json — метаданные (генерируется tools/export_artifacts.py)

Манифест содержит fw_version, sha256, total_bytes, product_id, board_id, hw_rev, expected_eeprom_schema. Подробности — в src/ota/FirmwareManifest.cpp.

2. Обновление через CLI

DEV=http://awdc.local
ADMIN=admin:NEW_ADMIN_PASSWORD

# 1. Передать манифест → менеджер валидирует совместимость
curl -u $ADMIN -X POST \
     -H "Content-Type: application/json" \
     --data-binary @manifest.json \
     $DEV/api/ota/prepare

# 2. Залить бинарь (raw bytes). На медленной сети может занять минуту-две
curl -u $ADMIN -X POST \
     -H "Content-Type: application/octet-stream" \
     --data-binary @firmware.bin \
     $DEV/api/ota/upload

# 3. Применить → устройство перезагрузится через 500 мс
curl -u $ADMIN -X POST $DEV/api/ota/apply

# (соединение оборвётся — нормально)

После reboot подождать 10…20 секунд, потом проверить:

curl -u $ADMIN $DEV/api/ota/result

Успех: {"success":true,"applied_version":"...","ota_partition":"app1"}. Если был rollback — поле success:false и rolled_back_to:"...".

3. Мониторинг прогресса

Во время заливки можно опросить статус из другого терминала:

curl -u $ADMIN $DEV/api/ota/status

Покажет state, uploaded_bytes / total_bytes, computed_sha256.

4. Отмена

Если что-то пошло не так до apply:

curl -u $ADMIN -X POST $DEV/api/ota/cancel

После cancel слот освободится, можно начинать заново с prepare.

5. Rollback policy

После apply устройство пишет флаг ota_pending=true и пробует загрузиться. На первом успешном boot: - Если probe-валидация (NVS, EEPROM, периферия) прошла — фиксируется как «good», ota_pending=false. - Иначе — bootloader при следующем reboot откатится на предыдущий раздел. Счётчик попыток b_att ограничивает loop.

Детали — src/ota/BootValidation.cpp, ADR в doc/dev/adr/adr-003-*.md.

6. Совместимость

Менеджер проверяет в prepare: - product_id — должен совпадать (AWDC) - board_id — версия платы - hw_rev — ревизия железа - expected_eeprom_schema — для миграции счётчиков

При несовпадении возвращает 400 с описанием.

7. Pull-mode: обновление с сервера обновлений

Альтернатива ручному upload — контроллер сам скачивает прошивку с https-хоста. Удобно, когда сборочная машина выкладывает свежие билды автоматически (см. §8). На контроллере:

  1. Web-UI → Система → Обновление прошивки (OTA). В шапке карточки видно бейдж сервер сконфигурирован, если URL уже задан.
  2. Раскрыть подраздел «Прошивка с сервера обновлений».
  3. В поле URL вписать https-корень хоста (напр. https://awdc.nikolaev.world/) и «Сохранить». URL пишется в NVS ota/host_url.
  4. «Проверить» → контроллер тянет <host>/firmware/index.json. После успешной проверки рядом появляется зелёная ✓ и блок: На хосте: <fw_version> · <sha> · <size> · <built_at>.
  5. Если есть более новая версия — кнопка «Скачать и подготовить». Прогресс-бар идёт от того же /api/ota/status (uploaded/total в байтах + КБ/с + ETA), что и ручной upload.
  6. По завершении state переходит в UPLOAD_DONE → общий шаг 3 «Применить» (как при ручном upload).

Эндпоинты (если предпочитаете curl):

curl -u $ADMIN -X POST $DEV/api/ota/host    -d '{"url":"https://awdc.nikolaev.world/"}'
curl -u $ADMIN -X POST $DEV/api/ota/check                   # → JSON с latest
curl -u $ADMIN -X POST $DEV/api/ota/pull    -d '{"sha":"<sha>"}'   # 202, pull-task в фоне
curl -u $ADMIN $DEV/api/ota/status                          # прогресс
curl -u $ADMIN -X POST $DEV/api/ota/apply                   # после UPLOAD_DONE

/api/ota/pull отвечает 202 сразу — pull идёт в отдельной FreeRTOS-таске, прогресс смотреть через /api/ota/status. pull_error в /api/ota/host показывает, если что-то упало (TLS handshake, HTTP code != 200, размер не совпал с manifest).

8. Auto-deploy: выкладка свежей прошивки на сервер обновлений

На сборочной машине каждый успешный pio run может автоматически заливать новые артефакты на хост обновлений. Опт-ин через deploy.env в корне репо (см. также подробный pipeline в devops.md).

8.1. Подготовить хост

Один раз на сервере обновлений:

sudo mkdir -p /var/www/awdc.nikolaev.world/firmware
sudo chown andy:www-data /var/www/awdc.nikolaev.world/firmware
sudo chmod 755 /var/www/awdc.nikolaev.world/firmware

В nginx добавлен location /firmware/ (см. max-webapp/scripts/nginx.conf.example) — JSON без кэша, .bin/.sha256 с immutable на 30 дней.

8.2. SSH-ключ для логина без пароля

deploy-firmware.sh запускается из-под pio run неинтерактивно (BatchMode=yes) — пароль набрать некуда. Нужен SSH-ключ.

Способ A (рекомендуемый)ssh-keygen в git-bash, одной командой:

ssh-keygen -t ed25519 -f /c/Users/<you>/.ssh/awdc_deploy -N "" -C "awdc-deploy"
cat /c/Users/<you>/.ssh/awdc_deploy.pub

Содержимое .pub дописать на хосте в ~/.ssh/authorized_keys нужного юзера (DEPLOY_HOST).

Способ B — PuTTYgen. Подвох: для Ed25519 рабочий пункт меню —

Conversions → Export OpenSSH key (force new file format)

а не соседний «Export OpenSSH key» без скобок — последний для Ed25519 пишет устаревший PEM-контейнер, и современный OpenSSH/libcrypto падает с:

Load key "...": error in libcrypto: unsupported
Permission denied (publickey,password).

Если уже наступили — откройте .ppk обратно в PuTTYgen и переэкспортируйте правильным пунктом. Сам публичный ключ из верхнего окошка PuTTYgen дописать в authorized_keys как в способе A.

8.3. Прописать deploy.env

В корне репо скопировать шаблон и заполнить:

cp deploy.env.example deploy.env

Минимум для прошивочного деплоя:

DEPLOY_HOST=andy@awdc.nikolaev.world
SSH_KEY=/c/Users/<you>/.ssh/awdc_deploy
DEPLOY_FIRMWARE_PATH=/var/www/awdc.nikolaev.world/firmware/
DEPLOY_FIRMWARE_CHANNEL=dev

deploy.env в .gitignore — каждая инсталляция своя. Этот же файл используется для деплоя max-webapp (см. секцию DEPLOY_WEBAPP_* в devops.md §1.3).

Путь к ключу — в POSIX-стиле (/c/Users/...), не C:\Users\...: git-bash'ный ssh так понимает Windows-пути.

8.4. Что делает скрипт

После успешного pio run PlatformIO дёргает tools/deploy_firmware_hook.py, он находит git-bash (стандартные пути Git for Windows + fallback через git --exec-path) и запускает tools/deploy-firmware.sh:

  1. Берёт последний билд из build_artifacts/<ver>/AWDC_fw-*.{bin,manifest.json,sha256}.
  2. scp всех трёх файлов на DEPLOY_HOST:DEPLOY_PATH/<sha>.{bin,manifest.json,sha256}.
  3. По ssh на хосте перегенерирует index.json со списком всех билдов и указателем current = <sha> (только что залитый).

Все ssh/scp идут через SSH_KEY с IdentitiesOnly=yes — никаких других ключей из агента не пробуется. При падении trap печатает подсказку, куда смотреть (auth, формат ключа, libcrypto-ошибка).

См. также: api/system.md#ota-обновление-прошивки, memory-map.md.