Espruino:Примеры/BLE-коммуникация
BLE-коммуникация[1]
В других руководствах (вроде вот этого, где объясняется, как выводить на дисплей Pixl.js данные, считываемые температурным датчиком) мы используем для коммуникации рассылку Bluetooth-объявлений. Этот тип коммуникации хорошо подходит для проектов, где одно устройство коммуницирует со множеством других устройств, но часто проекты не такие масштабные и в них требуется, чтобы просто два устройства коммуницировали друг с другом.
В этом руководстве мы расскажем о том, как при помощи Espruino-устройства создать подключение к другому устройству. Кроме того, благодаря NRF.setConnectionInterval(), с помощью которой задаётся интервал подключения, эту коммуникацию можно сделать очень энергоэффективной (по умолчанию в Espruino 2v00 и новее используется энергоэффективный интервал, но когда по этому каналу связи начинается приём/передача данных, автоматически включается самая быстрая скорость).
В этом примере мы воспользуемся интерфейсной платой MDBT42Q, подключенной к серводвигателю. Коммуницировать можно несколькими разными способами:
- При помощи отправки команд в виде JavaScript-кода
- При помощи отправки команд на кастомную BLE-характеристику
- При помощи передачи потока данных на кастомную BLE-характеристику
Более подробно читайте ниже (для лучшей навигации мы всё разбили по заголовкам). В видео ниже рассказывается об отправке команд, а в разделе «Передача потока данных» ниже рассказывается, соответственно, о том, как передавать данные потоком.
Bluetooth LE on Espruino, Part 1
Отправка команд (в виде JavaScript-кода)
Получение (периферийное Bluetooth-устройство)
Этот код нужен лишь для управления серводвигателем. В нём мы отправляем функцию flag(), содержащую все необходимые нам действия.
var s = require("servo").connect(D14);
s.move(1,3000); // переместить на позицию 0 через 3 секунды
var timeout;
function flag() {
if (timeout) clearTimeout();
s.move(0.2,2000);
timeout = setTimeout(function() {
timeout = undefined;
s.move(1,2000);
},2000);
}
setWatch(flag, BTN, {repeat:true});
Отправка (центральное Bluetooth-устройство)
Чтобы отправить команду так, будто мы печатаем её в REPL, мы просто воспользуемся модулем «ble_simple_uart». Помните, что для выполнения этих команд нужно будет также указать символ новой строки – это будет аналогично нажатию ↵ Enter в левой части IDE Espruino.
function flag() {
NRF.requestDevice({ filters: [{ namePrefix: 'MDBT42Q' }]
}).then(function(device) {
require("ble_simple_uart").write(
device,
"flag()\n",
function() {
print('Готово!');
});
});
}
setWatch(flag, BTN, {repeat:true});
Отправка команд (кастомный Bluetooth-сервис)
Получение (периферийное Bluetooth-устройство)
Мы также можем создать более «нормальное» Bluetooth-устройство, создав собственный Bluetooth-сервис, а также отключить REPL (которую вы, возможно, использовали ранее) при помощи настройки {uart:false} в функции setServices().
Вы всегда должны использовать собственный уникальный UUID – его можно создать при помощи UUID-генератора (который можно найти онлайн) или, если у вас Linux, вписав в терминал date | md5sum. Затем сохраните этот UUID и поменяйте в нём символы с 5-го по 8-ой на «0001», «0002» и так далее в каждом новом сервисе или характеристике, которые вам понадобятся. Для BLE это эффективный подход, потому что так мы отправляем 128-битный UUID всего один раз.
// Тот же код, что и прежде:
var s = require("servo").connect(D14);
s.move(1,3000); // переместить на позицию 0 через 3 секунды
var timeout;
function flag() {
if (timeout) clearTimeout();
s.move(0.2,2000);
timeout = setTimeout(function() {
timeout = undefined;
s.move(1,2000);
},2000);
}
setWatch(flag, BTN, {repeat:true});
// Новый код, где мы задаем сервис и характеристику:
NRF.setServices({
"3e440001-f5bb-357d-719d-179272e4d4d9": {
"3e440002-f5bb-357d-719d-179272e4d4d9": {
value : [0],
maxLen : 1,
writable : true,
onWrite : function(evt) {
// Записав значение в характеристику, вызываем flag():
flag();
}
}
}
}, { uart : false });
// Меняем рассылаемое в объявлениях название:
NRF.setAdvertising({}, {name:"Flag"});
Отправка (центральное Bluetooth-устройство)
Код для Puck.js чуть посложнее, чем нужно, потому что также вылавливает ошибки и одновременно управляется сразу с несколькими вызовами.
Но базовую часть, где используются requestDevice(), connect(), getPrimaryService(), getCharacteristic() и writeValue(), можно использовать и в Web Bluetooth.
var busy = false;
function flag() {
if (busy) {
digitalPulse(LED1,1,[10,200,10,200,10]);
return;
}
busy = true;
var gatt;
NRF.requestDevice({ filters: [{ name: 'Flag' }] })
.then(function(device) {
console.log("Нашли");
digitalPulse(LED2,1,10);
return device.gatt.connect();
}).then(function(g) {
console.log("Подключились");
digitalPulse(LED3,1,10);
gatt = g;
return gatt.getPrimaryService(
"3e440001-f5bb-357d-719d-179272e4d4d9");
}).then(function(service) {
return service.getCharacteristic(
"3e440002-f5bb-357d-719d-179272e4d4d9");
}).then(function(characteristic) {
return characteristic.writeValue(1);
}).then(function() {
digitalPulse(LED2,1,[10,200,10,200,10]);
gatt.disconnect();
console.log("Готово!");
busy=false;
}).catch(function(e) {
digitalPulse(LED1,1,10);
console.log("ERROR",e);
busy=false;
});
}
setWatch(flag, BTN, {repeat:true});
NRF.setServices({}, { uart : false });
NRF.setAdvertising({}, {showName:false, connectable:false, discoverable:false});
Передача потока данных
В этой ситуации мы отправляем на Espruino не команду, а поток данных.
Bluetooth LE on Espruino, Part 2
Получение (периферийное Bluetooth-устройство)
В коде получателя мы задаём сервисы (как и в прошлом примере), но также дополнительно используем данные, которые были присланы в событии onWrite().
NRF.setServices({
"3e440001-f5bb-357d-719d-179272e4d4d9": {
"3e440002-f5bb-357d-719d-179272e4d4d9": {
value : [0],
maxLen : 1,
writable : true,
onWrite : function(evt) {
// Данные пришли в виде байта.
// Преобразовываем их в число в диапазоне между 0 и 1:
var n = evt.data[0] / 255;
// Отправляем данные напрямую серводвигателю как ШИМ:
analogWrite(D14, (1.2 + n*0.8)/20, {freq:50});
}
}
}
}, { uart : false });
// При отключении останавливаем серводвигатель:
NRF.on('disconnect', function() {
digitalWrite(D14,0);
});
// Меняем название на «Flag»:
NRF.setAdvertising({}, {name:"Flag"});
Отправка (центральное Bluetooth-устройство)
В коде для отправителя данных всё примерно то же самое, что и раньше, но в этот раз мы не отключаемся, а просто постоянно вызываем writeValue(). Обратите внимание, что в этом коде мы также определяем, продолжается ли передача данных и не было ли потеряно соединение.
var gatt,characteristic;
NRF.requestDevice({ filters: [{ name: 'Flag' }] }).then(function(device) {
console.log("Нашли");
return device.gatt.connect();
}).then(function(g) {
console.log("Подключились");
gatt = g;
return gatt.getPrimaryService(
"3e440001-f5bb-357d-719d-179272e4d4d9");
}).then(function(service) {
return service.getCharacteristic(
"3e440002-f5bb-357d-719d-179272e4d4d9");
}).then(function (c) {
console.log("Прочли характеристику");
characteristic = c;
startWriting();
});
function startWriting() {
var busy = false;
var i = setInterval(function() {
if (!gatt.connected) {
clearInterval(i);
return;
}
if (busy) return;
busy = true;
var n = analogRead(D5)*255;
characteristic.writeValue([n]).then(function() {
busy = false;
});
}, 50);
}
См.также
Внешние ссылки