Espruino:Примеры/BLE-коммуникация: различия между версиями
Myagkij (обсуждение | вклад) (Новая страница: «{{Espruino/Панель перехода}} {{Перевод от Сubewriter}} {{Myagkij-редактор}} =<ref>[ www.espruino.com - ]</ref>= <syntaxhighlight...») |
Нет описания правки |
||
(не показано 5 промежуточных версий 2 участников) | |||
Строка 3: | Строка 3: | ||
{{Myagkij-редактор}} | {{Myagkij-редактор}} | ||
=<ref>[ www.espruino.com - ]</ref>= | =BLE-коммуникация<ref>[https://www.espruino.com/BLE+Communications www.espruino.com - BLE Communications]</ref>= | ||
<syntaxhighlight lang="javascript" | В других руководствах (вроде вот этого, где объясняется, как выводить на дисплей [[Pixl.js]] данные, считываемые температурным датчиком) мы используем для коммуникации рассылку Bluetooth-объявлений. Этот тип коммуникации хорошо подходит для проектов, где одно устройство коммуницирует со множеством других устройств, но часто проекты не такие масштабные и в них требуется, чтобы просто два устройства коммуницировали друг с другом. | ||
В этом руководстве мы расскажем о том, как при помощи Espruino-устройства создать подключение к другому устройству. Кроме того, благодаря NRF.setConnectionInterval(), с помощью которой задаётся интервал подключения, эту коммуникацию можно сделать очень энергоэффективной (по умолчанию в Espruino 2v00 и новее используется энергоэффективный интервал, но когда по этому каналу связи начинается приём/передача данных, автоматически включается самая быстрая скорость). | |||
{{Примечание1|Для отправителя рассылка Bluetooth-объявлений очень энергоэффективна, но вот получатель должен постоянно прослушивать пакеты, что очень требовательно к энергопотреблению. Так что смело используйте этот тип коммуникации, если вам надо, чтобы получатель как можно быстрее израсходовал свою батарею.}} | |||
В этом примере мы воспользуемся интерфейсной платой [[MDBT42Q]], подключенной к серводвигателю. Коммуницировать можно несколькими разными способами: | |||
* При помощи отправки команд в виде JavaScript-кода | |||
* При помощи отправки команд на кастомную BLE-характеристику | |||
* При помощи передачи потока данных на кастомную BLE-характеристику | |||
Более подробно читайте ниже (для лучшей навигации мы всё разбили по заголовкам). В видео ниже рассказывается об отправке команд, а в разделе «Передача потока данных» ниже рассказывается, соответственно, о том, как передавать данные потоком. | |||
[https://www.youtube.com/watch?v=lWjuMEIrmY8&feature=emb_title Bluetooth LE on Espruino, Part 1] | |||
== Отправка команд (в виде JavaScript-кода) == | |||
=== Получение (периферийное Bluetooth-устройство) === | |||
Этот код нужен лишь для управления серводвигателем. В нём мы отправляем функцию flag(), содержащую все необходимые нам действия. | |||
<syntaxhighlight lang="javascript"> | |||
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}); | |||
</syntaxhighlight> | |||
=== Отправка (центральное Bluetooth-устройство) === | |||
Чтобы отправить команду так, будто мы печатаем её в [[REPL]], мы просто воспользуемся [http://wikihandbk.com/wiki/Espruino:Примеры/Использование_UART-портов_(NUS)_при_помощи_BLE модулем «ble_simple_uart»]. Помните, что для выполнения этих команд нужно будет также указать символ новой строки – это будет аналогично нажатию {{клавиша|Enter}} в левой части [[IDE Espruino]]. | |||
<syntaxhighlight lang="javascript"> | |||
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}); | |||
</syntaxhighlight> | |||
{{Примечание1|По умолчанию [[Espruino]] считает, что команды отправляются из [[REPL]], и поэтому шлёт обратно все отправленные ей символы (плюс результат выполнения команды). Как правило, это нежелательно, так как замедляет коммуникацию. О том, как отключить это эхо-отображение, читайте в третьем абзаце [https://www.espruino.com/Interfacing этой статьи].}} | |||
== Отправка команд (кастомный Bluetooth-сервис) == | |||
=== Получение (периферийное Bluetooth-устройство) === | |||
Мы также можем создать более «нормальное» Bluetooth-устройство, создав собственный Bluetooth-сервис, а также отключить REPL (которую вы, возможно, использовали ранее) при помощи настройки {uart:false} в функции setServices(). | |||
Вы всегда должны использовать собственный уникальный UUID – его можно создать при помощи UUID-генератора (который можно найти онлайн) или, если у вас Linux, вписав в терминал '''date | md5sum'''. Затем сохраните этот UUID и поменяйте в нём символы с 5-го по 8-ой на «0001», «0002» и так далее в каждом новом сервисе или характеристике, которые вам понадобятся. Для [[BLE]] это эффективный подход, потому что так мы отправляем 128-битный UUID всего один раз. | |||
<syntaxhighlight lang="javascript"> | |||
// Тот же код, что и прежде: | |||
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"}); | |||
</syntaxhighlight> | |||
=== Отправка (центральное Bluetooth-устройство) === | |||
Код для [[Puck.js]] чуть посложнее, чем нужно, потому что также вылавливает ошибки и одновременно управляется сразу с несколькими вызовами. | |||
Но базовую часть, где используются requestDevice(), connect(), getPrimaryService(), getCharacteristic() и writeValue(), можно использовать и в [http://wikihandbk.com/wiki/Espruino:Примеры/Использование_спецификации_Web_Bluetooth_с_Espruino Web Bluetooth]. | |||
<syntaxhighlight lang="javascript"> | |||
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}); | |||
</syntaxhighlight> | |||
{{Примечание1|О том, как повысить скорость соединения при помощи запоминания характеристик, смотрите в видео.}} | |||
== Передача потока данных == | |||
В этой ситуации мы отправляем на [[Espruino]] не команду, а поток данных. | |||
[https://www.youtube.com/watch?v=9y-kHxamDec&feature=emb_title Bluetooth LE on Espruino, Part 2] | |||
=== Получение (периферийное Bluetooth-устройство) === | |||
В коде получателя мы задаём сервисы (как и в прошлом примере), но также дополнительно используем данные, которые были присланы в событии onWrite(). | |||
<syntaxhighlight lang="javascript"> | |||
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"}); | |||
</syntaxhighlight> | |||
== Отправка (центральное Bluetooth-устройство) == | |||
В коде для отправителя данных всё примерно то же самое, что и раньше, но в этот раз мы не отключаемся, а просто постоянно вызываем writeValue(). Обратите внимание, что в этом коде мы также определяем, продолжается ли передача данных и не было ли потеряно соединение. | |||
<syntaxhighlight lang="javascript"> | |||
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); | |||
} | |||
</syntaxhighlight> | |||
=См.также= | =См.также= | ||
=Внешние ссылки= | =Внешние ссылки= | ||
Строка 15: | Строка 245: | ||
<references /> | <references /> | ||
{{Навигационная таблица/Espruino | {{Навигационная таблица/Портал/Espruino}} | ||
Текущая версия от 20:06, 23 мая 2023
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);
}
См.также
Внешние ссылки