Files
viktor b5a8e92e45 DMR-данные (SMS) на TYT MD-UV380 под OpenGD77
Восстановлено из транскрипта сессии после потери рабочего дерева на сервере сборки.

Что внутри:
- четыре новых модуля прошивки (приём, сборка исходящего, меню Messages);
- правки существующих файлов в виде скриптов, с порядком применения;
- инструменты стенда: снятие экрана рации по USB, дамп кадров, эталонная сборка пакета;
- NOTES.md: что проверено в эфире, что не доделано, где грабли.

Работало в эфире в августе 2026: данные ходят в обе стороны в режиме хотспота,
рация принимает SMS и показывает на экране. Отправка собрана и сверена с эталоном
сервера побайтово, но в эфире не проверялась.

Claude-Session: https://claude.ai/code/session_01GJtvG5WwqYNe1b7s9LLtgy
2026-09-11 02:39:24 +09:00

11 KiB

DMR-данные (SMS) на TYT MD-UV380 под OpenGD77

Что это: доработка прошивки OpenGD77, которая учит рацию передавать и принимать пакетные данные DMR — текстовые сообщения. В апстриме этого нет: в официальном README прямо написано, что отправка и приём SMS не поддерживаются.

Базовый исходник — официальный релиз R20260131 (ветка MDUV380_DM1701), скачан с opengd77.com. Первый коммит в этом репозитории — он же, без единого изменения. Всё, что дальше — наши правки, так что git diff первого и второго коммита показывает ровно их.


Что работает (проверено в эфире)

Режим модема (хотспот), обе стороны. Раньше SMS через рацию-модем молча пропадали: модем подтверждал кадры хосту, а в эфир ничего не уходило. Теперь данные проходят насквозь в обе стороны. Проверено с настоящим сервером и вторым приёмником.

Приём SMS на самой рации. В обычном режиме (не модем) рация принимает сообщение из эфира, собирает его из кадров, разбирает сетевую обёртку и показывает на экране: отправитель, текст, кнопка OK.

Меню Messages в главном меню: New msg, Inbox, Outbox; у папок — список сообщений и Delete All. Хранится по 6 сообщений на папку, текст до 64 символов (ограничение оперативной памяти, см. ниже).

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

Чего нет

  • Отправка не проверена в эфире, ввод текста с клавиатуры не написан: New msg шлёт фиксированный текст, кнопка «Ответить» — короткое OK.
  • Подтверждение доставки (TMS-ACK) не отправляется: отправитель, запросивший его, покажет «не доставлено», даже если текст на экране появился.
  • Контрольные суммы принятых блоков не проверяются.
  • Кириллица не поддержана — символы вне ASCII заменяются на ?. Нужен кириллический шрифт (в проекте есть образец: отдельный набор для японского).
  • Режим rate 3/4 не декодируется (там иное кодирование). Практическое следствие: приватные сообщения от AnyTone напрямую не читаются, групповые rate 1/2 — читаются.

Как это устроено

Ключевая находка: чип HR-C6000 умеет собирать кадры данных сам — это уже использовалось прошивкой для пробуждения репитера. Нужно положить 12 байт полезной нагрузки на страницу регистров 0x02 и записать тип кадра в регистр 0x50 (значение — тип из стандарта, сдвинутый на 4 бита). Чип добавит помехоустойчивое кодирование и синхронизацию.

Приём симметричен: принятые данные читаются с той же страницы 0x02.

Новые файлы:

  • application/source/functions/sms.c + include/functions/sms.h — сборка входящих сообщений, хранилище Inbox/Outbox, очередь передачи.
  • application/source/functions/smsSend.c — построение исходящего пакета: TMS, IP/UDP, контрольная сумма сообщения, заголовок данных, преамбулы.
  • application/source/user_interface/menuMessages.c — экраны меню.

