Минимальный набор компонентов для отправки и приёма сообщений по LoRa с поддержкой шифрования, очередей QoS, веб-интерфейса и управлением через Serial/HTTP. Репозиторий содержит как прошивку, так и вспомогательные библиотеки и средства тестирования.
- Обзор проекта
- Структура репозитория
- Основные компоненты прошивки
- Разработка в VSCode и PlatformIO
- Каталог
libs/ - Веб-интерфейс
- Вкладка Chat
- Вкладка Antenna helper
- Вкладка Channels/Ping
- Вкладка Settings
- Вкладка Security
- Вкладка Debug
- HTTP API
- Хранилище ключей и обмен
- Типы пакетов
- Справочник API
- Команды Serial
- Пример последовательности обработки
- Тестирование
- Файл конфигурации
- Wi-Fi точка доступа
- Что реализовано
- Что осталось сделать
В форке сохранены только базовые элементы исходного проекта: очередь сообщений, разбиение и сборка пакетов, радиомодуль SX1262, механизм шифрования ChaCha20-Poly1305 и совместимый режим AES-CCM, а также сопутствующая инфраструктура. Штатная конфигурация рассчитана на работу как на ESP32, так и на ПК.
Прошивка умеет:
- отправлять и принимать фрагментированные сообщения с подтверждениями ACK;
- применять код Рида–Соломона, байтовый и битовый интерливинг, свёрточное кодирование и скремблирование;
- вести журнал статусов, управлять радиопараметрами и обмениваться ключами шифрования;
- предоставлять веб-интерфейс для чата и настройки устройства.
⚠️ Текущее состояние. В рамках последнего изменения сложная обработка кадров полностью отключена. Транспорт работает по схеме «данные → байты → отправка», а на приёмной стороне полезная нагрузка сразу передаётся в колбэк без дешифрования и ФЕК. Логика подготовки ACK, кодирования, интерливинга и скремблирования сохранена в коде, но выключена директивами препроцессора и комментариями, чтобы в дальнейшем можно было вернуть полный конвейер без переписывания с нуля.
tx_module.cpp,rx_module.cpp,message_buffer.cpp— ядро обмена сообщениями;radio_interface.h,radio_sx1262.*— абстракция радиоинтерфейса и реализация для SX1262;src/main.cpp— основной скетч PlatformIO с HTTP-интерфейсом, обработкой Serial и интеграцией всех модулей. В файле реализованы вспомогательные функцииparseFloatArgument,parseIntArgument,parseBandwidthArgument,parseSpreadingFactorArgument,parseCodingRateArgument,parseChannelArgumentиparsePowerPresetArgument, которые проверяют диапазоны числовых HTTP-параметров/cmdи формируют человекочитаемые сообщения об ошибках;web/— встроенный клиент с вкладками Chat/Antenna helper/Channels/Settings/Security/Debug;key_storage/— файлы с ключами шифрования;libs/— сторонние и вспомогательные библиотеки (кодек, интерливинг, логгер и т.д.);tests/— набор проверок без реального радиоканала;libs_includes.cpp— единая точка подключения реализаций для Arduino;platformio.ini— конфигурация PlatformIO для сборки, прошивки и мониторинга;.vscode/— рекомендованные настройки VSCode и задач PlatformIO.
- MessageBuffer — очередь сообщений с ограничением по количеству элементов и доступом к идентификатору. Позволяет откатывать добавление и просматривать первый элемент без извлечения.
- ReceivedBuffer — буфер принятых элементов (RAW/SP/GO) с готовыми именами и снимками для
команды
RSTS. - PacketSplitter — делит сообщение на блоки фиксированного размера либо пользовательской длины.
- PacketGatherer — объединяет части сообщения обратно в единый буфер и отслеживает завершение.
- FrameHeader — формирует и проверяет заголовок LoRa-кадра, уплотняя флаги, индекс и длину полезной нагрузки в 32-битное поле и полагаясь на AEAD-тег для контроля целостности вместо отдельных CRC.
- TxModule — в текущей версии работает как прямой буферизованный передатчик: сообщения
добавляются в очередь без префиксов, не кодируются и не шифруются, после чего передаются
в радио единым блоком. Историческая логика QoS, ACK, кодирования и скремблирования
сохранена в файле, но обёрнута комментариями и
#if 0. - RxModule — принимает сырые байты от радиомодуля и немедленно пробрасывает их в пользовательский колбэк. Старый конвейер расшифровки, FEC и сборки фрагментов оставлен закомментированным для будущего возврата.
- IRadio — контракт радиомодуля (описан в
radio_interface.h). - RadioSX1262 — реализация интерфейса с настройкой банка каналов, полосы, SF, CR,
мощности и аппаратного режима RX boosted gain, а также методами пинга, маяка и получения
последних RSSI/SNR.
- Вспомогательная функция
startReceiveWithRetry()перезапускает приём с несколькими попытками, логирует коды ошибок и выполняет программный сброс SX1262 при занятом канале, чтобы основной цикл продолжал работу без зависаний.
- Вспомогательная функция
- Блокировка радиоканала. Все публичные операции
RadioSX1262(send,ping,sendBeacon,ensureReceiveMode,resetToDefaults) теперь возвращаютint16_tи защищены мьютексом: на ESP32 используетсяSemaphoreHandle_t, в тестовой среде —std::timed_mutex. Захват выполняется черезlockRadio()с тайм-аутомLOCK_TIMEOUT_MS = 500мс, итоговый код передаётся вызывающему коду. ЗначениеIRadio::ERR_TIMEOUT(-32000) означает, что радио уже занято,RadioSX1262::ERR_PING_TIMEOUT(-31999) сигнализирует об истечении ожидания ответа,RadioSX1262::ERR_INVALID_ARGUMENT(-31998) — о некорректных параметрах вызова. TxModule, CLI и HTTP-команды проверяют эти коды: повторная отправка планируется позже, в консоль и веб возвращается понятное сообщениеradio busy/ERR. Такой контракт фиксирован для будущих задач и описан в коде и тестах. - DefaultSettings — флаг
DEBUG, уровни логирования, включение RS-кода/ACK и RX boosted gain, пауза между отправками, размер блока дляPacketGathererи значениеPING_WAIT_MS.
- TextConverter (
libs/text_converter/) — преобразует текст между UTF-8 и CP1251, что используется при отправке сообщений и отображении входящих пакетов. - SimpleLogger (
libs/simple_logger/) — журнал статусовERR/PROG/GOс кольцевым буфером и единым обработчиком вывода в debug, доступный командойSTS. - Logger (
src/logger.cpp/.h) — асинхронная FreeRTOS-задачаLogger::task, которая читает кольцевую очередь, формирует кадры с CRC16 и выводит сообщения вSerialиLogHook. Очередь автоматически усекает слишком длинные записи, считает потери при переполнении и поддерживает немедленную доставку в тестовой среде. Ограничитель пропускает до 80 строк в секунду с начальными 40 токенами, параметры задаются вDefaultSettings::LOGGER_RATE_LINES_PER_SECиLOGGER_RATE_BURST. МакросLOG_WRAP(tag, expr)измеряет время выполнения выражения и кладёт CSV-записьtag,start_us,end_us,duration_usв очередь, что позволяет собирать профили критичных участков без блокировок. - KeyTransferWaiter (
libs/key_transfer_waiter/) — конечная автоматика ожидания кадраKEYTRANSFER RECEIVE, отслеживает тайм-аут, подписчиков (Serial/HTTP) и готовый JSON-результат, позволяя главному циклу опрашивать состояние без блокирующихwhile. - Макросы
LOG_ERROR/LOG_WARN/LOG_INFO/DEBUG_LOGи их варианты*_FROM_ISRтеперь только формируют строку и отправляют её в очередь логгера; немедленный вывод выполняет задачаLogger::task. При необходимости можно включить форсированныйSerial.flush()через флагDefaultSettings::SERIAL_FLUSH_AFTER_LOG(по умолчанию отключён, чтобы не тормозить поток IRQ-логов). - HttpBodyReader (
src/http_body_reader.h) — шаблонная функцияreadBodyToVectorпотоково считывает тело HTTP-запроса вstd::vector<uint8_t>, контролирует ограничение по размеру и сообщает о превышении лимита или незавершённом чтении. Используется обработчиком/api/tx-image, чтобы принимать JPEG с нулевыми байтами без промежуточныхString. DEBUG_LOGи другие макросы принимают форматированные строки в стилеprintf, что упрощает вывод параметров без ручной конкатенации.
Проект переведён на рабочий процесс с использованием Visual Studio Code и расширения PlatformIO. Теперь все основные сценарии — сборка, прошивка и мониторинг — доступны прямо из VSCode без Arduino IDE и вспомогательных скриптов.
- Установите VSCode и расширение PlatformIO IDE (в нём уже есть тулчейн
xtensa-esp32-elf,esptool.pyи менеджер библиотек). - Откройте папку
satprjctчерезFile → Open Folder…— PlatformIO автоматически обнаружит файлplatformio.iniи настроит рабочее пространство. - При первом запуске примите установку рекомендуемых расширений из
.vscode/extensions.json, чтобы получить IntelliSense, шаблоны задач и форматирование кода.
- Команда PlatformIO: Build (
Ctrl+Alt+B) или задачаPIO Buildсобирает прошивку ESP32. Результат по умолчанию складывается вC:\\pio-build\\sat2(см. опциюbuild_dir). - PlatformIO: Upload прошивает устройство, используя скорость из
upload_speed = 921600. - PlatformIO: Monitor открывает последовательный порт на скорости
115200бод (monitor_speed). - Для быстрого цикла отладки можно использовать PlatformIO: Upload and Monitor, которая сразу запускает монитор после прошивки.
- Все эти операции доступны и из терминала:
pio run,pio run -t upload,pio device monitor.
- Сторонние библиотеки объявлены в секции
lib_depsфайлаplatformio.iniи размещаются в каталогеlibs/, поэтому дополнительных действий в Arduino IDE не требуется. - В
lib_extra_dirsможно прописать пути к дополнительным Arduino-библиотекам, если они находятся вне репозитория. - Параметр
build_src_filterисключает из прошивки каталогиtests/,REF/и содержимоеlibs/*, чтобы прошивка собиралась только из необходимых файлов.
- В
.vscode/settings.jsonсохранены параметры подсветки, форматирования и IntelliSense для C++17 и Arduino. - Доступ к встроенным задачам PlatformIO (Build/Upload/Monitor) осуществляется через палитру
команд VSCode (
Ctrl+Shift+P).
Если требуется офлайн-сборка на другой машине, достаточно установить Python 3 и PlatformIO CLI (
pip install platformio), после чего выполнить команды из раздела «Сборка, прошивка и мониторинг».
Каждая библиотека расположена в отдельной подпапке каталога libs/.
| Библиотека | Назначение |
|---|---|
rs255223 |
Обёртки encode()/decode() для кода Рида–Соломона RS(255,223). |
byte_interleaver |
Байтовый интерливинг (interleave/deinterleave). |
conv_codec |
Свёрточное кодирование и декодирование (encodeBits/viterbiDecode). |
bit_interleaver |
Битовый интерливинг (interleave/deinterleave). |
scrambler |
Скремблирование кадра на основе LFSR (полином x^16 + x^14 + x^13 + x^11, seed 0xACE1). |
frame |
Структуры заголовков кадров (FrameHeader). |
key_loader, crypto/aes_ccm, key_transfer |
Работа с ключами, AES-CCM и обмен корневым ключом. |
crypto/hkdf |
HKDF-SHA256 для вывода симметричных ключей и вспомогательных параметров. |
simple_logger, text_converter |
Логирование, преобразование текста. |
serial_mirror |
Зеркалирует вывод базового Serial в LogHook, предоставляет метод availableForWrite(), который напрямую делегирует HardwareSerial::availableForWrite() и сохраняет совместимость с конструкциями вида while (!Serial). |
config_loader |
Читает файл config/default.ini, подставляет значения по умолчанию и предоставляет доступ к структуре настроек. |
- Метод
SerialMirror::availableForWrite()возвращает количество байт, которое базовыйHardwareSerialготов принять для записи без блокировок, что позволяет корректно использовать проверку заполнения буфера перед дампом RX-данных. - Функция
dumpRxToSerialWithPrefix()(файлsrc/rx_serial_dump.h) безопасно выгружает RX-пакет вSerial, фиксируя предупреждение «RX dump truncated» черезLogHook, если буфер порта не вместил все данные.
Файл libs_includes.cpp подключает реализации библиотек для Arduino-проекта и избавляет от
неявных зависимостей. Сборкой и прошивкой управляет platformio.ini, поэтому отдельные скрипты
для arduino-cli больше не требуются.
Каталог web/ содержит встраиваемый интерфейс с вкладками Chat, Channels/Ping, Settings,
Security и Debug. Страница отдаётся прошивкой напрямую из web/web_content.h, который
формируется скриптом tools/generate_web_content.py из файлов web/index.html, web/style.css,
web/script.js, web/libs/sha256.js, web/libs/mgrs.js, web/libs/freq-info.csv и
web/libs/geostat_tle.js.
Для раздачи справочника частот и преобразования координат используются обработчики
handleFreqInfoCsv и handleMgrsJs в src/main.cpp, чтобы документация соответствовала
реальной реализации веб-сервера.
Команда: python src/tools/generate_web_content.py --output src/web/web_content.h
- Редактируйте HTML/CSS/JS в каталоге
web/, после чего выполнитеpython3 tools/generate_web_content.py, чтобы пересобратьweb/web_content.hперед сборкой прошивки. - Скрипт
tools/generate_geostat_tle.pyобновляетweb/libs/geostat_tle.jsиз файлаREF/tle.txt; для встраивания изменений запустите генератор веб-контента повторно.
- Используйте задачи PlatformIO (Build, Upload, Upload and Monitor) либо CLI-команды
pio run,pio run -t upload,pio device monitor. Конфигурация (скорость порта, разделы флеша, флаги компилятора) задаётся вplatformio.ini.
- Единый адаптивный дизайн с навигацией, раскрывающейся поверх контента на мобильных устройствах.
- При переключении вкладок прокрутка блокируется, а активная вкладка подсвечивается.
- История сообщений и настройки сохраняются в
localStorage; при его недоступности используется резервное хранение в памяти вкладки. - Если интерфейс открыт в нескольких вкладках, история чата и состояние ключей синхронизируются
через
localStorage, поэтому областьchat-area,keyPublic,keyIdиkeyBackupобновляются без перезагрузки страниц. - Внизу страницы отображается подпись «Powered by AS Systems» с номером версии из
/ver. После успешного чтения значение кэшируется вlocalStorage, поэтому отображается даже при временном отсутствии связи. Дополнительно используется командаVER, а ответыunknown/пустые строки очищают кэш и скрывают значение. Файлverхранит версию в кавычках, поэтому прошивка получает резервное значение даже при отсутствии внешнего файла. - Переключатель темы поддерживает «красный» ночной режим и автоматическое включение по времени; автоматический режим можно отключить.
- Блок состояния показывает флаги ACK и шифрования: индикаторы работают как переключатели.
- Прошивка, собранная из этого репозитория, поднимает SSE-эндпоинт
/events, который отдаёт краткие JSON-уведомления о появлении новых элементов вReceivedBuffer. Сообщение содержит тип (raw/split/ready), имя, идентификатор и размер полезной нагрузки. - Канал
/eventsдополнен событиемlog, передающим JSON{id, uptime, text}с новыми строками Serial-журнала, поэтому вкладка Debug получает обновления в реальном времени без дополнительного опроса. - Веб-интерфейс автоматически подключается к каналу: при активном push обновления попадают в чат сразу, а периодический запрос
RSTSпереводится в резервный режим (30 с). Если SSE недоступен (старая прошивка, неподдерживаемый браузер, временный обрыв), приложение возвращается к классическому опросу каждые 5 с без вмешательства пользователя. - При ошибке создания SSE-подключения клиент сохраняет причину сбоя, показывает её на вкладке Debug и автоматически планирует повторное подключение с увеличением задержки (до 30 с). После смены адреса в настройках счётчики сбрасываются, и страница сразу пытается открыть новый канал поверх актуального эндпоинта.
- Отладочная вкладка Debug показывает текущий режим («push активен»/«используется опрос»), а также напоминает, что push требует обновлённой прошивки с поддержкой SSE.
- Пузырьковое отображение истории сообщений с цветами для исходящих, входящих и системных сообщений.
- Команды можно отправлять напрямую через
/, аргументы (/ACK 0,/TX текст) сохраняются в истории. - В чате появился модуль обмена изображениями:
- кнопка с пиктограммой и drag&drop принимают JPEG/PNG/HEIC размером до 20 МБ;
- браузер масштабирует изображение в рамку 320×240 с сохранением пропорций (letterbox) и по умолчанию переводит его в градации серого;
- профили
S/M/Lуправляют качеством JPEG (≈0.30/0.40/0.40) и цветностью, отправка выполняется черезPOST /api/tx-image, а прошивка отклоняет кадры больше 20 480 байт (160 слотов × 128 байт); - части изображения маркируются индексами
IMGP-XXXXXX, собранные кадрыIMG-XXXкэшируются и появляются в истории чата с подписью.
- Системные ответы выводятся курсивом уменьшенного кегля, подтверждение
TXотображается внутри сообщения отправителя. - Кнопки
BCN,TESTRXMи блок отправкиTXLвызывают служебные команды без ввода вручную. - Полученные пакеты автоматически дублируются в чат: основное тело показывает полезную нагрузку из
поля
text, а под ним выводятся подписиnameиLenв байтах. При восстановлении браузер пробует несколько кодировок и использует эвристики для борьбы с «кракозябрами» (СЃР±…), поэтому кириллица отображается корректно даже после двойного перекодирования. Старые записи с тегомrx-nameнормализуются в форматrx-message. - Если устройство сперва вернуло только имя
GO-xxxxx, чат автоматически обновит запись и подставит текст сразу после появления полезной нагрузки в ответеRSTS. - История чата автоматически ограничивается 500 сообщениями: функции
applyChatHistoryLimit,adjustChatProgressAfterTrimиadjustChatDomAfterTrimизweb/script.jsобрезают избыточные записи, синхронизируют DOM-индексы и корректируют карту прогресса, чтобы сериализация и прогресс приёма оставались консистентными. - Фоновый монитор входящих автоматически подключается к SSE-каналу
/eventsи получает push-уведомления о новыхGO-xxxxx/SP-xxxxx/R-xxxxxx. При активной подписке чат обновляется мгновенно, а резервный опросRSTSзапускается раз в 30 с. Если прошивка не поддерживает SSE или соединение недоступно, веб-интерфейс сам возвращается к прежнему режиму опроса каждые 5 с, поэтому совместимость со старыми версиями сохраняется. - Парсер ответов
RSTSтеперь распознаёт вложенные JSON-структуры (ready/split/raw, массивы байтов) и корректно восстанавливает полезную нагрузку даже при изменённом формате прошивки, поэтому входящие сообщения снова появляются в чате сразу после приёма. - Если полезная нагрузка отсутствует, пузырь чата показывает имя пакета и вычисленный объём данных
(
GO-xxxxx · 42 байт), чтобы было видно, что ответ собран полностью. - Пока в списке
RSTSостаются промежуточные пакетыSP-, в левом нижнем углу истории чата отображается анимированный индикатор «Приём сообщения…», который гаснет сразу после сборки сообщения. - Под окном истории расположен стеклянный статус-бар «Последний IRQ»: функция
renderChatIrqStatus()вweb/script.jsреагирует на строки журналаRadioSX1262: IRQ=...(уровеньDEBUG), показывает текст события и при наличии дописывает uptime устройства. Так операторы видят актуальные аппаратные флаги, не переключаясь во вкладку Debug. - При появлении
SP-xxxxxв списке входящих в чате создаётся отдельный пузырь «Приём пакета GO-xxxxx», отображающий объём уже полученных данных. Как только приходит финальный пакетGO-, индикатор прогресса превращается в обычное сообщение с полезной нагрузкой и убирается из списка ожидающих. - Текст готовых сообщений
GO-xxxxxвосстанавливается изhexпрямо в браузере (функцииhexToBytes,decodeBytesToText,resolveReceivedText), поэтому в списке и в чате сразу отображается собранный payload, даже если прошивка отдаёт только шестнадцатеричный снимок. - Команда
TESTRXMформирует серию из пяти сообщений на основе фразы Lorem ipsum: четыре варианта содержат 100/50/30/20 % текста, а пятое проходит через эмуляциюPacketGatherer(создаются записиR-/SP-/GO-); задержка между генерациями составляет 0,5 секунды. - Функция
pollReceivedAfterTestRxm()автоматически опрашиваетRSTSи подхватывает готовые сообщения после запускаTESTRXM, добавляя их в чат без повторов благодаря отслеживанию последнего имени пакета. - В чат автоматически попадают все элементы со статусом
ready(не толькоGO-xxxxx), а промежуточныеSP-/R-остаются в списке входящих — так легче отслеживать историю сборки сообщений. - Логика распознавания статуса вынесена в функцию
normalizeReceivedType(), чтобы без регулярных выражений унифицировать работу с типами пакетов. - При прокрутке истории вверх появляется плавающая кнопка «вниз», возвращающая к последнему сообщению без неожиданных скачков; если пользователь читает старые сообщения, новое сообщение не перехватывает фокус.
- Входящие записи с ролью
rxсопровождаются коротким звуковым уведомлением через Web Audio — звук активируется после первого взаимодействия и помогает не пропустить ответ. - В навигации добавлен бейдж непрочитанных сообщений: функция
updateChatUnreadBadge()читает и сохраняет счётчик вlocalStorage, анимацией напоминает о новом сообщении и автоматически выключаетaria-live, когда вкладка чата активна. - Блок быстрых команд и отправка
TXLсвернуты в<details>, что экономит место на мобильных устройствах и не перегружает интерфейс.
- Бывшая вкладка Pointing переименована в «Antenna helper» и отражает обновлённую структуру интерфейса.
- Верхняя сводка отображает состояние TLE, введённые координаты наблюдателя и количество видимых спутников. Источник координат теперь всегда «ручной ввод».
- Панель наблюдателя стала складывающейся: внутри указываем квадрат MGRS (100 км) и высоту. По умолчанию используется сектор 43U CR; введённое значение мгновенно нормализуется и сохраняется в кеше браузера.
- Геолокация и датчики ориентации удалены, поэтому интерфейс больше не запрашивает разрешения и не привязан к HTTPS. Все подсказки про сенсоры и компас заменены на инструкции по ручному вводу.
- Панель горизонта получила разметку основных азимутов и компактные маркеры спутников. Цвет совпадает с квадрантом компаса, маркеры занимают на 50 % меньше места и автоматически разводятся по вертикали при плотном расположении аппаратов; расстояние до горизонта ближе к реальному.
- Круговой радар развёрнут так, что юг расположен в верхней части (как при наведении антенны), а на циферблат добавлены подсвеченные сектора и подписи всех сторон света. Легенда по-прежнему повторяет цветовые группы квадрантов.
- Ручной ввод координат MGRS стал устойчивее: высота обновляется без повторного ввода квадрата, допустимы пробелы и запятые, а сохранённое значение высоты применяется сразу.
- Если библиотека
/libs/mgrs.jsнедоступна, вкладка выводит предупреждение, блокирует ввод и сохраняет последнее введённое значение до восстановления конвертера. - Список спутников сгруппирован по квадрантам; карточки показывают азимут, возвышение, долготу подспутника и другие параметры. Функции выбора и подсветки синхронизированы с панелью горизонта и радаром.
- Преобразование MGRS в широту/долготу выполняет новая библиотека
web/libs/mgrs.js, которая восстанавливает центральные координаты 100‑км квадрата.
- Таблица частот активного банка показывает частоты, RSSI, SNR, статус и результат сканирования.
- Текущий канал подсвечивается, строки получают классы
busy,freeилиunknown. - При клике открывается карточка с измерениями и справочными данными (System/Band Plan/Purpose).
Данные берутся из
web/libs/freq-info.csv, при недоступности — из встроенной копии. - Stability test сохраняет для каждого пакета задержку, причину ошибки и обновлённые метрики RSSI/SNR; сводка показывает успешность, среднюю задержку, джиттер и диапазоны значений.
- График Stability test получил подсказки по наведению, отображает средние значения, диапазоны RSSI/SNR и подсвечивает точки провалов; карточки под графиком помогают быстро оценить разброс метрик.
- История Stability test хранится в браузере по каналам: отображаются последние прогоны с трендом успешности, доступен экспорт в CSV/JSON и очистка истории.
- В верхней части вкладки выводится индикатор состояния справочника: он показывает загрузку CSV, предупреждает об использовании fallback и сообщает о реальной ошибке запроса.
- Последний успешно полученный список каналов кешируется в
localStorage: при тайм-ауте запросов отображаются реальные частоты вместо демонстрационных значений, а после восстановления связи данные автоматически перезаписываются актуальным ответом устройства. - Карточка содержит кнопку «Установить текущим каналом» для мгновенного переключения радиомодуля.
- Кнопка Search последовательно пингует каналы активного банка, выделяя состояния классами
scanning,signal,crc-error,no-response. Результаты можно экспортировать в CSV с полемscan_state. - В карточке канала появились тесты Stability test и CR test. Первый выполняет 20 ping с интервалом 300 мс, строит график RSSI/SNR и считает процент успешно доставленных пакетов. Второй перебирает все значения CR, выполняет ping для каждого и выводит таблицу с результатами и измеренными RSSI/SNR.
- Обновление статусов во время сканирования выполняется через вспомогательную функцию
uiYield()(requestAnimationFrameилиsetTimeout). Дополнительные функции:detectScanState(),applyPingToEntry(),setChannelScanState().
- Отдельная секция «Подключение» содержит поле
Endpoint (HTTP)с подсказкой по формату. Значение сохраняется вlocalStorage, а при изменении интерфейс немедленно инициирует повторный опрос радиомодуля без перезагрузки страницы. - Поддерживает ввод
Endpoint, управление BANK/BF/CH/CR/PW/RXBG/SF и настройку количества повторовACKR(при включённом подтверждении), паузы между отправками (PAUSE), тайм-аута ACK (ACKT) и задержки ответа ACK (ACKD). ЗначениеACKT = 0выключает ожидание ACK: отправленный пакет сразу считается доставленным, очередь не блокируется ожиданием подтверждения. Дефолтное значение тайм-аута составляет 320 мс, что синхронизировано между прошивкой и веб-интерфейсом. - Переключатель
RXBGуправляет аппаратным режимом повышенного усиления приёмника; подсказка рядом показывает текущее состояние, считанное с устройства. - Переключатель «Light pack — прямой текст» переводит передачу в минимальный режим: текст из чата уходит напрямую в LoRa-модуль «сырым» пакетом без заголовка и служебного префикса, а принятые кадры отображаются без дополнительных полей.
- Секция «Ключ шифрования» содержит кнопки
LOCALиPEER, которые вызывают командыKEYGENиKEYGEN PEERнапрямую из настроек. КнопкаPEERавтоматически блокируется, если нет сохранённого публичного ключа партнёра, а подсказка поясняет текущий режим. - Значения считываются с устройства при загрузке страницы, выбранный канал сохраняется в локальном хранилище и восстанавливается после перезапуска.
- Под выпадающим списком отображаются частоты RX/TX активного канала, подсказки рядом с
ACKR,ACKTиACKDотображают актуальные задержки и тайм-ауты. - Подсказка рядом с
ACKRотображает суммарное ожидание перед повторной отправкой с учётом тайм-аута и задержки формирования ACK. - Поле «Сообщение для TESTRXM» позволяет задать текст для теста входящих сообщений (до 2048 символов, ~2 КиБ); при пустом значении используется встроенный шаблон Lorem ipsum.
- Переключатель «Тестовый режим TX/RX» активирует эмуляцию
SendMsg_BR/received_msg: введённые через чат сообщения кодируются по старому формату, сохраняются в буфере и сразу отображаются без обращения к реальному радиоканалу. Состояние можно запросить и отключить из настроек.
- Управление ключами (
KEYSTATE,KEYGEN,KEYRESTORE,KEYTRANSFER SEND/RECEIVE). Все операции дублируются в отладочный журнал. - Реализация SHA-256 (
web/libs/sha256.js) позволяет вычислять хеш ключа в среде без WebCrypto. - Переключатель включает принудительное шифрование (даже для старых кадров без флага).
- Кнопка
KEYTRANSFER SENDкопирует публичный ключ в буфер обмена (при поддержке API браузера);KEYTRANSFER RECEIVEсопровождается мигающим индикатором ожидания. - Неблокирующее ожидание
KEYTRANSFER RECEIVEреализовано функциямиclearKeyReceivePollTimer,resetKeyReceiveWaitState,updateKeyReceiveWaitMessage,scheduleKeyReceivePollиpollKeyTransferReceiveStatusвweb/script.js: первый ответstatus="waiting"запускает фоновый поллинг состояния, обновляет текст статуса и удерживает кнопку в режиме ожидания до получения окончательного JSON. - Ответ
KEYSTATEдополнительно содержит поляbaseKey,safeModeиstorageReady: базовый симметричный ключ (hex), признак защищённого режима и готовность хранилища. - Над блоком кнопок выводится строка индикаторов «Ключ / Хранилище / Safe mode», которая меняет цвет при использовании базового ключа, отключении хранилища или активации safe mode и поясняет текущий статус отдельным текстовым сообщением.
- Функция
parseJsonLenientвweb/script.jsсначала пытается очистить и нормализовать ответKEYSTATE, убирая висячие запятые и комментарии, а затем передаёт его вJSON.parse. - Функция
parseKeyStateFallbackвweb/script.jsразбирает нестрогие ответыKEYSTATE(без корректного JSON), извлекая тип ключа, идентификаторы и признак резервной копии. - Функция
deriveKeyIdFromHexвweb/script.jsвычисляет ID ключа из HEX-представления черезsha256Bytes, чтобы сравнить активное значение с базовым и подсветить ситуацию, когда новый ключ ещё не применён. - Блок ENCT показывает исходные данные, шифртекст, тег, nonce и результат дешифрования.
- При активации защищённого режима (отказ
KeyLoader::ensureStorage()) веб-интерфейс показываетsafeMode = true, отключает управление шифрованием и сообщает о необходимости повторной инициализации командыKEYSTORE RETRYпосле устранения причин.
-
Выводит отладочные сообщения, включая операции с ключами и ответы сервера.
-
Поддерживает видимое выделение фокуса для ссылок и кнопок.
-
Кнопки
RSTS FULLиRSTS JSONвыполняют запросы списка сообщений с разным форматом вывода и пишут подробный ответ в отладочный лог. -
Доступна выгрузка полного списка сообщений (
RSTS full json=1) в файлrsts-full-<timestamp>.jsonодной кнопкой. -
Кнопка
Экспорт TXTсохраняет текущий видимый журнал Debug в текстовом файле с отметками времени, типом события, источником и счётчиком повторов. -
Карточки журнала стали компактнее: уменьшены отступы, бейджи переносятся на новую строку без наложения, улучшена читабельность на узких экранах.
-
Фоновая панель показывает суммарный объём принятых данных (
Принято данных) и количество полностью собранных сообщений: счётчик строится по уникальным пакетамready, а человекочитаемое значение выводится черезformatBytesBinary. -
В верхней части размещён блок «Мониторинг опроса RSTS», который показывает текущий интервал, длительность последнего запроса, экспоненциально усреднённое время ответа, число ошибок/таймаутов и случаи блокировки из-за параллельных обращений. Индикатор автоматически обновляется каждую секунду, помогая быстро обнаружить задержки или зависание фонового опроса.
-
Рядом размещён блок «Использование памяти браузера»: он использует API
performance.memoryилиmeasureUserAgentSpecificMemory, отображает текущий размер JS-кучи, лимит, тенденцию за последние минуты и позволяет вручную обновить значения. При отсутствии поддержки браузером выводится пояснение и ориентировочный объём памяти устройства поnavigator.deviceMemory. -
При открытии вкладки выполняется запрос
/api/logs, который подтягивает свежую историю Serial, а затем карточка debug-log пополняется push-событиямиlogпрактически без задержки. -
Любые вызовы
Serial.print/Serial.println, включая вспомогательные макросы и прямой вывод, автоматически зеркалируются в буферLogHook, поэтому журнал Debug всегда содержит те же сообщения, что и физический UART; непечатные байты конвертируются в компактные HEX-блоки с префиксом[BIN]. -
Функции
enqueueLogEntryиflushPendingLogEntriesскладывают новые строки в небольшую очередь и выгружают их в SSE-клиентов изloop(), избавляя USB-Serial от пауз при длительной отправке push-сообщений. -
Для отправки SSE-событий добавлен буфер
SseBufferedWriter, который дробит кадры на куски в соответствии с доступным окном TCP, отслеживает тайм-ауты ожидания освобождения буфера и защищает основной цикл от блокировок при медленных клиентах. -
SSE-канал
/eventsтеперь отдаёт событиеirqс последней расшифровкой флагов SX1262, поэтому блок «Последний IRQ» обновляется даже если зеркалирование Serial отключено или уровень логов поднят до WARN. -
Журнал debug-log перестаёт автоматически прокручиваться, если пользователь пролистал историю вверх; возврат к последним записям снова закрепляет прокрутку у конца списка.
-
Функция
formatTimeOfDayприводит отметки времени журнала и чата к 24-часовому формату с секундами, устраняя расхождения при системной локали с 12-часовым отображением. -
Функция
logErrorEventвweb/script.jsагрегирует одинаковые сетевые ошибки и обновляет последнюю запись счётчиком повторов, поэтому временная недоступность устройства больше не заполняет журнал десятками одинаковых строк.
POST /api/tx— отправляет текст черезTxModule(используется кнопкой «Отправить по радио»).POST /api/tx-image— принимает JPEG (320×240) с заголовкамиX-Profile/X-Name, добавляет его в очередь бинарных сообщений и передаёт в ESP32 без дополнительной обработки.GET /cmd?c=<CMD>иGET /api/cmd?cmd=<CMD>— выполнение команд (PI,SEAR,BANK,CH,CHLIST,STS,RSTS,RXS,INFO,VER). Параметры передаются черезvилиbank.- HTTP-команды для настройки:
BF,SF,CR,PW,RXBG,PAUSE,ACK,LIGHT,ACKR,ACKT,ACKD,RXSTAT,BCN,TXL,TX,TESTMODE(0/1/toggle — управление тестовым режимом TX/RX). ACKDуправляет задержкой отправки подтверждения (0–5000 мс) — значение передаётся вTxModule::setAckResponseDelay().RXSвыводит сводкуrx.dropStats()— суммарный счётчик отброшенных кадров и топ причин. Параметрtop(n,limit) ограничивает размер выборки (по умолчанию 5),fmt=json(json=1) переключает формат.RXS RESETмгновенно обнуляет накопленные значения и возвращает пустую статистику (флагreset=trueдобавляется в JSON). Команда доступна и через Serial (RXS,RXS JSON,RXS RESET).RXSTATотдаёт агрегированную статистику дропов и последние конфликты индексов фрагментов (fragmentMismatchHistory()), позволяя оперативно выявлять разрывы последовательности.GET /libs/geostat_tle.js— отдаёт встроенный список TLE для офлайн-работы вкладки Antenna helper.GET /libs/mgrs.js— возвращает библиотеку преобразования квадрата MGRS в координаты для вкладки Antenna helper.GET /libs/freq-info.csv— отдаёт справочник частот, используемый вкладкой Channels/Ping (совпадает с CSV изweb/libs).GET /api/logs?n=<count>— возвращает JSON\"logs\"с массивом объектов{id, uptime, text}для инициализации вкладки Debug.- Команды работы с ключами возвращают JSON (
KEYSTATE,KEYGEN,KEYRESTORE,KEYTRANSFER SEND,KEYTRANSFER RECEIVE, а также совместимыеKEYSEND/KEYRECV). GET /api/cmd?cmd=KEYRECV&peer=<HEX>— передаёт публичный ключ напарника вcmdKeyReceiveSecure; параметрpeerзаменяет устаревший псевдонимpub, который продолжает поддерживаться для обратной совместимости.- Ответ
RSTS full=1включает список элементов с полямиname,type,len,text,hex.
RXSTAT доступен одновременно по Serial и HTTP (fmt=json или json=1 переключают формат).
Помимо суммарных счётчиков из dropStats() команда возвращает массив fragmentMismatches с полями:
msgId— идентификатор сообщения, к которому относится конфликт.expected— индекс фрагмента, который ожидалRxModule.actual— индекс, полученный фактически.fragCnt— сколько фрагментов заявлено в кадре.ageMs— время, прошедшее с момента фиксации (милисекунды).
Рекомендации по устранению:
- Если
actualсбрасывается в0, передатчик начал новое сообщение до завершения текущего — проверьте порядок вызововTxModule::queue()и наличие повторных отправок из-за тайм-аутов ACK. - Когда
actual > expected, вероятна потеря одного или нескольких фрагментов. Стоит повысить класс QoS сообщения или увеличить паузуPAUSE, чтобы дать радиоканалу стабилизироваться. - При многократных повторениях одного
msgIdубедитесь, что приложение не переиспользует идентификаторы и не сбрасывает очередьTxModuleво время передачи. - Если значения
ageMsмалы, конфликты продолжают происходить — сохраните выводRXSTAT json=1и проанализируйте детализациюdrop.details, чтобы найти типовые параметры.
После устранения причин можно вызвать rx.resetDropStats() и очистить историю fragmentMismatchHistory()
через повторный вызов RXSTAT, чтобы убедиться в исчезновении новых записей.
-
Ключи на ПК сохраняются в файле
key_storage/key.stkey. Запись содержит две части: актуальный (current) и предыдущий (previous) набор данных. Это позволяет мгновенно вернуть старый ключ без доступа к дополнительным файлам. -
В хостовых сборках приватный корневой ключ перед сохранением шифруется ключом устройства. По умолчанию используется предустановленный мастер-ключ и выводится предупреждение. Чтобы задать собственное значение, установите переменную окружения
KEYLOADER_HOST_MASTER_KEY(64 HEX-символа, 32 байта). -
На ESP32 по умолчанию используется NVS/Preferences. Раздел SPIFFS больше не применяется, поэтому требования к файловой системе для ключевого хранилища отсутствуют.
-
Перед обращением к NVS прошивка проверяет состояние Flash Encryption. При выключенной защите попытки чтения или записи завершаются ошибкой с уведомлением в Serial — необходимо включить шифрование флеша до использования
KeyLoader. -
Каждая запись включает корневую пару Curve25519, симметричный ключ AES-CCM, соль нонсов и последний публичный ключ удалённой стороны. При обновлении текущее состояние автоматически перемещается в секцию
previous. -
Для явного выбора хранилища доступна команда
KEYSTORE(Serial/HTTP API):KEYSTORE AUTO— автоматический выбор (по умолчанию).KEYSTORE NVS— принудительное использование Preferences/NVS (ESP32).KEYSTORE FS— использование локальной файловой системы (только в хостовых сборках).KEYSTORE RETRY— повторная проверка и инициализация хранилища после устранения ошибок.
-
При ошибке подготовки хранилища устройство переходит в защищённый режим: шифрование отключается, команды
ENCи все операции с ключами возвращают ошибкуkey-safe-mode, а ответыKEYSTATEиKEYSTOREпоказываютsafeMode = trueиready = false. Дополнительно полеcontext(вKEYSTORE) иsafeModeContext(вKEYSTATE) содержат текстовую причину последней активации safe mode, чтобы было понятно, какая операция инициировала блокировку. После устранения причины выполнитеKEYSTORE RETRY(через Serial или HTTP), чтобы заново вызватьKeyLoader::ensureStorage()и снять блокировку. -
Все команды управления ключами (
KEYGEN,KEYGEN PEER,KEYRESTORE,KEYSEND/KEYTRANSFER SEND,KEYRECV/KEYTRANSFER RECEIVE) теперь перед выполнением автоматически пытаются повторно инициализировать хранилище. Если ошибка была временной (например, NVS успел подготовиться после старта), защита снимается без явного вызоваKEYSTORE RETRY, и команда продолжит работу.
- Вкладка Security показывает тип активного ключа (
LOCALилиEXTERNAL), идентификатор (первые байты SHA-256 симметричного ключа), публичный корневой ключ, наличие резервной копии и используемое хранилище (storage/preferredв JSON ответах API). - Кнопка KEYGEN генерирует новую пару Curve25519, обновляет симметричный ключ и сохраняет файл
key.stkey, одновременно обновляя модулиTxModuleиRxModule. Веб-интерфейс корректно обрабатывает текстовые ответы (OK,KEYGEN DONE), даже если прошивка временно не возвращает JSON, и автоматически запрашивает актуальное состояние ключа. Из текстового ответа без JSON дополнительно извлекаютсяID,PUB,PEER, наличие резервной копии и тип ключа, чтобы карточка состояния заполнилась сразу. - Кнопка KEYGEN PEER пересчитывает симметричный ключ на основе сохранённого публичного ключа
удалённого устройства (поле
peer), повторно запуская процедуру ECDH. Кнопка активна только когда в состоянии есть непустое значениеpeer, что предотвращает случайные запросы без данных. - KEY RESTORE возвращает
key.stkeyизkey.stkey.old(если существует) и перечитывает ключи. - KEYTRANSFER SEND формирует защищённый LoRa-кадр с публичным ключом устройства, шифрует его корневым AES-ключом и отправляет партнёру. В актуальном протоколе (версия 2) для каждого сеанса генерируется эпемерная пара X25519, публичный ключ включается в полезную нагрузку, а приватный ключ очищается сразу после обмена. Если удалённая сторона отвечает кадрами старого формата, прошивка фиксирует это и автоматически возвращается к совместимому кадру версии 1 без эпемерных полей. Веб-интерфейс дополнительно копирует ключ в буфер обмена.
- KEYTRANSFER RECEIVE запускает конечную автоматику ожидания кадра, сразу возвращает JSON
{"status":"waiting"}и не блокирует основной цикл:radio.loop(),tx.loop(),flushPendingLogEntries()иmaintainPushSessions()продолжают выполняться, поэтому SSE-клиенты продолжают получать логи. При получении кадра автоматика публикует результат в Serial на следующем проходеloop(), а HTTP-клиент может получить готовый JSON повторным запросом команды. В кадрах версии 2 общий секрет вычисляется из локального эпемерного приватного ключа и удалённого эпемерного публичного ключа; после завершения сеанса временные ключи затираются. При тайм-ауте команда возвращает{"error":"timeout"}. - HKDF-SHA256 используется для вывода симметричных ключей и вспомогательной соли нонсов: локальная
генерация (
KEYGEN) использует публичный ключ устройства в качестве соли, а обмен с собеседником — отсортированную пару публичных ключей и разныйinfoдля статического/эпемерного режима. При загрузке хранилища старые записи автоматически мигрируются на новое вычислениеnonce_salt.
- После успешного применения публичного ключа прошивка вычисляет идентификатор активного сеансового
ключа (
KeyLoader::keyId(KeyLoader::loadKey())) и сравнивает его сremote_id, переданным в кадре. Для протокола версии 2 эта проверка пропускается: идентификатор можно вычислить только после завершения эпемерного обмена, а отправитель не знает локальный приватный ключ. В режиме совместимости (версия 1) несоответствие по-прежнему приводит к откату на предыдущий снимок (restorePreviousKeyили локальный бэкап) и установке ошибкиverify. - Лог (
SimpleLogger) фиксирует сообщение видаKEYTRANSFER MISMATCH local/remote, а Serial выводит подробности и уведомляет об откате ключа. HTTP-команда/cmd?cmd=KEYTRANSFER&v=RECEIVEпри первом вызове запускает ожидание и возвращает{"status":"waiting"}, при последующих запросах выдаёт готовый JSON с результатом (makeKeyStateJson()или{"error":...}), а Serial печатает тот же JSON автоматически при следующем проходеloop(). - Состояние
keyTransferRuntimeпосле сбоя очищается, поэтому ожидание нового кадра можно повторить без перезапуска устройства: достаточно заново отправить командуKEYTRANSFER RECEIVE.
Дополнительно для хостовых сборок генератор crypto::x25519::random_bytes смешивает несколько
источников энтропии: std::random_device (при доступности), /dev/urandom, метки времени и адреса
стека. Это гарантирует уникальный seed между перезапусками даже при псевдослучайном поведении
std::random_device.
- Если в NVS или в файле
key_storage/key.stkeyсохранена запись старого формата (один ключ +key.stkey.old), модульKeyLoaderавтоматически загружает эти данные и перепаковывает их в новую структуруcurrent/previous. - Дополнительных действий не требуется: резервная копия будет перенесена в секцию
previous, а вспомогательные ключи (key.stkey.old,backup) удалятся при следующей успешной записи.
Тестовая команда формирует короткое сообщение, шифрует его, расшифровывает обратно и выводит результат сравнения.
Запуск теста на ПК:
g++ -I. tests/test_enct.cpp libs_includes.cpp -std=c++17 && ./a.outЧерез Serial (в src/main.cpp):
ENCT
ENCT: успех
Обмен сообщениями KEYTRANSFER использует отдельный статический AES-ключ из libs/key_transfer.
Он шифрует только LoRa-кадры с публичными ключами и не влияет на рабочий симметричный ключ трафика.
KeyTransfer::setTrustedRoot(const std::array<uint8_t,32>&)— сохраняет публичный корневой ключ удостоверяющего центра, относительно которого проверяется цепочка доверия. Корень хранится в памяти процесса и используется для всех входящих кадров.KeyTransfer::setLocalCertificate(const CertificateBundle&)— регистрирует локальную цепочку сертификатов Ed25519, которая будет автоматически добавлена при формировании кадров черезKeyTransfer::buildFrame().KeyTransfer::verifyCertificateChain(const std::array<uint8_t,32>&, const CertificateBundle&, std::string*)— проверяет подписи Ed25519 в цепочке и подтверждает, что она восходит к доверенному корню.- Если доверенный корень не настроен (устройства используют одинаковую прошивку и общий стек), прошивка принимает кадр с сертификатом без проверки цепочки и фиксирует предупреждение в журнале, чтобы обмен ключами оставался совместимым.
KeyTransfer::hasTrustedRoot()/getTrustedRoot()иKeyTransfer::hasLocalCertificate()/getLocalCertificate()позволяют проверить наличие данных перед отправкой или обработкой кадра.
Для проверки подписей используется библиотека libsodium (crypto_sign_ed25519_verify_detached). В
хостовых сборках необходимо установить пакет libsodium-dev и при сборке тестов добавлять флаг
-lsodium:
g++ -I. tests/test_key_transfer.cpp \
libs/key_transfer/key_transfer.cpp \
libs/frame/frame_header.cpp \
libs/scrambler/scrambler.cpp \
libs/crypto/aes_ccm.cpp \
libs/crypto/ed25519.cpp \
libs/crypto/curve25519_donna.cpp \
libs/crypto/x25519.cpp \
libs/mbedtls/aes.c \
libs/mbedtls/ccm.c \
-lsodium -std=c++17 && ./a.outПолный справочник вынесен в файл docs/packet_types.md. Ниже приведена краткая сводка основных форматов:
- Пользовательские кадры — стандартные сообщения с заголовком
FrameHeader, опциональным префиксом[TAG|i/n]и поддержкой шифрования/кодирования. - RAW/SP/GO — имена элементов
ReceivedBuffer: сырые вставкиR-000000|N, промежуточныеSP-xxxxxи финальныеGO-xxxxx. - ACK — подтверждение в виде однобайтового маркера
0x06. - Пинг — пятибайтовый тестовый пакет, который должен вернуться без изменений.
- Маяк — служебный пакет
BEACONс XOR-идентификатором. - KEYTRANSFER — защищённый кадр с публичным ключом для обмена корневой парой.
- Legacy тест — исторический формат
SendMsg_BR, используемый режимомTESTRXMдля эмуляции входящих сообщений.
MessageBuffer(size_t capacity, size_t slot_size = 256)— создать буфер с ограничением на число сообщений и фиксированным размером слота под каждый фрагмент без повторного выделения памяти.- Конструктор автоматически исправляет нулевые параметры (вместимость и размер слота) до минимально
допустимого значения
1, чтобы избежать деления по модулю нулевой ёмкости и пустых сообщений. uint32_t enqueue(const uint8_t* data, size_t len)— добавить сообщение, при переполнении вернуть0.size_t freeSlots() const— количество свободных слотов.bool dropLast()— удалить последнее сообщение (откат).bool hasPending() const— наличие сообщений.bool pop(uint32_t& id, std::vector<uint8_t>& out)— извлечь сообщение и его идентификатор.const std::vector<uint8_t>* peek(uint32_t& id) const— указатель на данные первого сообщения без извлечения.size_t slotSize() const— узнать, какой объём данных допустим в одном слоте очереди.
std::string pushRaw(uint32_t id, uint32_t part, const uint8_t* data, size_t len)— сохранить сырой пакет и получить имяR-000000|номер.std::string pushSplit(uint32_t id, const uint8_t* data, size_t len)— сохранить объединённые данныеSP-00000.std::string pushReady(uint32_t id, const uint8_t* data, size_t len)— сохранить готовые данныеGO-00000.Item::nameхранит заранее подготовленную подпись, повторные обращения не требуют форматирования.bool popRaw(Item& out),bool popSplit(Item& out),bool popReady(Item& out)— извлечь данные.std::vector<std::string> list(size_t count)— имена последних элементов (не болееcount), начиная с самых свежих.std::vector<SnapshotEntry> snapshot(size_t count)— копия последних элементов с типом очереди для формирования JSON-ответаRSTS.void setNotificationCallback(NotificationCallback cb)— назначить обработчик, который вызывается при каждомpushRaw/pushSplit/pushReady.- При активном
RxModule::setBuffer()короткие и повреждённые кадры без заголовка сохраняются как RAW-пакеты с идентификатором0и увеличивающимсяpart, чтобы не терять последовательность одиночных байтов и не смешивать их с обычными сообщениями.
PacketSplitter(PayloadMode mode, size_t custom = 0)— создать делитель с нужным режимом или произвольным размером блока.void setMode(PayloadMode mode)иvoid setCustomSize(size_t custom)— сменить режим и размер.uint16_t splitAndEnqueue(MessageBuffer& buf, const uint8_t* data, size_t len)— разбить данные на части с проверкой свободных слотов и откатом при ошибке.- Для многочастных сообщений в начало каждого сегмента добавляется префикс
[ABCDE|1/5]: общее имя, номер текущей части и общее количество фрагментов. ИдентификаторABCDEтеперь выдаётся монотонным счётчиком и сериализуется восьмизначным шестнадцатеричным числом ([0000ABCD|1/5]), что гарантирует уникальность в рамках сессии. Получатель использует эти пометки, чтобы заново собрать изображение или длинный текст в исходном порядке. - Дополнительно поле
frag_cntв заголовке кадра передаёт точное количество фрагментов: как только принимающая сторона увидит часть с индексомfrag_idx = frag_cnt - 1,PacketGathererпонимает, что сообщение собрано полностью и можно отдавать полезную нагрузку дальше.
PacketGatherer(PayloadMode mode, size_t custom = 0)— создать собиратель.void reset()— очистить состояние.void add(const uint8_t* data, size_t len)— добавить часть сообщения.bool isComplete() const— проверить завершение сбора (актуально при фиксированном размере блока).const std::vector<uint8_t>& get() const— получить готовое сообщение.- Сборщик автоматически подстраивает базовый размер блока по первому фрагменту и сбрасывает накопленное,
если приходит новый
msg_id— так исключается смешивание незавершённых сообщений разных пакетов.
bool encode(uint8_t* out, size_t out_len, const uint8_t* payload, size_t payload_len)— записать заголовок в буфер по компактной схеме без вычисления CRC; длина полезной нагрузки упаковывается в полеpackedи дополнительно передаётся параметром.static bool decode(const uint8_t* data, size_t len, FrameHeader& out)— разобрать заголовок в форматеверсия | msg_id | frag_cnt | packed.uint8_t getFlags() const/void setFlags(uint8_t)— доступ к флагам, упакованным в восемь старших битpacked.uint16_t getFragIdx() const/void setFragIdx(uint16_t)— получить или задать индекс фрагмента, занимающий 12 средних битpacked.uint16_t getPayloadLen() const/void setPayloadLen(uint16_t)— получить или задать длину полезной нагрузки (12 младших битpacked).- Флаги
FLAG_CONV_ENCODED,FLAG_RS_ENCODEDиFLAG_BIT_INTERLEAVEDотражают реальное применение свёртки, кода Рида—Соломона и битового интерливинга. При смене настроек на лету заголовок всегда сообщает фактический режим кодирования, поэтому приёмник может автоматически адаптироваться. - Заголовок занимает 12 байт: байт версии, по два байта для
msg_idиfrag_cnt, четыре байтаpackedи три зарезервированных нуля для выравнивания тега AEAD. - Контроль целостности обеспечивается AEAD-тегом; поля
ack_mask,hdr_crcиframe_crcудалены, аcrc16()оставлена как вспомогательный инструмент для проверки пилотов и исторических форматов.
TxModule(IRadio& radio, const std::array<size_t,4>& capacities, PayloadMode mode)— инициализация с четырьмя очередями QoS.void setPayloadMode(PayloadMode mode)— сменить размер пакета.uint16_t queue(const uint8_t* data, size_t len, uint8_t qos = 0)— добавить сообщение в очередь.bool loop()— попытаться отправить один кадр (фрагмент) очередного сообщения. Возвращаетtrue, если кадр ушёл в эфир, иfalse, когда требуется дождаться паузы/ACK или очередь пуста. Для передачи длинного сообщения требуется несколько последовательных вызововloop().- Прогресс по каждому сообщению (индекс фрагмента и таймер паузы) хранится внутри очереди, поэтому можно без блокировок продолжать передачу после ожидания ACK или внешних операций.
void setSendPause(uint32_t pause_ms)иuint32_t getSendPause() const— пауза между отправками.void setAckTimeout(uint32_t timeout_ms)иuint32_t getAckTimeout() const— управление тайм-аутом ожидания ACK. Значение0отключает ожидание подтверждений: флагwaiting_ack_сразу сбрасывается, пакет помечается доставленным и очередь продолжает обработку следующих элементов без паузы.void setAckResponseDelay(uint32_t delay_ms)— задержка перед отправкой ACK после успешного приёма; значение0отключает дополнительную паузу и подтверждение уходит сразу. Параметр доступен через командыACKD(Serial/HTTP) и одноимённое поле на вкладке Settings.- Подтверждение (
ACK) формируется отдельной веткой как минимальный кадрFrameHeaderбез дублей и CRC с полезной нагрузкой0x06. Шифрование и свёрточное кодирование пропускаются, поэтому флагиFLAG_ENCRYPTED/FLAG_CONV_ENCODEDостаются сброшенными. - Подробное описание формата и особенностей обработки подтверждения приведено в разделе «Структура ACK-пакета без дополнительного кодирования».
void prepareExternalSend()/void completeExternalSend()— учёт глобальной паузы при внешней отправке черезRadioSX1262.- При ожидании подтверждения радиоинтерфейс переводится в режим приёма; пауза применяется между частями одного сообщения.
- Последовательность кодирования:
rs255223::encode()→byte_interleaver::interleave()→conv_codec::encodeBits()→ (опционально)bit_interleaver::interleave()→scrambler::scramble(). static void setEncryptOverrideForTests(TxModule::EncryptOverride fn)иstatic void resetEncryptOverrideForTests()— тестовый хук для подмены реализации ChaCha20-Poly1305. Используется только вsrc/tests, чтобы эмулировать ошибки подготовки фрагментов без переписывания производственного кода.
void setCallback(RxModule::Callback cb)— обработчик входящих данных.void setAckCallback(std::function<void()> cb)— уведомление о поступившем ACK; удобно привязывать кTxModule::onAckReceived()или собственной логике подтверждений.void onReceive(const uint8_t* data, size_t len)— принять кадр, проверить CRC и передать данные; слишком короткие или повреждённые фреймы без валидного заголовка направляются напрямую в пользовательский колбэк (и вReceivedBufferпри его наличии) без дескремблирования.void enableProfiling(bool enable)— включает накопление телеметрии по этапам обработки кадра; вместе сlastProfiling()позволяет контролировать задержки и точки отбраковки.RxModule::ProfilingSnapshot lastProfiling() const— возвращает последний снимок: длительность этапов (descramble,header,payload_extract,decode,decrypt,assemble,deliver), флаги использования свёртки/дешифрования и причину досрочного выхода (если кадр не был собран).RxModule::DropStats dropStats() const— агрегированная статистика причин отбраковки кадров; полезно для анализа деградации канала при низком SNR и повторяющихся ошибок. Для каждой причины дополнительно накапливаются уникальные параметры (например,expected,got,offset), а также несколько последних примеров для оперативного разбора инцидентов без покадровых логов.std::vector<RxModule::FragmentMismatchInfo> fragmentMismatchHistory() const— кольцевой буфер последних конфликтов индексов фрагментов: возвращает идентификатор сообщения, ожидаемый и фактический индексы, объявленноеfrag_cntиage_ms, показывающий давность события. Эти данные помогают быстро понять, нарушена ли последовательность доставки или очередь QoS.void resetDropStats()— сброс накопленной статистики дропа, чтобы оценить свежую картину после изменения параметров приёма.void setBuffer(ReceivedBuffer* buf)— привязать буфер для автоматического сохранения сообщений.void tickCleanup()— запустить таймерную очистку кэшей свёрточных блоков и незавершённыхsplitвручную; полезно вызывать из главного цикла даже при отсутствии новых кадров.- Буферы кадра, полезной нагрузки и результата переиспользуются между вызовами.
- Обратная цепочка обработки:
scrambler::descramble()→ (опционально)bit_interleaver::deinterleave()→conv_codec::viterbiDecode()→byte_interleaver::deinterleave()→rs255223::decode(). - Префиксы вида
[TAG|n/m]используются для ленточного объединения:RxModuleнакапливает сегменты в промежуточном буфереpending_split_, фиксирует прогресс черезpushSplit, а готовый результат (n == m) выдаёт одним вызовом колбэка и сохраняет вReceivedBuffer. - Модуль отслеживает
msg_idи ожидаемые индексы фрагментов, сбрасывая сборку при разрыве последовательности — неполные сообщения больше не смешиваются с новыми кадрами. - Кэш свёрточных блоков автоматически очищается по таймеру; одновременно
pending_conv_иpending_split_ограничиваются 64 свежими элементами — самые старые записи удаляются, чтобы не накапливать лишнюю память. - Для временной диагностики
RxModuleсверяетhdr.getPayloadLen()с собственными расчётами длины свёртки и выводит подробные логи по каждому ключуpending_conv_: объём пришедшего фрагмента, накопленный объём, ожидаемую длину и общий размер всех незавершённых свёрточных буферов. - Принудительное шифрование теперь не блокирует приём открытых кадров: при ошибке AES-CCM фрагмент обрабатывается как незашифрованный с предупреждением в лог.
- Детальный разбор этапов и типовых мест задержек приведён в
docs/rx_pipeline_profiling.md.
Слишком короткие кадры (< 40 байт) и пакеты, в которых не удалось восстановить заголовок, теперь
возвращаются напрямую в пользовательский колбэк без дескремблирования и последующей проверки. Это
позволяет не терять одиночные байты от старых клиентов или сервисных каналов. При активном
ReceivedBuffer такие входящие попадают в очередь RAW со специальным идентификатором 0 и
монотонным part, что отделяет их от нормальных сообщений. Ограничения режима:
- данные не дешифруются и не проходят проверку CRC — ответственность на потребителе;
msg_idотсутствует, поэтому привязка к собранным сообщениям невозможна;- пакеты не участвуют в сборке
PacketGatherer, не образуются элементыSP-иGO-.
Клиенты, которые анализируют вывод RSTS, должны фильтровать записи R-000000|N и относиться к ним
как к самостоятельным сырым пакетам.
bool begin()— инициализация радиомодуля с возвратом параметров к значениям по умолчанию.void send(const uint8_t* data, size_t len)— отправка пакета.bool ping(...)— пинг с ожиданием эха и возвратом параметров приёма.void loop()иvoid setReceiveCallback(...)— обработка готовности пакета и регистрация колбэка.float getLastSnr() const,float getLastRssi() const— параметры последнего пакета.uint8_t randomByte()— случайный байт от радиомодуля.- Настройка и получение параметров:
setBank,setChannel,setBandwidth,setSpreadingFactor,setCodingRate,setPower,getBank,getChannel,getBankSize,getBandwidth,getSpreadingFactor,getCodingRate,getPower,getRxFrequency,getTxFrequency. bool setRxBoostedGainMode(bool enabled),bool isRxBoostedGainEnabled() const— управление режимом повышенного усиления приёмника.static uint16_t bankSize(ChannelBank bank),static float bankRx(...),static float bankTx(...),static const char* bankDescription(...)— справочные методы.static void logIrqFlags(uint32_t flags)— форматированный вывод активных IRQ-флагов радиочипа в журнал черезDEBUG_LOG, чтобы сообщения не поднимали общий уровень логирования. При наличии в RadioLib отдельных масокRADIOLIB_IRQ_RX_TIMEOUTиRADIOLIB_IRQ_TX_TIMEOUTвывод различает таймауты приёма и передачи для ускоренной диагностики.- При успешной инициализации
begin()один раз выводит отладочное сообщениеIRQ logger started, подтверждая запуск механизма логирования IRQ. void flushPendingIrqLog()— обходной ручной вызов переноса и вывода отложенных IRQ-логов.bool resetToDefaults()— возврат параметров к значениям по умолчанию.- Вспомогательная обёртка
PublicSX1262добавляет совместимые методыgetIrqFlags(),clearIrqFlags(...)иgetIrqStatus(uint16_t*), чтобы прошивка одинаково собиралась с разными версиями RadioLib. - После применения настроек по умолчанию LoRa-модем переводится в режим implicit header с фиксированной
длиной полезной нагрузки
64байта, преамбулой из16символов, аsend()автоматически дополняет более короткие кадры нулями.ping()временно включает explicit header, чтобы передавать сервисный пакет без привязки к длине данных. loop()блокирует доступ к радиочипу через общий мьютекс и пропускает чтение, если модуль занят внешней операцией (send/ping), что исключает гонку и случайные перезагрузки при одновременном доступе из разных задач.
void resetBuffer()— очистить буфер.bool appendToBuffer(const String& line)— добавить строку с проверкой переполнения.
String makeAccessPointSsid()— формирует имя точки доступа с коротким суффиксом из уникального идентификатора устройства.void resetKeyTransferWaitTiming()— сбрасывает временные отметки ожидания KEYTRANSFER, чтобы повторный запрос начинался с корректных значений дедлайна после успеха, тайм-аута или ошибки.void configureKeySafeModeController(std::function<void(bool)> applyEncryption, bool* encryptionFlag, bool* safeModeFlag, bool* storageReadyFlag)— связывает логику safe mode с глобальными флагами и колбэком шифрования, сохраняет исходноеencryptionEnabled, отключает шифрование при недоступности хранилища и гарантированно восстанавливает режим либо фиксирует ошибку в журнале при неудаче.bool keySafeModeHasReason()— сообщает, есть ли сохранённый контекст причины включения защищённого режима.std::string keySafeModeReason()— возвращает текстовый контекст последней активации safe mode (пустая строка, если причина не зафиксирована).
std::vector<uint8_t> utf8ToCp1251(const std::string& in)— UTF-8 → CP1251.std::string cp1251ToUtf8(const std::vector<uint8_t>& data)— CP1251 → UTF-8 (неизвестные символы заменяются на?).
void encode(const uint8_t* in, uint8_t* out)— кодирует 223 байта в 255.bool decode(const uint8_t* in, uint8_t* out)— декодирует 255 байт и возвращает 223 байта.
void encodeBits(const uint8_t* in, size_t len, std::vector<uint8_t>& out)— свёрточное кодирование (R=1/2).bool viterbiDecode(const uint8_t* in, size_t len, std::vector<uint8_t>& out)— декодирование алгоритмом Витерби.
void interleave(uint8_t* buf, size_t len)— битовый интерливинг.void deinterleave(uint8_t* buf, size_t len)— обратный интерливинг.
std::array<uint8_t,16> loadKey()— загрузить активный симметричный ключ (при отсутствии создаётся запись по умолчанию).bool saveKey(const std::array<uint8_t,16>& key, KeyOrigin origin, const std::array<uint8_t,32>* peer, uint32_t salt)— сохранить ключ с указанием происхождения и публичного ключа собеседника.bool generateLocalKey(KeyRecord* out = nullptr)— создать новую пару Curve25519, обновить симметричный ключ и переместить предыдущую запись в секциюprevious(для последующего восстановления). Сеансовый ключ иnonce_saltвыводятся через HKDF-SHA256 с солью из публичного ключа устройства.bool restorePreviousKey(KeyRecord* out = nullptr)— восстановитьkey.stkeyиз резервной копии.bool applyRemotePublic(const std::array<uint8_t,32>& remote, const std::array<uint8_t,32>* remote_ephemeral = nullptr)— вычислить общий секрет и сохранить внешний симметричный ключ. При передаче эпемерного ключа обмен выполняется в рамках протокола версии 2 и использует временную пару X25519, которая очищается после успешного (или аварийного) завершения. Соль HKDF формируется из отсортированной пары публичных ключей, аinfoразличается для статического и эпемерного режимов.bool regenerateFromPeer(const std::array<uint8_t,32>* remote_ephemeral = nullptr, KeyRecord* out = nullptr)— повторно применить сохранённый публичный ключ удалённой стороны; при наличии эпемерного ключа пересчёт выполняется с учётом временной пары.bool hasPeerPublic()— проверить, сохранён ли публичный ключ удалённого собеседника.bool previewPeerKeyId(std::array<uint8_t,4>& key_id)— пересчитать идентификатор ECDH-ключа для текущего партнёра без изменения снимка.KeyState getState()— текущее состояние ключа (тип, идентификатор, публичные ключи, резервная копия).std::array<uint8_t,12> makeNonce(uint8_t version, uint16_t frag_cnt, uint32_t packed_meta, uint16_t msg_id)— сформировать нонс для AEAD (ChaCha20-Poly1305 и совместимого режима AES-CCM).bool startEphemeralSession(std::array<uint8_t,32>& public_out, bool force_new = true)— подготовить эпемерную пару X25519 и вернуть публичный ключ для включения в кадр обмена.bool hasEphemeralSession()— проверить, активна ли временная пара для текущего сеанса.void endEphemeralSession()— стереть эпемерный приватный ключ и сбросить состояние обмена.void setLogCallback(KeyLoader::LogCallback callback)— передать обработчик логов (например, послеSerial.begin()), чтобы накопленные сообщения KeyLoader выгрузились безопасно и не обращались к неинициализированному UART; обработчик должен возвращатьtrueпри успешной доставке сообщения иfalse, если попытку требуется повторить позже (например, до появления USB). ТипKeyLoader::LogCallbackна Arduino получаетconst __FlashStringHelper*, чтобы не копировать строки из PROGMEM, а в хостовых сборках принимаетconst char*.void flushBufferedLogs()— вручную инициировать повторную попытку выгрузки буфера KeyLoader, например, сразу после того как Serial стал доступен без перепривязки обработчика.
Prk extract(const uint8_t* salt, size_t salt_len, const uint8_t* ikm, size_t ikm_len)— стадия HKDF-Extract (HMAC-SHA-256 по соли и входному материалу).bool expand(const Prk& prk, const uint8_t* info, size_t info_len, uint8_t* okm, size_t okm_len)— стадия HKDF-Expand с выводом заданного количества байтов.bool derive(...)— обёртка, последовательно выполняющая Extract и Expand; доступна версия для указателей и шаблонная обёртка для стандартных контейнеров.
bool crypto::chacha20poly1305::encrypt(...)— шифрование с формированием тега Poly1305 и поддержкой дополнительного связанных данных.bool crypto::chacha20poly1305::decrypt(...)— проверка тега Poly1305 и расшифровка.
bool encrypt_ccm(...)— легаси-шифрование для кадров версии 1.bool decrypt_ccm(...)— расшифровка и проверка легаси-тегов.
- Основные параметры запуска выносятся в файл
config/default.ini. Он использует простой формат INI и делится на секции[wifi],[radio]и[keys]. - При старте прошивка читает файл через модуль
libs/config_loader/; если файл отсутствует либо содержит ошибки, в лог выводится предупреждение, а работа продолжается со значениями изDefaultSettings. - Параметры, непосредственно влияющие на инициализацию RadioLib/SX1262 (кроме выбора частот, полосы, SF, CR и мощности), сведены в
src/libs/radio/lora_radiolib_settings.h. В файле перечислены все управляемые опции драйвера с русскоязычными комментариями и дефолтными значениями. - Пример содержимого:
[wifi] ssid=sat_ap password=12345678 ip=192.168.4.1 gateway=192.168.4.1 subnet=255.255.255.0 [radio] bank=TEST channel=0 powerPreset=0 bwPreset=2 sfPreset=2 crPreset=0 rxBoostedGain=true useAck=false ackRetryLimit=3 ackResponseDelayMs=20 sendPauseMs=370 ackTimeoutMs=320 useEncryption=true useRs=false useConv=true useBitInterleaver=true [keys] default=000102030405060708090a0b0c0d0e0f
- Для изменения параметров достаточно отредактировать соответствующие ключи и перезапустить устройство. В хостовой сборке конфигурацию можно перечитать без перезапуска, вызвав
ConfigLoader::reload(). - Настройки
useConvиuseBitInterleaverпозволяют временно отключить свёрточное кодирование и битовый интерливинг для отладки. При выключенииuseConvRS-блоки также не формируются, а заголовок кадра автоматически сообщает фактический режим кодирования. - Все строки допускают комментарии, начинающиеся с
;или#, и игнорируют лишние пробелы. Некорректные значения фиксируются в логе (Config: некорректный …) без остановки прошивки.
- Имя сети формируется автоматически: к базовому префиксу из конфигурации добавляется шестнадцатеричный суффикс из
последних трёх байтов MAC-адреса, поэтому итоговый SSID выглядит как
sat_ap-12ABCD. - Прошивка запускает точку доступа в диапазоне 2,4 ГГц и фиксирует IP-адрес из конфигурации (по умолчанию 192.168.4.1). При сбоях конфигурации используется штатная сетка ESP32/ESP8266, что видно в Serial-журнале по предупреждению «Wi-Fi: не удалось применить статический IP…».
- Добавлена автоматическая повторная инициализация: если запуск точки доступа завершился неудачей, модуль выполняет до трёх попыток и выводит сообщение «Wi-Fi: повторный запуск точки доступа (попытка N)». После успешного старта в журнале появляется строка «Wi-Fi: точка доступа … запущена, IP …».
- Перед настройкой сети вызывается функция
waitForSerial(timeout_ms), которая ждёт появления USB-Serial не дольше заданного интервала (1,5 с по умолчанию) и не позволяет зависнуть старту точки доступа при отключённом ПК. - Если сеть по-прежнему не видна, проверьте питание, антенну и наличие сообщений об ошибках в Serial-консоли. При необходимости можно перезапустить устройство: повторные попытки очистят сохранённые STA-параметры и принудительно вернут модуль в режим точки доступа.
BANK <e|w|t|a|h|n>— выбрать банк каналов.CH <номер>— переключить канал в текущем банке.PI— отправить пинг, ожидать эхоPING_WAIT_MSмиллисекунд и вывести RSSI/SNR либоtimeout.SEAR— последовательный пинг всех каналов с выводом RSSI/SNR и отметкой времени ожидания.ACK [0|1]— включить/выключить подтверждения.LIGHT [0|1]— включить или выключить режим прямой отправки текста (Light pack) с raw-пакетом напрямую в SX1262.RXBG <0|1|toggle>— переключить режим повышенного усиления приёмника SX1262.KEYTRANSFER SEND— отправить публичный корневой ключ по защищённому каналу.KEYTRANSFER RECEIVE— ожидать защищённый кадр и применить ключ при успехе.- Дополнительно доступны команды
BF,SF,CR,PW,RXBG,TX,TXL,BCN,INFO,STS <n>,RSTS <n>(список имён) /RSTS FULL <n>(RSTS JSON <n>для вывода JSON с данными),ACKR <повторы>,PAUSE <мс>,ACKT <мс>,ACKD <мс>,ENC [0|1],LIGHT,TESTRXM. - Значение
ACKT 0выключает ожидание подтверждений: текущее сообщение считается доставленным, очередь не блокируется и немедленно берёт следующий пакет. - Команда
ACKDзадаёт задержку ответа на входящий пакет (0–5000 мс) до постановки ACK в очередь.
Дополнительно введён банк HOME (буква h/H) — это подборка из семи каналов полного банка ALL.
Банк NEW (буква n/N) содержит все новые пары частот из каналов 163–308 и позволяет работать
с обновлённым списком без переключения на полный банк ALL.
Значения частот для быстрого доступа:
- RX: 262.425, 257.500, 257.150, 255.775, 267.200, 249.930, 245.950 МГц.
- TX: 296.025, 311.350, 298.150, 309.300, 308.200, 308.750, 299.400 МГц.
// Передача
шифрование -> PacketSplitter (223 байта) -> rs255223::encode ->
byte_interleaver::interleave -> conv_codec::encodeBits ->
bit_interleaver::interleave -> scrambler::scramble -> отправка
// Приём
scrambler::descramble -> bit_interleaver::deinterleave ->
conv_codec::viterbiDecode -> byte_interleaver::deinterleave ->
rs255223::decode -> PacketGatherer -> обработка сообщения
- Криптографические проверки (
tests/test_key_transfer.cpp,libs/crypto/*.cpp) используют функциональность libsodium. Убедитесь, что установлен development-пакет библиотеки (для Debian/Ubuntu:sudo apt-get install libsodium-dev).
- Для сборки бинарника
tests/test_key_transfer.cppвместе с криптомодулями можно воспользоватьсяtests/Makefile:При необходимости можно указать другой набор исходников, передавmake -C tests ./tests/build/test_key_transfer
TEST_SRCS="test_key_transfer.cpp test_enct.cpp". - Для проверки форматирования IRQ-логов радиомодуля подготовлен отдельный тест:
Заглушки для Arduino/RadioLib подключаются автоматически, поэтому аппаратное окружение не требуется.
make -C tests build/test_radio_irq_logging ./tests/build/test_radio_irq_logging
- Комплексный тест полного цикла без радиоканала по-прежнему можно собрать напрямую:
g++ -I. -I.. tests/test_processing_without_send.cpp tx_module.cpp rx_module.cpp \ message_buffer.cpp libs_includes.cpp -std=c++17 && ./a.out - В процессе теста проверяется, что короткие сообщения проходят напрямую в колбэк и попадают в
RAW-очередь
ReceivedBuffer, если она активирована. - Проверка логики веб-интерфейса выполняется отдельным набором модульных тестов на Node.js (используется встроенный
node:test):Перед запуском убедитесь, что установлен Node.js версии 18+ (в контейнере используется v22.19).node --test tests/web_ui/web_ui.test.js
- Базовые тесты буфера сообщений, делителя пакетов и формирования кадров находятся в каталоге
tests/. - Тест совместимости
RxModuleс внешнимReceivedBufferподтверждает корректное отображение готовых сообщений. - Тест
tests/test_pilot_marker.cppпроверяет, что новый пилотный маркер не удаляет полезные данные, даже если нагрузка содержит «старую» двухбайтовую сигнатуру.
- Прямая передача данных без изменений:
TxModuleставит сообщения в очередь и отправляет их в LoRa без заголовков, шифрования и ФЕК. - Простая доставка входящих кадров:
RxModuleбез дополнительных проверок пробрасывает полезную нагрузку в пользовательский колбэк. - Базовые очереди сообщений и вспомогательные буферы продолжают работать как оболочка над сырым потоком, сохраняя идентификаторы и минимальный контроль переполнения.
- Вся прежняя логика обработки сохранена в исходниках и может быть возвращена по мере необходимости.
- Вернуть ACK, ретраи и планирование повторов, если потребуется подтверждение доставки.
- Реактивировать шифрование ChaCha20-Poly1305/AES-CCM и управление ключами на уровне конвейера.
- Включить обратно RS-код, интерливинг, свёрточное кодирование и работу с пилотами.
- Снова собрать пайплайн PacketSplitter/PacketGatherer, чтобы поддерживать большие сообщения и управление префиксами.
- Обновить тесты и документацию после включения каждой стадии, чтобы отразить переход от прямого режима к полноценному.
- Радио и планирование
- Расширить управление радиомодулем дополнительными командами и параметрами.
- Внедрить планировщик WFQ для классов QoS.
- Наведение антенны
- Автоматически обновлять TLE и напоминать пользователю о необходимости актуализации данных.
- Расширить поддержку MGRS (точность ниже 100 км, корректная работа в южном полушарии и проверка некорректных квадратов).
- Добавить учёт горизонта и препятствий (маски высоты) и визуальную подсказку с картой линии визирования.
- Обработка сигнала
- Реализовать обработку пилотов в RxPipeline (
updatePhaseFromPilot) вместо простого удаления. - Доработать алгоритмы коррекции ошибок и добавить дополнительные схемы шифрования при необходимости.
- Добавить расширенные метаданные для сырых пакетов (например, отметки времени или источник), чтобы упростить автоматический анализ потока.
- Реализовать обработку пилотов в RxPipeline (
- Текст и совместимость
- Добавить поддержку дополнительных кодировок и символов в
TextConverter. - Сохранение собранной программы и расширенную обработку команд завершения в SerialProgramCollector.
- Добавить поддержку дополнительных кодировок и символов в
- Тесты и верификация
- Расширить тесты обработки пилотов (базовая проверка отсутствия потерь данных добавлена в
tests/test_pilot_marker.cpp). - Добавить отдельные тесты/настройки для битового интерливинга и свёрточного кодека.
- Расширить тесты обработки пилотов (базовая проверка отсутствия потерь данных добавлена в
- Обмен изображениями
- Добавить автоматические проверки обработки изображений в браузере и на прошивке.
- Расширить контроль очистки кэша и параметров профилей (HEIC/EXIF) после полевых испытаний.
- Конфигурация
- Добавить редактирование
config/default.iniчерез веб-интерфейс и синхронизацию изменений без перезагрузки устройства.
- Добавить редактирование