Извините, я не могу сдерживаться. Я вчера это чинил до четырёх утра, потому что в семь утра это уже надо было везти и ставить. Не конкретно вот это, разумеется, а вообще порядка двух тысяч строк кода, написанных вот так. Я их с утра понедельника чиню.
***
Задача - услышать бикон (стоит в подъезжающем авто), аутентифицировать, проверить по базе, что этому можно въезжать, определить расстояние и направление движения, в нужный момент дать команду на открывание ворот. Смысл - не просто аутентификация транспондера, а чтобы авто проезжало через уже открывшиеся ворота, не задерживаясь перед ними. И чтобы при этом ворота не реагировали на те же транспондеры в машинах, например, стоящих на стоянке в двадцати метрах за воротами.
Ну т.е.
1) слышим бикон
2) отвечаем ему ACK, в котором есть случайное число
3) бикон отвечает пакетом с этим же числом, подписанным криптографической подписью с ключом, известным только бикону и базе (аутентифицировали)
4) база запускает процедуру измерения расстояния - в ней всё уже формируется на уровне железа: база излучает специальный пакет, бикон на него отвечает (ToF на LoRa в диапазоне 2,4 ГГц)
5) база повторяет измерение три раза с интервалом 1 секунда (определяем направление движения)
Теперь решение.
Как выдерживается секундный интервал между измерениями?
Миганием светодида. Ну т.е. зовётся функция мигания светодиода, у которой параметры - сколько раз, период 1, период 0. 5, 100, 100 - вот и получилась секунда.
Если вы думаете, что пора смеяться, то нет, ещё рано.
Что происходит дальше?
Дальше синхронно на мастере и слейве запускается процедура вычисления расстояния - мастер испускает пакет, слейв на него отвечает (железно, с гарантированной величиной задержки).
Между миганием и вычислением есть выхлоп в консоль, ну что-то типа printf(«Sending ranging request to the beacon\n»). Ну и в биконе что-то типа printf(«Waiting for ranging request from the gateway\n»). Безобидное совершенно.
Так вот, если на слейве эта строчка длиннее, чем на мастере, примерно на 2 или 3 буквы, то... правильно! Мастер испускает пакет раньше, чем слейв включает приём! Потому что printf у нас - блокирующая функция!
И вот тогда - таймаут. Причём всегда таймаут, на всех измерениях.
***
Я просто в немом восхищении смотрю
Там столько способов прострелить себе ногу
1) вывод в консоль включается только при сборке отладочной прошивки, т.е. отладку включать нельзя, всё ломается (или нет, зависит от длины строчки, см. выше)
2) можно в настройках проекта включить неблокирующий printf, но! Это даже круче! Потому что как только кто-то начинает за него конкурировать, второй запрос на вывод в консоль при не отработанном первом автоматом становится блокирующим - ну т.к. канал DMA и USART у нас один, соответственно, там мьютекс стоит.
При этом решается в одну строчку ровно:
1) на слейве ставим таймаут на приём чего-нибудь типа 50 мс (штатный параметр функции, запускающей приём, 0 новых строчек кода)
2) на мастере делаем задержку передачи 25 мс (lptimer_sleep(25), одна строчка кода)
Всё. Это покрывает любые возможные рассинхронизации.
ОДНА СУКА СТРОЧКА.
***
И нет, это написано не джуном зелёным. Это написано человеком, у которого в резюме 11 лет опыта, в том числе в автомобильной электронике и системах промышленной автоматики.