BLE
Модуль принимает и передаёт широковещательные пакеты Bluetooth Low Energy (advertising). Сканер распознаёт Eddystone UID, URL и TLM, iBeacon и BTHome v2. Пакеты неизвестного формата передаются в Lua без изменений, поэтому данные конкретного устройства можно разобрать в программе.
Сканер не устанавливает BLE-соединение и не выполняет сопряжение. При активном
сканировании устройство запрашивает дополнительные данные и принимает
scanResponse. Такой режим создаёт большую нагрузку на радиоканал и расходует
больше энергии, чем пассивное сканирование.
bt.attach(mode)
Включает Bluetooth. Для сканирования достаточно режима bt.mode.BLE.
bt.attach(bt.mode.BLE)
Модуль bt доступен только в версиях прошивки с поддержкой Bluetooth.
bt.scan.start(callback [, options])
Запускает непрерывное сканирование. Старый вызов bt.scan.start(callback)
сохранён; без options используется активное сканирование с интервалом и окном
50 мс.
bt.scan.start(function(frame)
print(frame.address, frame.rssi, frame.raw)
end, {
active = true,
intervalMs = 50,
windowMs = 50,
duplicateMode = "logical",
duplicateWindowMs = 1500,
filters = {
{address = "54:48:E6:8F:80:A5"},
{adType = 0x16, prefix = "D2FC"},
{adType = 0xFF, prefix = "3412"},
},
bthomeKeys = {
["54:48:E6:8F:80:A5"] = "231d39c1d7cc1ab1aee224cd096db932",
},
})
Параметры:
active—trueдля активного сканирования,falseдля пассивного;intervalMs— период сканирования от 2,5 до 10 240 мс;windowMs— длительность окна в том же диапазоне, не большеintervalMs;duplicateMode—"logical"или"all";duplicateWindowMs— время подавления одинаковых пакетов неизвестного формата, целое число от 1 до 600 000 мс;filters— массив правил приёма;bthomeKeys— таблица ключей BTHome по MAC-адресам.
Значения времени округляются с шагом 0,625 мс. Стандартная прошивка принимает
до 16 фильтров и до 16 ключей BTHome за один запуск. Если таблица длиннее,
start возвращает ошибку.
В bthomeKeys ключом таблицы служит MAC-адрес, а значением — 128-битный ключ
из 32 шестнадцатеричных символов. Один адрес нельзя указывать дважды.
Повторный start до завершения stop возвращает ошибку.
Фильтры
Пакет достаточно сопоставить с одним правилом из filters. Внутри правила
должны совпасть все заданные поля. MAC-адрес можно записать с двоеточиями или
без них, регистр не важен. adType — тип AD-элемента от 0 до 255. prefix —
шестнадцатеричный префикс данных элемента, то есть байтов после adType;
prefix требует adType.
Например, правило {adType = 0x16, prefix = "D2FC"} принимает Service Data
BTHome с UUID 0xFCD2, записанным байтами D2 FC. Правило
{adType = 0xFF, prefix = "3412"} принимает данные производителя, которые
начинаются с байтов 34 12.
Без filters сканер передаёт все широковещательные пакеты. Повреждённая
последовательность AD-элементов остаётся доступной в raw или scanResponse,
но не разбирается как BTHome, Eddystone или iBeacon.
Подавление повторов
В режиме "logical" сканер объединяет повторные передачи одного события:
- для BTHome-пакетов с
packetIdсканер учитывает адрес и последовательность номеров; повторные и устаревшие номера подавляются в течение 4 секунд, переход255 -> 0обрабатывается корректно; - повтор одного зашифрованного BTHome-пакета с тем же счётчиком в течение 4 секунд не вызывает второй callback;
- явно отфильтрованный пакет неизвестного формата проверяется по адресу,
типу и содержимому основного пакета и
scanResponseв течениеduplicateWindowMs.
Два последовательных действия с новыми BTHome packetId передаются отдельно.
Неизвестные пакеты, для которых не задан фильтр, передаются без подавления
повторов. Режим "all" передаёт каждый допустимый пакет; защита зашифрованного
BTHome от повторного воспроизведения и подмены открытым пакетом остаётся
включённой.
Формат callback
Каждый callback получает таблицу с общими полями:
{
type = bt.frameType.Unknown,
address = "54:48:E6:8F:80:A5",
addressType = 0,
advType = 3,
rssi = -61,
raw = "020106...",
len = 18,
scanResponse = "",
scanResponseLen = 0,
ad = {
{source = "adv", type = 0x01, data = "06"},
{source = "adv", type = 0x16, data = "D2FC..."},
},
}
raw содержит основной широковещательный пакет, scanResponse — отдельный
ответ при активном сканировании. source равен "adv" или "scanResponse".
MAC-адрес всегда возвращается прописными буквами с двоеточиями.
Номера bt.frameType:
Unknown = 0;EddystoneUID = 1;EddystoneURL = 2;EddystoneTLM = 3;IBeacon = 4;BTHome = 5.
Поля существующих Eddystone/iBeacon-кадров сохранены: namespace, instance,
url, version, battery, temperature, advCount, timeSinceBoot,
uuid, major, minor, txPower и distance в зависимости от типа.
BTHome v2
Для Service Data UUID 0xFCD2 callback дополнительно содержит bthome:
{
accepted = true,
status = "ok",
version = 2,
encrypted = false,
authenticated = false,
triggerBased = true,
packetId = 9,
partial = false,
objects = {
{
id = 0x3A,
name = "button",
index = 1,
event = "press",
value = 1,
valueText = "1",
raw = "01",
},
},
}
Поддерживаются все актуальные объекты официального BTHome v2: числовые и
бинарные значения, text, raw, тип устройства, версия прошивки, а также
события button (0x3A), command (0x3B) и dimmer (0x3C). Полная
таблица идентификаторов, размеров, единиц и коэффициентов приведена в
спецификации BTHome.
Повторяющиеся объекты сохраняют исходный порядок. index нумерует объекты с
одинаковым ID начиная с единицы. У числового объекта:
value— удобное Lua-число;valueText— точная десятичная запись без потери 24- и 32-битного значения;raw— точные байты объекта без ID в том порядке, в котором они пришли;unit— единица измерения, если она определена спецификацией.
У text значение возвращается строкой UTF-8, у raw — шестнадцатеричной
строкой. У command есть event и шестнадцатеричное поле args; у dimmer —
event и steps. Известный усечённый объект отклоняет весь пакет. Неизвестный
ID останавливает разбор, сохраняет предыдущие объекты и выставляет
partial = true.
Возможные status:
ok— пакет принят;missing_key— зашифрованный пакет получен без ключа;auth_failed— AES-CCM MIC не подтверждён;replay— счётчик уменьшился, повторился с другими данными или тот же пакет повторно пришёл позже разрешённых 4 секунд;downgrade— для адреса с ключом получен незашифрованный пакет;malformed— нарушены длина, зарезервированные биты или структура объекта;unsupported_version— Service Data использует не BTHome v2.
Только при accepted = true таблица содержит разобранные objects. Для
зашифрованного BTHome используется AES-CCM с 128-битным ключом по
официальной схеме BTHome. Ключи нужно передавать
при каждом запуске сканирования: модуль не сохраняет их после stop или ошибки
запуска. История счётчиков также действует только до stop. После сброса
счётчика передатчика нужно остановить и заново запустить сканирование.
Shelly BLU Button/Remote и другие BTHome-пульты передают кнопки и колесо в широковещательных пакетах; соединение с ними не требуется. Настройка самого пульта через BLE-соединение в этот API не входит.
bt.scan.stats()
Возвращает накопительные счётчики текущего запуска сканирования:
{
received = 0,
delivered = 0,
filtered = 0,
duplicates = 0,
malformed = 0,
authFailed = 0,
replayBlocked = 0,
downgradeBlocked = 0,
queueDropped = 0,
}
Счётчики сбрасываются при новом start. queueDropped показывает, сколько
пакетов сканер не успел обработать из-за перегрузки.
bt.scan.stop()
Останавливает сканирование. После успешного возврата callback больше не
вызывается, а ключи и история текущего запуска очищаются. stop можно вызвать
из callback: текущий вызов завершится, но следующий уже не начнётся.
Передача маяков
bt.service.beacon.eddystone_uid(mac, tx_power, namespace, instance)
Создаёт Eddystone UID beacon.
bt.service.beacon.eddystone_url(mac, tx_power, url)
Создаёт Eddystone URL beacon.
bt.service.beacon.eddystone_tlm(mac, tx_power, mv, deg)
Создаёт Eddystone TLM beacon. mv — напряжение в милливольтах, deg —
температура.
bt.service.beacon.eddystone_tlm_update(beacon, mv, deg)
Обновляет напряжение и температуру TLM beacon.
bt.service.beacon.ibeacon(mac, tx_power, uuid, major, minor)
Создаёт iBeacon.
Методы Beacon:start() и Beacon:stop() запускают и останавливают передачу.
local mac = cpu.getmac(2, 1)
local tx_power = -59
local b1 = bt.service.beacon.eddystone_uid(
mac, tx_power, "3F3B51B60B88EF9949E5", "CF484185AE0B"
)
local b2 = bt.service.beacon.eddystone_url(mac, tx_power, "https://open-dev.ru")
local b3 = bt.service.beacon.eddystone_tlm(mac, tx_power, 3220, 26)
local b4 = bt.service.beacon.ibeacon(
mac, tx_power, "00112233445566778899AABBCCDDEEFF", 100, 1
)
b1:start()
b2:start()
b3:start()
b4:start()