Шапка MeshTRX
Шапка MeshTRX

LoRa придумывали для датчиков: раз в несколько минут отправить десяток байт на пару километров и прожить на батарейке год. Для голоса канал LoRa обычно считают слишком узким. MeshTRX — попытка передавать через него речь: бесплатная прошивка для недорогих плат Heltec WiFi LoRa 32 (тех же, что берут под Meshtastic), приложение для телефона и рация, которая работает там, где нет ни интернета, ни сотовой связи. Нажал кнопку — говоришь, отпустил — слушаешь.

Меня зовут Станислав (R7KIQ), я автор проекта. Ниже — как это устроено внутри: сколько байт уходит на 20 миллисекунд речи, почему голос не ходит через mesh-сеть так же, как текст, во что обходится шифрование в пакете на 39 байт, какие ошибки нашлись при испытаниях и какая получается дальность.

Исходники: github.com/StanislavButkovsky/meshtrx. Сайт с прошивкой из браузера и документацией: meshtrx.ru.

Что это и из чего состоит

  • Плата. Heltec WiFi LoRa 32 V3, V4 (ревизия 4.2) или V4.3: ESP32-S3 + радиочип Semtech SX1262, диапазон 863–870 МГц. Никакой доработки, нужна только антенна на 868 МГц.

  • Прошивка. Ставится за минуту прямо из Chrome через Web Serial: meshtrx.ru/flash. Не понравится — плата так же за минуту прошивается обратно в Meshtastic, железо то же самое.

  • Приложение. Android (с сайта; в RuStore — на модерации), iPhone и настольный клиент на Python. Телефон подключается к плате по Bluetooth LE и служит экраном, микрофоном и динамиком.

  • Связь. Платы говорят друг с другом напрямую или через ретранслятор. Ретранслятором становится любая плата одной настройкой.

Кроме голоса: текстовые сообщения с отметками доставки, фото и файлы, карта и радар с позициями участников, вызов «всех» или одного абонента, шифрование эфира по кодовому слову группы, 23 канала с поиском самого тихого.

Главный экран приложения
Главный экран приложения

Пропускная способность канала

Параметры радио в MeshTRX: SF7, полоса 250 кГц, избыточность 4/7. На бумаге это около 7,8 кбит/с — без учёта преамбулы, заголовков и пауз, так что на деле заметно меньше.

Для сравнения: голос в GSM — 13 кбит/с, самый скромный интернет-звонок — 8–16 кбит/с. Обычные голосовые кодеки в такой канал не помещаются.

Codec2

Используется Codec2 Дэвида Роу. Это вокодер: вместо звуковой волны он передаёт параметры речи — основной тон, энергию, форму спектра, — а на приёме речь синтезируется по ним заново.

Режим 3200 — 3200 бит в секунду. Кадр — 20 мс звука, 160 отсчётов при 8 кГц, и он укладывается в 8 байт. Кодирование идёт на телефоне: приложение пишет микрофон, сжимает речь и отдаёт рации уже готовые кадры по Bluetooth. Прошивка и все клиенты используют один и тот же режим.

Что лежит в пакете

Каждые 80 мс уходит один пакет: четыре кадра Codec2 подряд — 32 байта — и 7 байт заголовка.

Устройство голосового пакета
Устройство голосового пакета

Байт

Поле

Зачем

0

type

аудио, текст, файл, служебное

1

channel

номер канала 0–22; старший бит — «нагрузка зашифрована»

2

seq

порядковый номер

3

flags

начало и конец передачи, голосовая активация

4

ttl

сколько ещё пересылок разрешено

5–6

sender

последние два байта MAC — кто говорит

На SF7/250 кГц/4/7 такой пакет висит в эфире 53,4 мс. Пакеты идут каждые 80 мс, значит, пока человек говорит, канал занят на две трети.

Почему голос не ходит по mesh-сети

Текстовое сообщение — это один пакет. Ему нужно одно удачное окно в эфире: не дошло — приложение повторит через несколько секунд, и человек этого даже не заметит.

С голосом так нельзя: идёт двенадцать пакетов в секунду, и повтор, пришедший через полсекунды, уже бесполезен. Кроме того, при занятости канала 67% на многократную пересылку не остаётся эфирного времени.

Поэтому в MeshTRX голос идёт напрямую или через один ретранслятор; многоузловой пересылки, как у текста в Meshtastic, для голоса нет. TTL у пакета — 2. От зацикливания защищает память ретранслятора: он помнит, что уже переслал (кольцевой кеш на 128 записей; отправитель + номер + тип), и дубль отбрасывает.

