Skip to content

BeaconRadio — носимый SOS-маячок на LoRaWAN + GPS

Носимый маячок безопасности (легенда — для человека с деменцией): GPS-трек, кнопка SOS, автоопределение падения и неподвижности по акселерометру MPU6050, пред-тревога 10 с с отменой. Плата E77-900MBL (STM32WLE5) собирает координату, статус и заряд в 12-байтовый пакет и шлёт его по LoRaWAN (FPort 10 — телеметрия, FPort 20 — тревога) через шлюз E80 → ChirpStack → Node-RED → TimescaleDB → Grafana. Исходный код, теория интеграции GPS и разбор отладки — в firmware/Projects/LORAWAN/BeaconRadio.

Это фундаментальный модуль сквозного курса «Радио своими руками» — на нём рождаются понятия (пакет, CRC, дБм, статус-байт, конвейер данных), которые дальше становятся инструментами в проектах охоты на лис, PowerSentry и радиозанавеса. Главный урок проекта глубже техники: в устройстве, от которого зависит безопасность человека, критическая величина — не координата, а качество решения «упал/не упал». Python-стенд fall_detector_sim.py повторяет трёхфазную FSM детектора падения построчно — падать с платой не нужно, а компромисс порогов (ROC) можно пережить руками.

Методическое пособие: BeaconRadio — носимый SOS-маячок

Стенд: плата E77-900MBL (STM32WLE5) + GPS NEO-M8N + акселерометр MPU6050 + кнопка SOS + зуммер. Прошивка собирает координату, статус и заряд в 12-байтовый пакет и шлёт его по LoRaWAN (FPort 10 — телеметрия, FPort 20 — тревога) через шлюз E80 → ChirpStack → Node-RED → TimescaleDB → Grafana. На плате работает машина состояний (FSM) и детектор падения по акселерометру.

Легенда проекта, задающая всю инженерную планку: носимый маячок для человека с деменцией — GPS-трек, кнопка SOS, автоопределение падения и неподвижности, пред-тревога 10 с с возможностью отмены. Отсюда честный разговор о том, что в таком устройстве критическая величина — не координата, а РЕШЕНИЕ «упал/не упал»: пропущенное падение — беда не замечена, ложная тревога — сигналу перестают верить.

Это фундаментальный модуль сквозного курса «Радио своими руками» — на нём рождаются понятия (пакет, CRC, дБм, статус-байт, конвейер данных), которые дальше становятся инструментами в проектах охоты на лис, PowerSentry и радиозанавеса. Полная теория интеграции GPS — в README_GPS_lessons.md, история отладки — в двух врайтапах. Python-стенд fall_detector_sim.py повторяет трёхфазную FSM детектора падения построчно — падать с платой не нужно.

Кто чем пользуется

  1. Инженер отвечает за стенд: распиновку (по мануалу, не по памяти!), питание GPS, серверную цепочку до Grafana, чекпоинты-бипы init.
  2. Учитель проводит уроки-симуляции и полевые проверки по сценарию.
  3. Методист привязывает параметры пакета/фикса/детектора к темам программы.

Модуль 1. Информатика: пакет данных и контроль целостности

Урок 1. 12 байт, каждый с работой: проектируем формат пакета

  • Тема: представление данных, структуры, порядок байт (little-endian).
  • Параметры: BeaconPayload_t, статус-байт, FPort 10/20.

Ход работы. Разбор struct BeaconPayload_t в прошивке по байтам. Ученики раскладывают 12 байт на бумаге: [0] id, [1..4] широта×1e7 (int32), [5..8] долгота×1e7, [9] статус, [10] заряд, [11] CRC8.

На графике:

 [0]  [1  2  3  4] [5  6  7  8] [9]    [10]  [11]
 id   lat * 1e7     lon * 1e7    status batt  CRC8
                                 ▲ 8 битов-флагов: SOS,fall,immobile,gps_ok,low_batt,seq

