DMR-данные (SMS) на TYT MD-UV380 под OpenGD77
Восстановлено из транскрипта сессии после потери рабочего дерева на сервере сборки. Что внутри: - четыре новых модуля прошивки (приём, сборка исходящего, меню Messages); - правки существующих файлов в виде скриптов, с порядком применения; - инструменты стенда: снятие экрана рации по USB, дамп кадров, эталонная сборка пакета; - NOTES.md: что проверено в эфире, что не доделано, где грабли. Работало в эфире в августе 2026: данные ходят в обе стороны в режиме хотспота, рация принимает SMS и показывает на экране. Отправка собрана и сверена с эталоном сервера побайтово, но в эфире не проверялась. Claude-Session: https://claude.ai/code/session_01GJtvG5WwqYNe1b7s9LLtgy
Этот коммит содержится в:
+160
@@ -0,0 +1,160 @@
|
||||
# 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 прямо запрещает коммерческое использование и
|
||||
включение в проекты, допускающие коммерческое использование). Эти правки —
|
||||
производная работа и наследуют те же условия.
|
||||
Ссылка в новой задаче
Block a user