Open Sesame: PinkyWinky
Внезапным для самого себя образом обнаружил, что лучший подарок молодому IoT-строителю - переезд в дом %) Серьезно, с самого порога получаешь какое-то невероятное количество проблемок задачек, которые так и зудит порешать. В этот раз сказ о входных воротах.
Внутри использование ESPHome и CC1101, кастомная прошивка для бикона на nRF52810 и многое другое. Пристегивайтесь :)
Но сперва…на кой чёрт?
Вопрос, который должен бы задать себе любой человек перед тем, как приступить решать какую-либо проблемку. В моем случае проблема звучит так: жена, как и огромное количество людей в Тае, водит скутер и ей нужно открывать/закрывать входные ворота. Да, вот так просто. Вот они, кстати:
К счастью, они умеют открываться с брелока. Но, традиционно, брелок этот какая-то поделка, которой и стремно, и не удобно пользоваться будучи за рулем скутера.
Хмм, починить то, что не было сломано - бывает ли задачка достойнее? Я тоже так подумал и начал вынашивать план, как сделаю жену чуть счастливее :)
Планчик
Управление воротами у меня бесхитростное, работает на частоте 330Mhz, никаких rolling codes и прочих безопасностей не имеет. Посему, вариантов приделать более удобное управление огромное множество. Я решил так:
- берем BLE Beacon, лепим его на скутер аки кнопку
- дома включаем BLE сканер у ESP32 для отслеживания бикона
- к этому же ESP32 цепляем RF передачик
- отправляем сигнал блоку управления воротами при нажатии на кнопку
- профит
С одной стороны у нас получается довольно локальная история без зависимостей от Home Assistant, интернетов и т.д. С другой - поуправлять воротами из того же Home Assistant все-таки будет возможно. А с третьей - бикон должен позволить в будущем автоматизировать открытие ворот (но об этом в другой раз, не все штуки приехали). Ну и во все том же будущем подключить бикончик к эпловой (см. OpenHaystack) или лучше гугловой “Find My Device” сети, всяк лишним не будет. В общем, у меня на него большие планы, а пока побудет кнопкой ;)
RF transmitter
Наверное, никого не удивлю, если скажу, что люди решали эту задачу сотню другую раз. Поэтому тут довольно сложно быть оригинальным - берем ESP32 + RF передатчик + ESPHome и лепим то, что пожелаем :)
А значит и затягивать нет смысла, выбор железа у меня таков:
- Seeed Studio XIAO ESP32-S3 как основа всего
- CC1101 Wireless Module в роли RF передатчика
- 2.4G Antenna
- Lithium Battery 3.7v дабы не ресетить стейт от пропажи питания
- какая-то коробушка
ESP32-S3 я взял в-первую очередь ради поддержки Bluetooth 5 (LE), а во-вторую за его два ядра, опасаясь возможных проблем с сожительством BLE и WiFi (см. ESP32 BLE Tracker: Use on single-core chips).
А вот СС1101 взял наугад. В мире существует множество разных RF передатчиков с поддержкой 330Mhz (e.g. SYN115, SI4432 и т.д.), СС1101 мне показался достаточно популярным, чтобы не промахнуться - вродь не ошибся :D
RF transmitter: дела хардварные
С жезелом определились, время подключать:
| ESP32-S3 | СС1101 module |
|-----------------|---------------------------|
| GND | GND |
| VCC (3v3) | VCC |
| GPIO9 | TX |
| GPIO8 | SCK |
| GPIO7 | MISO |
| GPIO1 | CS |
| GPIO2 | MOSI |
| GPIO4 | RX |
Для визуалов: Запаиваем и корпусируем:
RF transmitter: дела софтварные
Фуф, запаяли, можно наконец-то заняться софтом. Как я и говорил, CC1101 довольно популярный чип, поэтому найти что-то готовое для его применения в ESPHome не составило труда, например:
- esphome PR: cc1101 radio transmitter component
- github://dbuezas/esphome-cc1101
- слышал об успешном использовании github.com://jgromes/RadioLib
Я выбрал PR для ESPHome в надежде, что его допинают до релиза и он станет доступен из коробки. Кмк, полезнее был бы порт RadioLib, но и CC1101 в составе ESPHome тоже неплохо :)
Дело за малым:
- немного программируем на ямлах: конфиг для ESPHome
- прошиваем им наш RF transmitter
- добавляем его в Home Assistant
- проверяем отправляемость сигнала:
- я, как хлебушек, пользуюсь Flipper Zero для этого
- запускаем запись Sub-GHz сигнала (
Sub-GHz
->Read RAW
) - жмем на кнопку
Open Gate
в Home Assistant:
- радуемся, ведь все выглядит рабочим, и мы можем двигаться дальше
RF transmitter: дела сигнальные
А дальше нас ждет увлекательное путешествие в мир радио, ведь нам нужно отправить правильный сигнал блоку управления воротами.
Скорее всего крепкие специалисты пользуются чем-то более подходящим (а то и самим CC1101, например), я же опять расчехляю Flipper Zero:
- запускаем запись Sub-GHz сигнала (см. Sub-GHz: Reading RAW signals)
- жмем кнопку брелока и прикапываем ее сигнал:
- закидываем в плоттер
- видим явные периоды отправки кода:
- и сам код при более детальном рассмотрении:
- осталось записать конечный код посчитав нолики и единички
- прикинуть продолжительность пульса, описать как выглядит sync (
1 + 0*21
),0
(1000
) и1
(1110
)
Вуаля, через пару итераций “записываем отправляемый сигнал, кидаем в плоттер, сравниваем с оригиналом” получаем итоговый конфиг открытия ворот:
button:
- platform: template
name: "Open gate"
id: gate
on_press:
- remote_transmitter.transmit_rc_switch_raw_cc1101:
code: '0100011111111100101110101'
protocol:
pulse_length: 345
sync: [1, 21]
zero: [1, 3]
one: [3, 1]
repeat:
times: 4
wait_time: 0s
Вот и все, пока можно отложить ESPHome. Дальше нам очень нужно разобраться с биконом.
PinkyWinky
С биконом, пожалуй, самая интересная история всего проекта. На всякий случай напомню, что зачастую бикон - это манюсенькое устройство, которое что-то броадкастит по Bluetooth. Причем, броадкастить он может примерно что угодно:
- это может быть как просто UUID для отслеживания положения, как в iBeacon
- так и ссылка/EID, как в случае с Eddystone
- так и любая нужная вендору информация. Например, состояние кнопки, уровень заряда батарейки и т.д.
В общем, отличная штуковина для нашей задачки. Но есть нюанс - все биконы, которые я держал в руках, были очень ватной кнопкой в connectionless режиме :( А я хотел именно connectionless режим, чтобы позволить себе терять пакетики при подъезде к дому (сложно, знаете ли, точно на глазок определить пора ли жать на кнопку)…
Вечер перестает быть, томным подумал я! Засучил рукава, открыл AliExpress и принялся искать бикон, для которого было бы удобно писать кастомную прошивку. Вынырнул я из поиска с заказом двух BLE биконов от Holyiot на nRF52810:
C акселометром и без, так уж вышло. На Summer Sale оба красавчика обошлись мне примерно в $5 каждый. Выбрал я их по нескольким причинам:
- SOC nRF52810 :
- выполнен на базе Cortex-M4 64 MHz
- поддерживает Bluetooth Low Energy
- имеет на борту 192 kB флешки и 24 kB RAM
- поддержан в Zephyr Project, на котором и будем писать прошивку
- обещает быть waterproof
- на платке есть выводы GND/SWCLK/SWDIO для прошивки и отладки
В общем, ТТХ идеально подходят для нашей будущей кнопки. А пока биконы ехали ко мне из Китая, я покопался в столе и вытащил XIAO nRF52840 + Expansion Board Base, которые купил на какой-то из распродаж до этого: И принялся писать прошивку. Почему на nRF52840? Это самое близкое к nRF52810, что у меня было + отлаживаться на nRF52840 сильно более приятно, т.к. он имеет целых 256 kB RAM против 24 kB у nRF52810 (которых, как оказалось, для BLE стека в Zephyr прям впритирку).
PinkyWinky: firmware
Сам PinkyWinky написан на C поверх Zephyr Project. Почему Zephyr? Давно хотел попробовать, а тут такой шанс. Если в двух словах, то Zephyr - real-time OS для кучи железок, имеет поддержку devicetree, bluetooth, многозадачности и прочих ништяков. Приятный проект, мне понравился :)
Логика работы PinkyWinky предельно проста, как то и должно быть:
- на старте генерирует случайное начальное значение таймстемпа (
initial_ts
) - запускает Legacy BLE Advertising (Extended Advertising не поддерживает ESPHome)
- в manufacturer specific data передает:
- версию (
version
) - 1 байт - заряд батарейки (
batt_percent
) - 1 байт - состояние кнопки (
btn_pressed
) - 1 байт - текущий таймстемп (
ts
) - 4 байта - укороченая sha1 подпись (
sign
) - 10 байт
- версию (
- при нажатии на кнопку:
- устанавливает
btn_pressed = true
- устанавливает
ts = initial_ts + seconds_since_boot
- зажигает LED
- перезапускает BLE Advertising для большей реактивности
- через какое-то время (1s по умолчанию):
- сбрасывает состояние
btn_pressed
и LED - устанавливает
ts = initial_ts + seconds_since_boot
- сбрасывает состояние
- устанавливает
- периодически (15s по умолчанию) устанавливает
ts = initial_ts + seconds_since_boot
Как то часто бывает, многое тут не случайно. Так таймстемп и его ротация служат защитой от копирования и заметного усложнения эксплуатации Replay, Rolljam и аналогичных атак. Рандом в initial_ts
нужен, т.к. у бикона нет источника времени. А подпись укорочена до 10 байт, потому что в Legacy BLE Advertising доступно лишь 31 (adv data) + 31 (scan response) байт данных на все про все, вместе с именем, флажками и т.д. При этом риски намутить коллизию на 7 байтах оцениваю как минимальные. Вместо подписи можно было бы зашифровать данные, но мне показалось лишним (скрывать там нечего, а бояться отслеживания с партией устройств в 1 штуку глупо).
Пора. Пора прошивать XIAO nRF52840, запускать nRF Connect и проверять:
PinkyWinky: ESPHome
Конечно, вся эта кастомщина в BLE Advertising не может обойтись без соответствующей поддержки в ESPHome. Так, наряду с прошивкой для бикона пришлось написать и компонент к ESPHome - pinky_winky, который:
- реализует логику парсинга, валидации и проверки подписи данных от PinkyWinky
- умеет прикапывать
initial_ts
в Non-Volatile Storage, дабы переживать рестарты - позволяет сбросить
initial_ts
при смене батарейки у PinkyWinky - предоставляет Binary Sensor для кнопки
- предоставляет Sensor для батарейки
Но всегда лучше один раз увидеть, потому пробуем проверить input lag:
- еще немного попрограммируем на ямлах: конфиг для ESPHome
- тестим:
- нраица! Самое время катить в прод.
PinkyWinky: Holyiot
Уф, полевые испытания все ближе! А пока портируем прошивку под наш бикон. Благо, благодаря Zephyr Project, все что для этого нужно - описать новый борд (см. Board Porting Guide): Holyiot 21014. Ничего хитрого.
Кстати, помните я говорил, что памяти у nRF52810 впритирку для BLE стека Zephyr? Вот какой-никакой пруф со сборкой PinkyWinky (тут отключено логирование, serial console, не используемые фичи bluetooth и пр.):
~/zephyr/apps/pinky-winky on main
% west build -p always -b holyiot_21014
...
...
Memory region Used Size Region Size %age Used
FLASH: 104440 B 192 KB 53.12%
RAM: 21208 B 24 KB 86.30%
IDT_LIST: 0 GB 32 KB 0.00%
Ну все, осталось совсем немного. Припаяться к платке:
Прошить нашу прошивочку:
Если интересно, то для прошивки и отладки я перешел на Black Magic Probe поверх WeAct Studio Black Pill F4. Хз как жил без него, если честно. Получать доступ в GDB server с автоопределением таргета без плясок с запуском скриптов для OpenOCD и прочего - это какая-то чистая магия :)
И подсобрать все обратно с небольшим модом для более короткого хода кнопки.
Final test
Yay! Пора пробовать в бою:
- в последний [на сегодня] раз программируем на ямлах: конфиг для ESPHome
- и тестим:
- тааа-дааам, работает как и планировалось :)
Дальше по планам проверсти больше полевых испытаний, чёнить помелочи поправить и уже зимо-осенью заняться автоматизацией открытия ворот и подключению к Find My Device. Да не скоро, но вы все равно не переключайтесь ;)
А пока у меня все, всем кота (づ˶•༝•˶)づ♡