С этим кешем была ошибка. Номер голосового пакета — один байт, и сначала он сбрасывался в ноль при каждом нажатии кнопки, чтобы приёмник определял начало фразы. А ретранслятор помнил пересланное тридцать секунд. Вторая фраза через пять секунд приходила с теми же номерами от того же отправителя и отбрасывалась целиком как дубль. На стенде: первая фраза — 10 пакетов из 10, вторая — 0 из 10, четвёртая через 40 секунд — снова 10 из 10. В поле это выглядело как «через ретранслятор голос доходит через раз».

Исправление: номер стал сквозным, начало фразы определяется отдельным флагом, а время хранения голосовых записей в кеше уменьшено до трёх секунд — этого достаточно, чтобы отсечь повторы.

Радар: станции вокруг и ретранслятор
Радар: станции вокруг и ретранслятор

Шифрование в 39 байтах

Первое время эфир MeshTRX был открыт, и мы прямо писали об этом. Когда дошли руки, оказалось, что каждый байт криптографии стоит эфирного времени:

Пакет

Время в эфире

Занято канала

без шифрования, 39 байт

53,4 мс

67%

+ подпись 4 байта

57,0 мс

71%

+ подпись 8 байт

60,5 мс

76%

+ полная подпись 16 байт

71,3 мс

89%

+ публичный ключ 32 байта

85,6 мс

107%

Последняя строка — не опечатка: пакет с публичным ключом X25519 в канал не помещается, передача не успевает закончиться до начала следующей. Поэтому обмена ключами в каждом пакете нет и не будет.

Что получилось:

  • Общий ключ группы из кодового слова. На всех рациях вводится одна и та же фраза, из неё выводится 256-битный ключ (PBKDF2-SHA256, 50 000 итераций — около трёх секунд на ESP32-S3, это защита от перебора). Фраза в телефоне не хранится и в эфир не уходит.

  • Шифр — AES-256 в режиме CTR, длина шифротекста равна длине открытого текста. Заголовок остаётся открытым: по нему ретранслятор делает дедупликацию и TTL, не зная ключа.

  • Подпись — AES‑CMAC, укороченная до 6 байт, плюс 2 байта эпохи. Итого +8 байт к пакету. Округление символов LoRa таково, что шесть байт подписи стоят не дороже четырёх.

  • Отпечаток ключа — четыре символа, по которым группа сверяет ключ вслух, не диктуя само слово. Если рядом говорят чужим ключом, приложение это показывает.

Честно об ограничениях: это общий ключ на группу, а не сквозное шифрование между двумя людьми. Потерянная рация с ключом — повод сменить кодовое слово у всех.

23 канала и соседи по диапазону

На 868 МГц живёт много кто: домофоны, метеостанции, счётчики, автосигнализации. В городе это заметный и неравномерный фон. Рация умеет пройти по всем 23 каналам (863,15–869,75 МГц с шагом 300 кГц) и померить шум. Уровень берётся как максимум серии, а не среднее: помеха бьёт вспышками, и по среднему канал выглядит тихим, хотя раз в секунду в нём проезжает чужой пакет — ровно то, что убивает голос. Первый же замер на столе показал 15 дБ между самым тихим и самым шумным каналом.

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

Настройки: канал, поиск свободного, ключ группы
Настройки: канал, поиск свободного, ключ группы

Хранение сообщений на рации

Если телефон адресата выключен или не подключён, его рация принимает сообщение и сохраняет в памяти: кольцевой буфер на 8 КБ, а на платах с PSRAM — на 128 КБ. Когда места не хватает, выбрасываются самые старые. Отправитель видит отметку «принято нодой», а когда адресат подключит телефон, сообщение придёт с пометкой, сколько оно пролежало.

Раньше при потере подтверждения на обратном пути отправитель видел «не доставлено», хотя сообщение было получено. Теперь у сообщения три статуса: отправлено, принято нодой, доставлено.

Чат с отметкой «принято нодой»
Чат с отметкой «принято нодой»

Дальность

Паспортные цифры дальности LoRa обычно даны для прямой видимости. Первые замеры пользователей в городе: устойчивый голос до двух километров, дальше проходят текстовые сообщения. В плотной застройке голос сдаётся раньше — около километра, при том что текст по тому же маршруту ходит почти до двух. На прямой видимости дальше, а ретранслятор на возвышенности заметно растягивает связь.

Meshtastic на медленных режимах связи добивает дальше: чем ниже скорость передачи, тем выше чувствительность приёма. Голосу нужна высокая скорость, поэтому MeshTRX использует быстрый режим.

Ошибки, найденные при испытаниях