Разбор. Координата хранится как целое lat×10⁷ (int32), а не как дробное число: 4 байта вместо 8, и никакой возни с плавающей точкой в эфире. Байт статуса упаковывает 5 булевых флагов + 3-битный счётчик seq в один байт — восемь фактов в восьми битах. seq (0..7 по кругу) вместе с FCnt LoRaWAN мгновенно показывает, ГДЕ теряются пакеты: дыры в FCnt на сервере = эфир/шлюз, а кадры есть, но строк в базе нет = проблема в Node-RED. Компактный бинарный формат под задачу — норма во встраиваемых системах, где каждый байт стоит эфирного времени и энергии.

Практическое задание. Соберите на бумаге статус-байт для события «нажата кнопка SOS, фикс GPS свежий, seq=3». Проверьте: должно получиться 0x69 (разложите по битам SOS=1, gps_ok=8, seq«5). Сверьте с логом прошивки из врайтапа шага 2 (там ровно status 0x29→0x49→0x69→0x89).


Урок 2. CRC8: приёмник узнаёт испорченный байт

  • Тема: обнаружение ошибок, полиномиальные вычисления, надёжность канала.
  • Параметры: crc8() (полином 0x07), проверка на сервере до записи в базу.

Ход работы. Показать, что тот же crc8 (полином 0x07) считается и в прошивке (последний байт пакета), и на сервере (Node-RED отбрасывает пакет с несошедшимся CRC). Умышленно «поломать» один байт в учебном разборе и увидеть, что CRC меняется.

Разбор. У LoRa есть свой аппаратный CRC, но проектный CRC8 в payload — это урок: контрольная сумма своими руками, восемь строк кода, показывающих, КАК приёмник узнаёт о повреждении. Данные «делятся» на многочлен 0x07, остаток едет хвостом пакета; сервер делит снова — не сошлось, пакет в мусор, в базу не идёт. Так «мусор» и «тишина» остаются разными диагнозами: битый пакет виден как CRC-fail, а пропавший — как дыра в seq. Тот же принцип — в Ethernet, USB, ZIP.

Практическое задание. Почему CRC считается по байтам [0..10], а сам занимает байт [11]? Что было бы, если бы CRC включал сам себя в расчёт?


Модуль 2. Физика и данные: GPS, качество фикса и спуфинг

Урок 3. NMEA: градусы, минуты и почему нельзя делить на 100

  • Тема: системы счисления координат, единицы измерения, парсинг данных.
  • Параметры: формат NMEA, TinyGPSPlus, талкеры GP/GL/GA.

Ход работы. Показать сырую строку NMEA (например $GPGGA,...,5537.838,N,...) и разобрать: 5537.838 — это 55° 37.838′, что равно 55.6306°, а НЕ 55.37°. Ручной перевод «поделить на 100» даёт ошибку в десятки километров.

На графике:

 NMEA:   5537.838  →  55° + 37.838'/60  =  55.6306°
 ОШИБКА:            →  55.37°  (сдвиг ~30 км!)

Разбор. Координаты в NMEA хранятся в формате «градусы и минуты», а десятичные градусы получаются как градусы + минуты/60. Библиотека TinyGPSPlus делает это правильно, но понимать формат нужно, чтобы не поверить в неверную ручную формулу. Талкеры в начале строки говорят, чьё это решение: $GP=GPS, $GL=ГЛОНАСС, $GA=Galileo, $GN=смешанное — сравнивать надо тип сообщения (3 буквы), а не весь префикс. Поле качества фикса в GGA: 0=нет, 1=GPS, 6=dead reckoning (модуль экстраполирует БЕЗ спутников — для «есть ли фикс» это может быть враньём).

Практическое задание. Переведите 5540.500,N и 03736.200,E в десятичные градусы. Найдите это место на карте — попадёте ли в нужный город?


Урок 4. Дозревание и свежесть: почему первому фиксу нельзя верить

  • Тема: сходимость измерений, погрешность, кэширование и устаревание данных.
  • Параметры: GPS_SETTLE_MS, GPS_VALID_AGE_MS, lastFixMs, флаг ST_GPS_OK.

