Espruino:Примеры/Рассылка BLE-объявлений при помощи Node.js/Python/C: различия между версиями
Myagkij (обсуждение | вклад) (Новая страница: «{{Espruino/Панель перехода}} {{Перевод от Сubewriter}} {{Myagkij-редактор}} =<ref>[ www.espruino.com - ]</ref>= <syntaxhighligh...») |
Myagkij (обсуждение | вклад) Нет описания правки |
||
(не показано 5 промежуточных версий 2 участников) | |||
Строка 3: | Строка 3: | ||
{{Myagkij-редактор}} | {{Myagkij-редактор}} | ||
=<ref>[ www.espruino.com - ]</ref>= | =Рассылка BLE-объявлений при помощи Node.js/Python/C#/Android<ref>[https://www.espruino.com/BLE+Advertising www.espruino.com - BLE Advertising with Node.js/Python/C#/Android]</ref>= | ||
Коммуницировать с [[Puck.js]] через [[BLE]] можно тремя разными способами: | |||
* Подключитесь к [[Puck.js]] с другого устройства и отправьте данные на сервис [[Nordic UART]] (или сервис, который вы создали сами) – большинство устройств ([[ПК]], [[Mac]], [[Linux]], [[Android]]) одновременно могут быть подключены максимум к ''5-6'' устройствам. | |||
* Подключите [[Puck.js]] к устройству и коммуницируйте с сервисами этого устройства – большинство устройств поддерживают только одно одновременное подключение. | |||
* Рассылайте с помощью [[Puck.js]] объявления для устройств, находящихся в пределах BLE-диапазона – это работает только в одном направлении (от [[Puck.js]] к прослушивающему устройству), но так вы можете отправлять данные от какого угодно количества [[Puck.js]]. | |||
Более подробно о том, что всё это значит, можно прочесть [https://www.espruino.com/About+Bluetooth+LE тут], а здесь мы сосредоточимся на способе с рассылкой BLE-объявлений. | |||
Советы по использованию Bluetooth-соединения читайте тут: | |||
* [https://www.espruino.com/BLE+Communications Подключение через другое Espruino-устройство]. | |||
* [https://www.espruino.com/Web+Bluetooth Подключение через веб-страницу с Web Bluetooth]. | |||
* [https://www.espruino.com/Interfacing#bluetooth-le Подключение через Node.js или Python]. | |||
Чтобы преобразовать данные объявления в [[MQTT]], можно воспользоваться программой [https://github.com/espruino/EspruinoHub EspruinoHub] (причём её также можно [https://www.espruino.com/BLE%20Node-RED использовать вместе с Node-RED]), но в этом руководстве мы сосредоточимся на написании собственного приложения-хоста. | |||
Сначала нам нужно разослать BLE-объявления с данными. Это можно сделать при помощи метода NRF.setAdvertising(). | |||
Есть два главных типа рассылаемых данных: | |||
* '''Сервисы''' – У каждого сервиса есть свой [[UUID]]. Он может быть 16-битным или 128-битным. 16-битные UUID [https://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx присваиваются Bluetooth SIG], так что будьте внимательны и используйте правильные UUID. Вы можете задать собственные 128-битные UUID, но они, в-первых, должны быть случайными, и во-вторых, BLE-объявление очень невелико, так что после использования 128-битного UUID у вас останется мало свободного места. Например, можно воспользоваться UUID сервиса «0xFFFF» (для серийных устройств его использовать не стоит) с помощью команды '''NRF.setAdvertising({0xFFFF:"Hello"});'''. | |||
* '''Данные производителя''' – Прошивка [[Espruino]]/[[Puck.js]] 1v95 и новее позволяет задать данные производителя. Это почти то же самое, как использовать сервисы с 16-битными [[UUID]], однако для [[Espruino]]/[[Puck.js]] зарезервирован собственный 16-битный UUID (0x0590), который можно использовать для чего угодно. | |||
Ниже мы воспользуемся данными производителя. Подключитесь к [[Puck.js]] с помощью [[IDE]] и загрузите на него следующий код: | |||
<syntaxhighlight lang="javascript"> | |||
var presses = 0; | |||
NRF.setAdvertising({},{manufacturer: 0x0590, manufacturerData:[presses]}); | |||
setWatch(function() { | |||
presses++; | |||
NRF.setAdvertising({},{manufacturer: 0x0590, manufacturerData:[presses]}); | |||
}, BTN, {edge:"rising", repeat:1, debounce:20}) | |||
</syntaxhighlight> | |||
Этот код рассылает в BLE-объявлениях один байт (''«0»''), но с каждым нажатием на кнопку это значение будет увеличиваться. | |||
Теперь вам надо отключить онлайн-IDE от [[Puck.js]], потому что [[Puck.js]] рассылает данные, только когда к нему не подключено никаких устройств. | |||
{{Примечание1|'''manufacturerData''' – это массив байтов (значений в диапазоне ''между 0 и 255''). Более крупные значения будут обрезаны. Вы также можете задать строку – например, '''NRF.setAdvertising({},{manufacturer: 0x0590, manufacturerData:"Hello"});'''. Если указать слишком много данных (''больше 21 байта''), это может обернуться исключением '''DATA_SIZE'''.}} | |||
== Puck.js/Espruino == | |||
Кроме того, [[Puck.js]] умеет прослушивать BLE-объявления других устройств при помощи NRF.setScan() (для непрерывного прослушивания) или NRF.findDevices() (для прослушивания в пределах заданного периода времени и агрегации всех ответов). К примеру, вы можете запустить на другом [[Puck.js]] вот такой код... | |||
<syntaxhighlight lang="javascript"> | |||
NRF.findDevices(print) | |||
</syntaxhighlight> | |||
…и в итоге получите примерно следующее: | |||
<syntaxhighlight lang="javascript"> | |||
[ | |||
BluetoothDevice { | |||
"id": "de:70:d9:0c:eb:86 random", | |||
"rssi": -44, | |||
"data": new Uint8Array([2, 1, 5, 4, 255, 144, 5, 11, 20, 9, 69, 115, 112, 114, 117, 105, 110, 111, 32, 78, 82, 70, 53, 50, 56, 51, 50, 68, 75]).buffer, | |||
"manufacturer": 1424, | |||
"manufacturerData": new Uint8Array([0]).buffer, | |||
"name": "Puck.js eb86" | |||
} | |||
] | |||
</syntaxhighlight> | |||
Как и все прочие устройстве в пределах BLE-диапазона. После этого можно выполнить поиск '''manufacturer==0x0590''' и извлечь передаваемые данные, прочитав поле '''manufacturerData'''. | |||
{{Примечание1|Сканирование рассылаемых BLE-данных – очень энергоёмкий процесс, т.к. из-за этого Bluetooth-компоненту [[Puck.js]] нужно всегда оставаться включённым. Так что, если у вас быстро расходуется заряд батареи, лучше пользоваться этой функцией как можно реже.}} | |||
== Node.js == | |||
В [https://nodejs.org/en/ Node.js] есть отличный модуль под названием [https://www.npmjs.com/package/noble Noble], работающий на [[Windows]], [[Mac OS]] и [[Linux]]. | |||
* '''В данный момент в Mac OS Mojave [https://github.com/noble/noble/issues/834 в Noble сломана поддержка BLE]''', так что лучше воспользоваться вместо него библиотекой [https://github.com/Timeular/noble-mac noble-mac]. | |||
* Пользователи '''[[Windows 10]] вместо [[Noble]] могут воспользоваться noble-uwp''' – там поддержка Bluetooth есть по умолчанию. | |||
* '''При использовании [[Windows]] вместе с [https://www.espruino.com/Quick+Start+BLE#puckjs USB-адаптером с BLE-функционалом]''' не нужно устанавливать драйвер, идущий в комплекте с этим адаптером. Это значит, что вам необязательно иметь [[Windows 10]], но если у вас есть [[Windows 10]] и нет поддержки [[BLE]], то вам нужно будет дополнительно подключить [[USB-адаптер]] с BLE-функционалом. | |||
* Убедитесь, что у вас установлен [https://nodejs.org/en/ Node.js]. | |||
* Затем установите '''noble''' при помощи [[npm]]. | |||
<syntaxhighlight lang="ssh"> | |||
# Устанавливаем Noble, чтобы получить поддержку BLE для Node.js: | |||
npm install noble | |||
# ТОЛЬКО ДЛЯ LINUX | |||
# Разрешаем Node.js получить доступ к BLE | |||
# даже без запуска при помощи «sudo»: | |||
sudo setcap cap_net_raw+eip $(eval readlink -f `which node`)) | |||
</syntaxhighlight> | |||
Теперь добавляем в '''advertising_nodejs.js''' код ниже: | |||
<syntaxhighlight lang="javascript"> | |||
var noble = require('noble'); | |||
function onDiscovery(peripheral) { | |||
// peripheral.rssi - мощность сигнала | |||
// peripheral.address - MAC-адрес | |||
// peripheral.advertisement.localName - название устройства | |||
// peripheral.advertisement.manufacturerData - данные | |||
- производителя | |||
// peripheral.advertisement.serviceData - обычные | |||
- данные сервиса, | |||
- рассылаемые | |||
- в объявлении | |||
// Игнорируем устройства без данных производителя: | |||
if (!peripheral.advertisement.manufacturerData) return; | |||
// Печатаем в консоли всё, что прочли: | |||
console.log( | |||
peripheral.address, | |||
JSON.stringify(peripheral.advertisement.localName), | |||
JSON.stringify(peripheral.advertisement.manufacturerData) | |||
); | |||
} | |||
noble.on('stateChange', function(state) { | |||
if (state!="poweredOn") return; | |||
console.log("Начинаем сканирование..."); | |||
noble.startScanning([], true); | |||
}); | |||
noble.on('discover', onDiscovery); | |||
noble.on('scanStart', function() { console.log("Сканирование началось."); }); | |||
noble.on('scanStop', function() { console.log("Сканирование завершилось.");}) | |||
</syntaxhighlight> | |||
* И запускаем его при помощи node advertising_nodejs.js | |||
У вас должно получиться примерно следующее: | |||
<syntaxhighlight lang="ssh"> | |||
Начинаем сканирование... | |||
Сканирование началось. | |||
de:70:d9:0c:eb:86 "Puck.js eb86" {"type":"Buffer","data":[144,5,0]} | |||
de:70:d9:0c:eb:86 "Puck.js eb86" {"type":"Buffer","data":[144,5,0]} | |||
de:70:d9:0c:eb:86 "Puck.js eb86" {"type":"Buffer","data":[144,5,0]} | |||
de:70:d9:0c:eb:86 "Puck.js eb86" {"type":"Buffer","data":[144,5,0]} | |||
de:70:d9:0c:eb:86 "Puck.js eb86" {"type":"Buffer","data":[144,5,1]} | |||
de:70:d9:0c:eb:86 "Puck.js eb86" {"type":"Buffer","data":[144,5,1]} | |||
de:70:d9:0c:eb:86 "Puck.js eb86" {"type":"Buffer","data":[144,5,2]} | |||
de:70:d9:0c:eb:86 "Puck.js eb86" {"type":"Buffer","data":[144,5,2]} | |||
de:70:d9:0c:eb:86 "Puck.js eb86" {"type":"Buffer","data":[144,5,3]} | |||
de:70:d9:0c:eb:86 "Puck.js eb86" {"type":"Buffer","data":[144,5,4]} | |||
de:70:d9:0c:eb:86 "Puck.js eb86" {"type":"Buffer","data":[144,5,5]} | |||
de:70:d9:0c:eb:86 "Puck.js eb86" {"type":"Buffer","data":[144,5,5]} | |||
de:70:d9:0c:eb:86 "Puck.js eb86" {"type":"Buffer","data":[144,5,5]} | |||
</syntaxhighlight> | |||
Вы можете прочесть строчки и от других устройств, у которых есть данные производителя. Как видите, третий элемент в буфере увеличивается при каждом нажатии на кнопку [[Puck.js]]. | |||
Это можно улучшить: | |||
* Чтобы нам сообщали обо всех устройствах с данными производителя. | |||
* Чтобы нам всегда присылали обратно рассылаемые данные – даже если они не изменились. | |||
Теперь попробуем новый код. Обязательно скопируйте адрес [[Puck.js]] из кода выше в константу devices: | |||
<syntaxhighlight lang="javascript"> | |||
var noble = require('noble'); | |||
// Список разрешённых устройств: | |||
const devices = [ | |||
"de:70:d9:0c:eb:86" | |||
]; | |||
// Рассылаемые BLE-данные, полученные в прошлый раз: | |||
var lastAdvertising = { | |||
}; | |||
function onDeviceChanged(addr, data) { | |||
console.log("Device ",addr,"changed data",JSON.stringify(data)); | |||
} | |||
function onDiscovery(peripheral) { | |||
// Мы знаем это устройство? | |||
if (devices.indexOf(peripheral.address)<0) return; | |||
// У него есть данные производителя с UUID Espruino/Puck.js's? | |||
if (!peripheral.advertisement.manufacturerData || | |||
peripheral.advertisement.manufacturerData[0]!=0x90 || | |||
peripheral.advertisement.manufacturerData[1]!=0x05) return; | |||
// Считываем только наши данные: | |||
var data = peripheral.advertisement.manufacturerData.slice(2); | |||
// Проверяем на предмет изменившихся сервисов: | |||
if (lastAdvertising[peripheral.address] != data.toString()) | |||
onDeviceChanged(peripheral.address, data); | |||
lastAdvertising[peripheral.address] = data; | |||
} | |||
noble.on('stateChange', function(state) { | |||
if (state!="poweredOn") return; | |||
console.log("Начинаем сканирование..."); | |||
noble.startScanning([], true); | |||
}); | |||
noble.on('discover', onDiscovery); | |||
noble.on('scanStart', function() { console.log("Сканирование началось."); }); | |||
noble.on('scanStop', function() { console.log("Сканирование завершилось.");}); | |||
</syntaxhighlight> | |||
В итоге получаем: | |||
<syntaxhighlight lang="ssh"> | |||
Начинаем сканирование... | |||
Сканирование началось. | |||
Device de:70:d9:0c:eb:86 changed data {"type":"Buffer","data":[5]} | |||
Device de:70:d9:0c:eb:86 changed data {"type":"Buffer","data":[6]} | |||
Device de:70:d9:0c:eb:86 changed data {"type":"Buffer","data":[7]} | |||
Device de:70:d9:0c:eb:86 changed data {"type":"Buffer","data":[8]} | |||
Device de:70:d9:0c:eb:86 changed data {"type":"Buffer","data":[9]} | |||
Device de:70:d9:0c:eb:86 changed data {"type":"Buffer","data":[10]} | |||
</syntaxhighlight> | |||
Теперь вы можете написать собственный обработчик для '''onDeviceChanged''', который делал бы то, что вам нужно, когда что-то происходит на [[Puck.js]]. | |||
== Python == | |||
Вы можете сделать то же самое на [[Linux]] (включая [[Raspberry Pi]]) при помощи [[Python]] и [http://ianharvey.github.io/bluepy-doc/ библиотеки bluepy]. На данный момент на [[Windows]] и [[Mac OS]] библиотека bluepy не поддерживается. | |||
* Python почти всегда установлен на [[Linux]]. | |||
* Сначала устанавливаем библиотеку bluepy: | |||
<syntaxhighlight lang="ssh"> | |||
sudo apt-get install python-pip libglib2.0-dev | |||
sudo pip install bluepy | |||
</syntaxhighlight> | |||
Затем используем код ниже – обработка рассылаемых данных почти такая же, как и на [[Node.js]], за исключением того, что в этом случае тип сервиса (''ffff'') находится в той же переменной '''value''', что и сами данные. | |||
* Добавляем в '''advertising_python.py''' код ниже: | |||
<syntaxhighlight lang="python"> | |||
from bluepy.btle import Scanner, DefaultDelegate | |||
def onDeviceChanged(addr, data): | |||
print "Device %s, value %s" % (addr,data) | |||
# Устройства, которые мы ищем: | |||
devices = [ | |||
"de:70:d9:0c:eb:86" | |||
]; | |||
# Данные, которые были получены в прошлый раз: | |||
lastAdvertising = {} | |||
# Считываем данные сканирования: | |||
class ScanDelegate(DefaultDelegate): | |||
def __init__(self): | |||
DefaultDelegate.__init__(self) | |||
def handleDiscovery(self, dev, isNewDev, isNewData): | |||
if not dev.addr in devices: return | |||
for (adtype, desc, value) in dev.getScanData(): | |||
if adtype==255 and value[:4]=="9005": # Данные производителя | |||
data = value[4:] | |||
if not dev.addr in lastAdvertising or lastAdvertising[dev.addr] != data: | |||
onDeviceChanged(dev.addr, data) | |||
lastAdvertising[dev.addr] = data | |||
# Начинаем сканирование: | |||
scanner = Scanner().withDelegate(ScanDelegate()) | |||
scanner.clear() | |||
scanner.start() | |||
# Продолжаем сканировать 10-секундными отрезками: | |||
while True: scanner.process(10) | |||
# Если хотим закончить, нужно вызвать stop(): | |||
scanner.stop() | |||
</syntaxhighlight> | |||
* Затем запускаем [[Python]]-файл. Чтобы получить доступ к Bluetooth-устройству, этот файл нужно запускать с правами администратора. | |||
<syntaxhighlight lang="ssh"> | |||
sudo python advertising_python.py | |||
</syntaxhighlight> | |||
И вы получите примерно следующее: | |||
<syntaxhighlight lang="ssh"> | |||
Device de:70:d9:0c:eb:86, value 35 | |||
Device de:70:d9:0c:eb:86, value 37 | |||
Device de:70:d9:0c:eb:86, value 39 | |||
Device de:70:d9:0c:eb:86, value 3a | |||
Device de:70:d9:0c:eb:86, value 3b | |||
Device de:70:d9:0c:eb:86, value 3c | |||
Device de:70:d9:0c:eb:86, value 3d | |||
</syntaxhighlight> | </syntaxhighlight> | ||
Обратите внимание, что '''data''' в '''onDeviceChanged''' – это шестнадцатеричная строка. Чтобы преобразовать её в целое число, воспользуйтесь '''int(data, 16)'''. | |||
== Универсальная платформа Windows (UWP) – C#/JS/VB == | |||
Пример для [[Node.js]] выше будет работать и на Windows, но вы, возможно, захотите использовать какой-то другой язык программирования. Поддержка BLE есть только в [[Windows 10]], так что ваше приложение будет ограничено только [[Windows 10]] и новее. | |||
В документации [[Microsoft]] есть пример кода для отслеживания Bluetooth-объявлений при помощи класса [https://docs.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.advertisement.bluetoothleadvertisementwatcher?view=winrt-19041 BluetoothLEAdvertisementWatcher] – посмотреть его можно [https://docs.microsoft.com/en-us/windows/uwp/devices-sensors/ble-beacon тут]. | |||
Кроме того, есть [https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/BluetoothAdvertisement примеры, полностью написанные] на C#, [[JavaScript]] и [[Visual Basic]]. | |||
== Android == | |||
Для [[Android]] есть [https://developer.android.com/guide/topics/connectivity/bluetooth-le.html хорошая документация по BLE] – посмотрите раздел '''Find BLE Devices («Поиск BLE-устройств»)'''. | |||
Кроме того, по этой ссылке можно найти [https://github.com/googlearchive/android-BluetoothAdvertisements приложение-пример]. | |||
=См.также= | =См.также= | ||
=Внешние ссылки= | =Внешние ссылки= | ||
Строка 18: | Строка 300: | ||
<references /> | <references /> | ||
{{Навигационная таблица/Espruino | {{Навигационная таблица/Портал/Espruino}} | ||
Текущая версия от 15:06, 28 мая 2023
Рассылка BLE-объявлений при помощи Node.js/Python/C#/Android[1]
Коммуницировать с Puck.js через BLE можно тремя разными способами:
- Подключитесь к Puck.js с другого устройства и отправьте данные на сервис Nordic UART (или сервис, который вы создали сами) – большинство устройств (ПК, Mac, Linux, Android) одновременно могут быть подключены максимум к 5-6 устройствам.
- Подключите Puck.js к устройству и коммуницируйте с сервисами этого устройства – большинство устройств поддерживают только одно одновременное подключение.
- Рассылайте с помощью Puck.js объявления для устройств, находящихся в пределах BLE-диапазона – это работает только в одном направлении (от Puck.js к прослушивающему устройству), но так вы можете отправлять данные от какого угодно количества Puck.js.
Более подробно о том, что всё это значит, можно прочесть тут, а здесь мы сосредоточимся на способе с рассылкой BLE-объявлений.
Советы по использованию Bluetooth-соединения читайте тут:
- Подключение через другое Espruino-устройство.
- Подключение через веб-страницу с Web Bluetooth.
- Подключение через Node.js или Python.
Чтобы преобразовать данные объявления в MQTT, можно воспользоваться программой EspruinoHub (причём её также можно использовать вместе с Node-RED), но в этом руководстве мы сосредоточимся на написании собственного приложения-хоста.
Сначала нам нужно разослать BLE-объявления с данными. Это можно сделать при помощи метода NRF.setAdvertising().
Есть два главных типа рассылаемых данных:
- Сервисы – У каждого сервиса есть свой UUID. Он может быть 16-битным или 128-битным. 16-битные UUID присваиваются Bluetooth SIG, так что будьте внимательны и используйте правильные UUID. Вы можете задать собственные 128-битные UUID, но они, в-первых, должны быть случайными, и во-вторых, BLE-объявление очень невелико, так что после использования 128-битного UUID у вас останется мало свободного места. Например, можно воспользоваться UUID сервиса «0xFFFF» (для серийных устройств его использовать не стоит) с помощью команды NRF.setAdvertising({0xFFFF:"Hello"});.
- Данные производителя – Прошивка Espruino/Puck.js 1v95 и новее позволяет задать данные производителя. Это почти то же самое, как использовать сервисы с 16-битными UUID, однако для Espruino/Puck.js зарезервирован собственный 16-битный UUID (0x0590), который можно использовать для чего угодно.
Ниже мы воспользуемся данными производителя. Подключитесь к Puck.js с помощью IDE и загрузите на него следующий код:
var presses = 0;
NRF.setAdvertising({},{manufacturer: 0x0590, manufacturerData:[presses]});
setWatch(function() {
presses++;
NRF.setAdvertising({},{manufacturer: 0x0590, manufacturerData:[presses]});
}, BTN, {edge:"rising", repeat:1, debounce:20})
Этот код рассылает в BLE-объявлениях один байт («0»), но с каждым нажатием на кнопку это значение будет увеличиваться.
Теперь вам надо отключить онлайн-IDE от Puck.js, потому что Puck.js рассылает данные, только когда к нему не подключено никаких устройств.
Puck.js/Espruino
Кроме того, Puck.js умеет прослушивать BLE-объявления других устройств при помощи NRF.setScan() (для непрерывного прослушивания) или NRF.findDevices() (для прослушивания в пределах заданного периода времени и агрегации всех ответов). К примеру, вы можете запустить на другом Puck.js вот такой код...
NRF.findDevices(print)
…и в итоге получите примерно следующее:
[
BluetoothDevice {
"id": "de:70:d9:0c:eb:86 random",
"rssi": -44,
"data": new Uint8Array([2, 1, 5, 4, 255, 144, 5, 11, 20, 9, 69, 115, 112, 114, 117, 105, 110, 111, 32, 78, 82, 70, 53, 50, 56, 51, 50, 68, 75]).buffer,
"manufacturer": 1424,
"manufacturerData": new Uint8Array([0]).buffer,
"name": "Puck.js eb86"
}
]
Как и все прочие устройстве в пределах BLE-диапазона. После этого можно выполнить поиск manufacturer==0x0590 и извлечь передаваемые данные, прочитав поле manufacturerData.
Node.js
В Node.js есть отличный модуль под названием Noble, работающий на Windows, Mac OS и Linux.
- В данный момент в Mac OS Mojave в Noble сломана поддержка BLE, так что лучше воспользоваться вместо него библиотекой noble-mac.
- Пользователи Windows 10 вместо Noble могут воспользоваться noble-uwp – там поддержка Bluetooth есть по умолчанию.
- При использовании Windows вместе с USB-адаптером с BLE-функционалом не нужно устанавливать драйвер, идущий в комплекте с этим адаптером. Это значит, что вам необязательно иметь Windows 10, но если у вас есть Windows 10 и нет поддержки BLE, то вам нужно будет дополнительно подключить USB-адаптер с BLE-функционалом.
- Убедитесь, что у вас установлен Node.js.
- Затем установите noble при помощи npm.
# Устанавливаем Noble, чтобы получить поддержку BLE для Node.js:
npm install noble
# ТОЛЬКО ДЛЯ LINUX
# Разрешаем Node.js получить доступ к BLE
# даже без запуска при помощи «sudo»:
sudo setcap cap_net_raw+eip $(eval readlink -f `which node`))
Теперь добавляем в advertising_nodejs.js код ниже:
var noble = require('noble');
function onDiscovery(peripheral) {
// peripheral.rssi - мощность сигнала
// peripheral.address - MAC-адрес
// peripheral.advertisement.localName - название устройства
// peripheral.advertisement.manufacturerData - данные
- производителя
// peripheral.advertisement.serviceData - обычные
- данные сервиса,
- рассылаемые
- в объявлении
// Игнорируем устройства без данных производителя:
if (!peripheral.advertisement.manufacturerData) return;
// Печатаем в консоли всё, что прочли:
console.log(
peripheral.address,
JSON.stringify(peripheral.advertisement.localName),
JSON.stringify(peripheral.advertisement.manufacturerData)
);
}
noble.on('stateChange', function(state) {
if (state!="poweredOn") return;
console.log("Начинаем сканирование...");
noble.startScanning([], true);
});
noble.on('discover', onDiscovery);
noble.on('scanStart', function() { console.log("Сканирование началось."); });
noble.on('scanStop', function() { console.log("Сканирование завершилось.");})
- И запускаем его при помощи node advertising_nodejs.js
У вас должно получиться примерно следующее:
Начинаем сканирование...
Сканирование началось.
de:70:d9:0c:eb:86 "Puck.js eb86" {"type":"Buffer","data":[144,5,0]}
de:70:d9:0c:eb:86 "Puck.js eb86" {"type":"Buffer","data":[144,5,0]}
de:70:d9:0c:eb:86 "Puck.js eb86" {"type":"Buffer","data":[144,5,0]}
de:70:d9:0c:eb:86 "Puck.js eb86" {"type":"Buffer","data":[144,5,0]}
de:70:d9:0c:eb:86 "Puck.js eb86" {"type":"Buffer","data":[144,5,1]}
de:70:d9:0c:eb:86 "Puck.js eb86" {"type":"Buffer","data":[144,5,1]}
de:70:d9:0c:eb:86 "Puck.js eb86" {"type":"Buffer","data":[144,5,2]}
de:70:d9:0c:eb:86 "Puck.js eb86" {"type":"Buffer","data":[144,5,2]}
de:70:d9:0c:eb:86 "Puck.js eb86" {"type":"Buffer","data":[144,5,3]}
de:70:d9:0c:eb:86 "Puck.js eb86" {"type":"Buffer","data":[144,5,4]}
de:70:d9:0c:eb:86 "Puck.js eb86" {"type":"Buffer","data":[144,5,5]}
de:70:d9:0c:eb:86 "Puck.js eb86" {"type":"Buffer","data":[144,5,5]}
de:70:d9:0c:eb:86 "Puck.js eb86" {"type":"Buffer","data":[144,5,5]}
Вы можете прочесть строчки и от других устройств, у которых есть данные производителя. Как видите, третий элемент в буфере увеличивается при каждом нажатии на кнопку Puck.js.
Это можно улучшить:
- Чтобы нам сообщали обо всех устройствах с данными производителя.
- Чтобы нам всегда присылали обратно рассылаемые данные – даже если они не изменились.
Теперь попробуем новый код. Обязательно скопируйте адрес Puck.js из кода выше в константу devices:
var noble = require('noble');
// Список разрешённых устройств:
const devices = [
"de:70:d9:0c:eb:86"
];
// Рассылаемые BLE-данные, полученные в прошлый раз:
var lastAdvertising = {
};
function onDeviceChanged(addr, data) {
console.log("Device ",addr,"changed data",JSON.stringify(data));
}
function onDiscovery(peripheral) {
// Мы знаем это устройство?
if (devices.indexOf(peripheral.address)<0) return;
// У него есть данные производителя с UUID Espruino/Puck.js's?
if (!peripheral.advertisement.manufacturerData ||
peripheral.advertisement.manufacturerData[0]!=0x90 ||
peripheral.advertisement.manufacturerData[1]!=0x05) return;
// Считываем только наши данные:
var data = peripheral.advertisement.manufacturerData.slice(2);
// Проверяем на предмет изменившихся сервисов:
if (lastAdvertising[peripheral.address] != data.toString())
onDeviceChanged(peripheral.address, data);
lastAdvertising[peripheral.address] = data;
}
noble.on('stateChange', function(state) {
if (state!="poweredOn") return;
console.log("Начинаем сканирование...");
noble.startScanning([], true);
});
noble.on('discover', onDiscovery);
noble.on('scanStart', function() { console.log("Сканирование началось."); });
noble.on('scanStop', function() { console.log("Сканирование завершилось.");});
В итоге получаем:
Начинаем сканирование...
Сканирование началось.
Device de:70:d9:0c:eb:86 changed data {"type":"Buffer","data":[5]}
Device de:70:d9:0c:eb:86 changed data {"type":"Buffer","data":[6]}
Device de:70:d9:0c:eb:86 changed data {"type":"Buffer","data":[7]}
Device de:70:d9:0c:eb:86 changed data {"type":"Buffer","data":[8]}
Device de:70:d9:0c:eb:86 changed data {"type":"Buffer","data":[9]}
Device de:70:d9:0c:eb:86 changed data {"type":"Buffer","data":[10]}
Теперь вы можете написать собственный обработчик для onDeviceChanged, который делал бы то, что вам нужно, когда что-то происходит на Puck.js.
Python
Вы можете сделать то же самое на Linux (включая Raspberry Pi) при помощи Python и библиотеки bluepy. На данный момент на Windows и Mac OS библиотека bluepy не поддерживается.
- Python почти всегда установлен на Linux.
- Сначала устанавливаем библиотеку bluepy:
sudo apt-get install python-pip libglib2.0-dev
sudo pip install bluepy
Затем используем код ниже – обработка рассылаемых данных почти такая же, как и на Node.js, за исключением того, что в этом случае тип сервиса (ffff) находится в той же переменной value, что и сами данные.
- Добавляем в advertising_python.py код ниже:
from bluepy.btle import Scanner, DefaultDelegate
def onDeviceChanged(addr, data):
print "Device %s, value %s" % (addr,data)
# Устройства, которые мы ищем:
devices = [
"de:70:d9:0c:eb:86"
];
# Данные, которые были получены в прошлый раз:
lastAdvertising = {}
# Считываем данные сканирования:
class ScanDelegate(DefaultDelegate):
def __init__(self):
DefaultDelegate.__init__(self)
def handleDiscovery(self, dev, isNewDev, isNewData):
if not dev.addr in devices: return
for (adtype, desc, value) in dev.getScanData():
if adtype==255 and value[:4]=="9005": # Данные производителя
data = value[4:]
if not dev.addr in lastAdvertising or lastAdvertising[dev.addr] != data:
onDeviceChanged(dev.addr, data)
lastAdvertising[dev.addr] = data
# Начинаем сканирование:
scanner = Scanner().withDelegate(ScanDelegate())
scanner.clear()
scanner.start()
# Продолжаем сканировать 10-секундными отрезками:
while True: scanner.process(10)
# Если хотим закончить, нужно вызвать stop():
scanner.stop()
- Затем запускаем Python-файл. Чтобы получить доступ к Bluetooth-устройству, этот файл нужно запускать с правами администратора.
sudo python advertising_python.py
И вы получите примерно следующее:
Device de:70:d9:0c:eb:86, value 35
Device de:70:d9:0c:eb:86, value 37
Device de:70:d9:0c:eb:86, value 39
Device de:70:d9:0c:eb:86, value 3a
Device de:70:d9:0c:eb:86, value 3b
Device de:70:d9:0c:eb:86, value 3c
Device de:70:d9:0c:eb:86, value 3d
Обратите внимание, что data в onDeviceChanged – это шестнадцатеричная строка. Чтобы преобразовать её в целое число, воспользуйтесь int(data, 16).
Универсальная платформа Windows (UWP) – C#/JS/VB
Пример для Node.js выше будет работать и на Windows, но вы, возможно, захотите использовать какой-то другой язык программирования. Поддержка BLE есть только в Windows 10, так что ваше приложение будет ограничено только Windows 10 и новее.
В документации Microsoft есть пример кода для отслеживания Bluetooth-объявлений при помощи класса BluetoothLEAdvertisementWatcher – посмотреть его можно тут.
Кроме того, есть примеры, полностью написанные на C#, JavaScript и Visual Basic.
Android
Для Android есть хорошая документация по BLE – посмотрите раздел Find BLE Devices («Поиск BLE-устройств»).
Кроме того, по этой ссылке можно найти приложение-пример.
См.также
Внешние ссылки