На одной из ревизий платы дальность была заметно ниже ожидаемой. Причина — вывод, переключающий внешний усилитель между приёмом и передачей, стоял не в том состоянии, и сигнал шёл в обход усилителя. Уровень у соседней рации: −78 дБм до правки и −20 дБм после, разница 58 дБ. Поэтому в мастере прошивки обязательный выбор ревизии платы: у V4 rev 4.2 и V4.3 разные усилители и разные выводы управления.

Вторая ошибка: на ретрансляторе оставался включённым Wi-Fi-клиент, и на стенде это давало ноль пересланных голосовых пакетов из двадцати. Проблему заметил участник сообщества, после отключения лишнего передатчика пересылка восстановилась.

Как это проверяется

Две платы подключены к компьютеру, телефон заменяет эмулятор протокола Bluetooth на Python. Он прогоняет сценарии: голос туда и обратно, сообщения при «выключенном» телефоне, смену канала, ретранслятор. В прошивке для стенда есть тестовая консоль, через которую видно, что рация приняла, переслала и отбросила. Таблицы в этой статье получены на этом стенде.

Стек: прошивка — PlatformIO + Arduino для ESP32-S3, RadioLib для SX1262, NimBLE-Arduino для Bluetooth, Codec2, mbedTLS. Android — Kotlin, iOS — SwiftUI, настольный клиент — Python.

Попробовать и поучаствовать

Понадобятся две платы Heltec WiFi LoRa 32 (V3, V4 или V4.3) с антеннами на 868 МГц и два телефона. Прошивка — meshtrx.ru/flash, приложение — meshtrx.ru/download. Помните, что 863–870 МГц — диапазон для маломощных устройств, и правила его использования в разных странах свои.

Проект некоммерческий, исходники открыты под лицензией CC BY-NC 4.0: свободно для некоммерческого использования с указанием авторства. Больше всего проекту помогают замеры дальности, отчёты об ошибках и фото ваших сборок — антенн, корпусов, ретрансляторов на крышах.

73! Станислав, R7KIQ

Meshtastic® — зарегистрированный товарный знак его владельцев; MeshTRX с проектом Meshtastic не связан.

Комментарии (17)


  1. DirOr
    23.10.2026 10:13

    Спасибо, очень интересно!

    К сожалению, на али не указывают подробности, только LoRa 32 V4, а какая ревизия, 4.2 или 4.3 как понять?


    1. Hahammer
      23.10.2026 10:13

      На плате, обычно написано


  1. iddi
    23.10.2026 10:13

    пропал LoRa...


  1. slog2
    23.10.2026 10:13

    Параметры модуляции Lora совсем не оптимальные для максимальной дальности. Понятно что это ради скорости и лучше сделать нельзя. На сколько в данном случае Lora лучше чем обычный FSK который SX1262 тоже умеет?


    1. NinaNina89
      23.10.2026 10:13

      Обычный FSK на такой же скорости требует значительно лучшего отношения сигнал/шум. В застройке без прямой видимости он сдохнет раньше чем LoRa начнет терять пакеты


  1. sim2q
    23.10.2026 10:13

    Вредительство.


    1. iddi
      23.10.2026 10:13

      вот вот


    1. ducemollari
      23.10.2026 10:13

      Кто не смотрел Игонина, теперь прочитал это.

      Помянем :)


  1. ub9obe
    23.10.2026 10:13

    #голос


  1. vagon333
    23.10.2026 10:13

    Юзанные телефоны сейчас дешевле грязи, но в них многое уже есть.

    А что, если добавить модуль LoRa через USB-C к телефону и пересылать текст?
    В телефоне приложение, работающее с микрофоном телефона, встроенными ASR и TTS?

    Может идиотское решение, но в теории должно позволить передавать и цифру между телефонами, где-нибудь в жопе вселенной.


    1. NinaNina89
      23.10.2026 10:13

      Локальные модели распознавания и синтеза речи на старых бюджетных телефонах будут нещадно выжирать батарею и тормозить. Codec2 кодирует сам звук на лету с минимальным расходом процессора


  1. peacemakerv
    23.10.2026 10:13

    Так какая дальность и при каких условиях (мощность)?


    1. wepp
      23.10.2026 10:13

      В тексте же написано: в городе до 2 км, при плотной застройке - 1.


  1. WaldemarsonTheForester
    23.10.2026 10:13

    Любопытства ради, а почему полоса 250 КГц, а не 500?


    И CR именно 4/7? Как-то экспериментально подтвердилось, что 4/7 настолько лучше, чем 4/5, что стоит лишнего времени в эфире?


    1. NinaNina89
      23.10.2026 10:13

      Лишняя коррекция 4/7 спасает пакеты от одиночных выпадений из-за помех от городских датчиков


      1. WaldemarsonTheForester
        23.10.2026 10:13

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