Ход работы. По коду S_ACQUIRE: первый валидный фикс НЕ отправляется сразу — прошивка ждёт 12 с (GPS_SETTLE_MS) и требует HDOP < 2.5. Обсудить с классом из врайтапа: первое решение после холодного старта уходило в 5+ км от истины при формально хорошем HDOP.

Разбор. Первое решение строится на неполных эфемеридах и плохой геометрии спутников — сходимость приходит за десятки секунд. Отсюда правило дозревания: не брать первый фикс, подождать 10–15 с, требовать HDOP < 2.5. И правило свежести: флаг валидности в телеметрии = «возраст фикса < порога», а не «фикс когда-то был» — иначе замолчавший GPS вечно отдаёт последнюю точку как живую. Нет фикса вовсе → слать последние известные координаты (или 0/0) с флагом валидности 0, чтобы потребитель мог это отличить. Это универсальный принцип работы с любыми кэшированными измерениями: значение без отметки свежести опасно.

Практическое задание. Почему для обычной телеметрии порог свежести 60 с, а для тревоги (ALARM_FRESH_MS) — жёстче, 30 с? Что важнее в момент SOS — успеть отправить или отправить точную координату? Обоснуйте компромисс.


Урок 5. GPS умеет уверенно врать: сигнатура спуфинга

  • Тема: доверие к данным, перекрёстная проверка, критическое мышление.
  • Параметры: HDOP, число спутников по созвездиям ($GLGSV), стабильность точки.

Ход работы. Разбор реального случая из врайтапа шага 2: два независимых запуска сошлись в одну чужую точку (~55.63, 38.06) при реальном положении в 30 км, HDOP отличный (1.1), GPS-спутников много — но ГЛОНАСС видел НОЛЬ.

На графике:

 Честный фикс          Спуфинг
  GPS: 8 спутников      GPS: 8-9 спутников, HDOP 1.1 (отлично!)
  ГЛОНАСС: 6            ГЛОНАСС: 0   ← так не бывает над городом
  точка = реальность    точка = стабильно чужая (~30 км), центр города/аэропорт

Разбор. Спуфер транслирует поддельный сигнал GPS L1 и давит остальное; приёмник честно решает фальшивую задачу — отсюда «отличный HDOP при вранье на 30 км». Сигнатура: координаты стабильно чужие (шум дал бы разброс, спуфинг даёт устойчивую подмену), HDOP хороший, а ГЛОНАСС/Galileo видят ноль. Прошивкой спуфинг не лечится — модуль верит эфиру. Проверка за минуту: смартфон с GPSTest рядом покажет ту же чужую точку → отравлен эфир, а не железо. Для маячка безопасности это прямой довод, зачем в пакете бит валидности и почему координатам без перекрёстной проверки нельзя доверять решения, ставящие на кон жизнь.

Практическое задание. Предложите эвристику анти-спуфинга, которую можно добавить в прошивку (подсказка из бэклога: «скачок > N км между соседними фиксами при хорошем HDOP → флаг подозрения»). В каких случаях она даст ложное срабатывание?


Модуль 3. Классификация: детектор падения и цена ошибки

Урок 6. Три фазы падения: почему нужны ВСЕ, а не одна

  • Тема: машина состояний, признаки события, последовательная логика.
  • Стенд: fall_detector_sim.py. Параметры: пороги FF/impact/σ, фазы FSM.

Ход работы. Запустите uv run fall_detector_sim.py. На графике |a| (модуль ускорения) для сцены: ходьба, затем настоящее падение (t=8) и две провокации — «уронил устройство» (t=20) и «сел резко» (t=30). Смотрите, как подсвечиваются фазы FSM и где загорается зелёная линия «подтверждено».

На графике (модуль ускорения |a|):

 |a|, g
   6│              █ удар >2.8g
   3│  ∿∿∿ходьба   █        ∿∿∿   ← провокации (t=20, t=30)
   1│─────────────╱ ╲──────────── лежит неподвижно (σ мал)
   0│            ▼ невесомость <0.4g
    └──────────────────────────► t
      фаза1→фаза2→фаза3 = ПАДЕНИЕ; провокации не проходят все три

