Коротко: в unified firmware ExpressLRS 4.1 конфигурационный блок действительно добавляется после ESP firmware image, но его начало нельзя задавать одним вечным абсолютным offset. ExpressLRS сначала вычисляет конец конкретного image, выравнивает позицию, а затем записывает product name, device/Lua name, defines и hardware layout.

Проверено по исходному коду: ExpressLRS tag 4.1.0, commit release a9d4a9c, файл src/python/UnifiedConfiguration.py. Структура ниже относится только к firmware, для которого этот unified layout применим.

Это не инструкция по редактированию машинного кода. Не применяйте материал к SPI RX, STM32 legacy firmware, неизвестным targets или произвольным бинарникам. Перед любой записью должен быть известен exact target и рабочий recovery path.

Почему «фиксированный offset от начала файла» неверен

В ExpressLRS 4.1 функция findFirmwareEnd() разбирает ESP image и вычисляет его реальный конец. Алгоритм проверяет magic byte 0xE9, учитывает layout ESP8266/ESP8285, проходит по firmware segments, затем выравнивает итоговую позицию.

Упрощённо логика такая:

  1. прочитать ESP image header;
  2. проверить magic 0xE9;
  3. определить особый путь ESP8266/8285 по структуре segments;
  4. пройти все segments по их размерам;
  5. выровнять позицию: (pos + 16) & ~15;
  6. для non-8285 добавить ещё 32 bytes;
  7. только после этого начинается unified configuration.
Следствие: два корректных firmware.bin могут иметь unified block по разным абсолютным адресам. Надёжный инструмент должен сначала воспроизвести логику поиска конца image, а не писать по hard-coded offset.

Структура unified block в ExpressLRS 4.1.0

Пусть BASE — позиция, возвращённая логикой, эквивалентной findFirmwareEnd(). Тогда базовые поля идут последовательно:

BASE + 0product_name
128 bytes
BASE + 128lua/device name
16 bytes
BASE + 144defines
512 bytes
BASE + 656hardware layout
2048 bytes
Поле Диапазон относительно BASE Размер Формат
Product name 0…127 128 B UTF-8 bytes, NUL-padded/truncated
Device/Lua name 128…143 16 B UTF-8 bytes, NUL-padded/truncated
Defines/options 144…655 512 B encoded configuration, NUL-padded
Hardware layout 656…2703 2048 B JSON layout, NUL-padded

После 2704 bytes блок может продолжаться

Нельзя считать BASE + 2704 обязательным концом конфигурации. Код ExpressLRS 4.1.0 после hardware layout может дописывать дополнительные данные:

  • logo data, если target использует logo_file;
  • marker BE EF CA FE и prior_target_name, если эта информация присутствует в target config.

Поэтому инструмент, который просто «обрезает всё после 2704 bytes», потенциально портит firmware.

Что допустимо редактировать

Безопасный подход — менять только те поля unified configuration, структура которых подтверждена для exact release и target class. Например, текстовое имя или JSON layout можно анализировать отдельно от машинного кода.

Недопустимо:

  • искать красивую ASCII-строку и предполагать, что рядом всегда находится тот же layout;
  • записывать данные длиннее выделенного поля;
  • сдвигать последующие bytes;
  • менять ESP segments или executable code без отдельного анализа;
  • применять offsets 4.1.0 к другой major/release без проверки source.

Минимальная процедура безопасной проверки

  1. Сделайте копию исходного firmware.bin.
  2. Посчитайте SHA-256 исходного файла.
  3. Подтвердите exact target и release.
  4. Вычислите BASE из ESP image structure.
  5. Прочитайте product/device/defines/layout без записи.
  6. Убедитесь, что JSON fields декодируются и соответствуют ожидаемому hardware.
  7. Изменяйте только разрешённое поле, сохраняя его фиксированный размер.
  8. Снова проверьте JSON/UTF-8.
  9. Сделайте binary diff исходного и candidate.
  10. Убедитесь, что изменились только ожидаемые bytes внутри разрешённого range.
  11. Сохраните SHA-256 candidate.
  12. Прошивайте только устройство с известным recovery path.

Каким должен быть автоматический тест инструмента

Если на сайте используется HEX/firmware parser, он должен иметь fixture из известной сборки ExpressLRS 4.1.0 и автоматически проверять:

  • magic/header распознан;
  • BASE вычислен корректно;
  • product name читается;
  • device name читается;
  • defines помещаются в 512 B;
  • hardware JSON помещается в 2048 B и валиден;
  • patch не меняет длину базовых фиксированных slots;
  • diff не выходит за разрешённые ranges;
  • machine-code segments остаются неизменными.
Если такой fixture/test не проходит для текущего release, инструмент не должен предлагать пользователю запись. Лучше остановить операцию и отправить пользователя к обычному hardware.json/recovery workflow.

Платформенные ограничения

Платформа Применимость этой статьи
Unified ESP8285 / ESP8266 class Да, при подтверждённом target/layout
Unified ESP32 class Да, при подтверждённом target/layout
SPI RX в flight controller Нет — это часть firmware FC
STM32 legacy ELRS Нет для 4.x
Unknown firmware.bin Нет

Почему hardware.json обычно лучше HEX

Если устройство загружается и WebUI/обычный configuration path доступен, используйте штатный hardware.json workflow. Binary patching нужен только когда есть конкретная причина работать с unified block и понятен путь восстановления.

Перед major upgrade 3.x → 4.x учитывайте переход SPIFFS → LittleFS: пользовательские изменения hardware/options нужно сохранить заранее.

Связанные материалы

Источник структуры

ExpressLRS 4.1.0 — UnifiedConfiguration.py. Для claims этой статьи использовать именно tag 4.1.0, а не меняющуюся ветку master.

Прокрутить вверх
На связи