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 детектора падения построчно — падать с платой не нужно.
Кто чем пользуется
- Инженер отвечает за стенд: распиновку (по мануалу, не по памяти!), питание GPS, серверную цепочку до Grafana, чекпоинты-бипы init.
- Учитель проводит уроки-симуляции и полевые проверки по сценарию.
- Методист привязывает параметры пакета/фикса/детектора к темам программы.
Модуль 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 даёт
пережить этот выбор руками, не рискуя ничьим здоровьем.