Изменённые файлы:

  • hardware/HR-C6000.c — захват кадров данных в обоих режимах, передача данных вместо голоса.
  • functions/hotspot.c — разбор типа кадра от хоста, отправка данных хосту.
  • applicationMain.c — разбор принятого и показ на экране.
  • user_interface/menuSystem.{c,h} — регистрация экранов, пункт меню.
  • языковые файлы — добавлена строка messages (во все 21).

Сборка

Проект рассчитан на STM32CubeIDE, Makefile в нём нет — он написан нами: MDUV380_firmware/Makefile.opengd77. Нужен arm-none-eabi-gcc (проверено на 14.2).

cd MDUV380_firmware
tools/codec_cleaner.Linux -C            # из каталога application/source/linkerdata
make -f Makefile.opengd77 -j$(nproc)

Первый шаг обязателен: он создаёт пустую секцию под AMBE-кодек, которого в исходниках нет.

Про кодек. Официально его «донорит» заводская прошивка TYT. Если её нет, кодек можно взять из дампа самой рации (она уже работает в DMR): секция лежит по смещению 0x6937C от начала образа, длина 0x48BB0, и в дампе она в открытом виде. Скрипт слияния — в истории работ, логика тривиальна: заменить эту область в собранном .bin на область из дампа.

Без кодека прошивка соберётся, но DMR работать не будет (только FM).

Прошивка

sudo python3 MDUV380_firmware/tools/opengd77_stm32_firmware_loader.py \
     -f OpenMDUV380.bin -m MD-UV380

Рация должна быть в режиме DFU (0483:df11) — вход только кнопками при включении, программного способа нет. Нужен python3-usb и root.

Калибровка и codeplug при прошивке не страдают: они в отдельной микросхеме памяти, прошивается только флеш микроконтроллера. Проверено побайтовым сравнением до и после.


Ловушки, на которые мы потратили время

Не то дерево. Для MD-UV380 нужна ветка MDUV380_DM1701 (STM32). Ветка MK22 — это GD-77/DM-1801/RD-5R, там другая структура и другой код хотспота. Исходники OpenGD77 не публикуются на GitHub — только zip-архивами на сайте.

Линковка кодека. Современный ld отвергает вызов по абсолютному числовому адресу. Адреса AMBE заменены на символы через .thumb_set — машинный код получается идентичный (проверено дизассемблером).

Уведомление на экране не годится для показа. uiNotificationShow() из главного цикла затирается штатной перерисовкой экрана. Из-за этого несколько часов казалось, что приём не работает, хотя он работал. Правильно — menuSystemPushNewMenu(UI_MESSAGE_BOX): экран держится до нажатия кнопки.

Правка передачи может сломать голос. Признак «это кадр данных» ставится только на пути хотспота, а сбрасывается после отправки. Если передача оборвалась, признак залипал — и обычная голосовая передача уходила без заголовка вызова, то есть рация излучала, а приёмники её не понимали. Вывод: любую правку передающего тракта проверять голосом с самой рации, а не только в режиме модема.

Оперативной памяти мало. Дважды упирались в переполнение при линковке (360 и 240 байт). Отсюда ограничения: 6 сообщений на папку, текст 64 символа. Если понадобится больше — хранить сообщения во флеш-памяти, а не в ОЗУ.

Диагностика. Счётчики сборщика публикуются в хвост звукового буфера (сигнатура SMSD) и читаются по USB штатным протоколом CPS (область 7). Это надёжнее экрана. Тем же протоколом (область 6) можно снимать содержимое экрана рации — удобно для отладки. Осторожно: частый опрос подпирает главный цикл прошивки и мешает приёму.

Режим модема. Рация входит в него автоматически от первого же кадра MMDVM по USB. Штатно она выходила из него после 20 секунд молчания хоста; по просьбе владельца это заменено на выход при отсоединении USB-хоста — так режим не отваливается при живой программе, но рация сама возвращается в норму, когда кабель вынут.


Лицензия

Исходный код OpenGD77 распространяется под собственной лицензией авторов (BSD-подобная, но пункт 4 прямо запрещает коммерческое использование и включение в проекты, допускающие коммерческое использование). Эти правки — производная работа и наследуют те же условия.