Разбор. Детектор подтверждает падение ТОЛЬКО если сложились три фазы подряд: невесомость (|a|<0.4g дольше 60 мс — тело в свободном падении) → удар (|a|>2.8g в окне 500 мс) → неподвижность (после 1.5 с «звона» дисперсия |a| за 8 с мала). Каждая фаза отсекает свою провокацию: «сел резко» даёт удар, но БЕЗ невесомости — застревает на фазе 1; «уронил устройство» даёт невесомость и удар, но потом его подбирают — движение проваливает фазу 3. Один признак (только удар) сделал бы детектор наполовину слепым. Это наглядная машина состояний и композиция признаков — основа любой детекторной логики.

Практическое задание. В симуляторе поднимите порог удара выше пика реального падения (~6.4g) — падение перестанет ловиться (пропуск). Верните и поднимите порог неподвижности σ до 0.4 — начнёт срабатывать «уронил» (ложная тревога). Найдите пару порогов, которая ловит падение и молчит на обе провокации.


Урок 7. Цена ошибки: пропуск против ложной тревоги (ROC)

  • Тема: компромисс ошибок I/II рода, ROC, ground truth.
  • Стенд: fall_detector_sim.py (табло попаданий/пропусков/ложных).

Ход работы. Игра «настрой детектор» (как в модуле радиозанавеса, но здесь цена ошибки — безопасность человека). Команда двигает три порога, стремясь поймать падение и не сработать на провокации. Табло вверху графика считает попадания, пропуски и ложные в реальном времени.

Разбор. Идеального порога не существует: ниже — ловим всё, но звеним на каждое «сел/уронил» (ложные тревоги → сигналу перестают верить); выше — тихо, но пропускаем настоящее падение (беда не замечена). Это выбор точки на кривой «ложные ↔ пропуски» (ROC). Для медицинского/охранного устройства цена двух ошибок РАЗНАЯ, и её задаёт не программист, а сценарий применения: для маячка человека с деменцией пропуск падения обычно дороже лишней проверки — значит порог смещают в сторону чувствительности, сознательно принимая часть ложных. Именно поэтому в прошивке есть пред-тревога 10 с с отменой кнопкой: она гасит ложные срабатывания до отправки SOS, не жертвуя чувствительностью детектора.

Практическое задание. Обсудите: как пред-тревога (10 с с возможностью нажать «я в порядке») сдвигает баланс ROC? Почему она допустима для падения (автоматика), но кнопка SOS уходит в тревогу СРАЗУ, без пред-тревоги?


Приложение: таблица переноса концепций в индустрию

Концепция из проектаГде встречается за пределами лаборатории
Бинарный пакет + порядок байтСетевые протоколы, форматы файлов, телеметрия
CRC контроль целостностиEthernet, USB, ZIP, прошивки, хранилища
Качество и свежесть измеренияДатчики IoT, финансовые тики, кэши, сенсор-фьюжн
Спуфинг и перекрёстная проверкаКибербезопасность, авионика, антифрод
Машина состояний (FSM)Протоколы, UI, промышленные контроллеры, игры
Композиция признаков событияНосимая электроника, медицинские мониторы, ML
ROC, цена ошибки I/II родаМедицинская диагностика, детекторы, антиспам, ML

Заключение

BeaconRadio — фундамент курса: на нём ученик впервые проектирует пакет, считает CRC руками, встречает дБм и статус-байт, проходит конвейер данных от чипа до карты в Grafana. Но его главный урок глубже техники: в устройстве, от которого зависит безопасность человека, критическая величина — не число на экране, а КАЧЕСТВО РЕШЕНИЯ. GPS умеет уверенно врать (спуфинг), фикс бывает несвежим, а детектор падения ошибается в обе стороны — и честная инженерия состоит не в том, чтобы спрятать эти ошибки, а в том, чтобы их измерить (ground truth, ROC) и осознанно выбрать компромисс под цену ошибки. Python-стенд fall_detector_sim.py даёт пережить этот выбор руками, не рискуя ничьим здоровьем.