Espruino:Примеры/BLE-коммуникация: различия между версиями

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску
(Новая страница: «{{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" enclose="div">
В других руководствах (вроде вот этого, где объясняется, как выводить на дисплей [[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>


=См.также=
=См.также=
{{ads}}


=Внешние ссылки=
=Внешние ссылки=
Строка 15: Строка 245:
<references />
<references />


{{Навигационная таблица/Espruino}}
{{Навигационная таблица/Портал/Espruino}}
{{Навигационная таблица/Телепорт}}

Текущая версия от 20:06, 23 мая 2023

Перевод: Максим Кузьмин
Проверка/Оформление/Редактирование: Мякишев Е.А.


BLE-коммуникация[1]

В других руководствах (вроде вот этого, где объясняется, как выводить на дисплей Pixl.js данные, считываемые температурным датчиком) мы используем для коммуникации рассылку Bluetooth-объявлений. Этот тип коммуникации хорошо подходит для проектов, где одно устройство коммуницирует со множеством других устройств, но часто проекты не такие масштабные и в них требуется, чтобы просто два устройства коммуницировали друг с другом.

В этом руководстве мы расскажем о том, как при помощи Espruino-устройства создать подключение к другому устройству. Кроме того, благодаря NRF.setConnectionInterval(), с помощью которой задаётся интервал подключения, эту коммуникацию можно сделать очень энергоэффективной (по умолчанию в Espruino 2v00 и новее используется энергоэффективный интервал, но когда по этому каналу связи начинается приём/передача данных, автоматически включается самая быстрая скорость).

Примечание

Для отправителя рассылка Bluetooth-объявлений очень энергоэффективна, но вот получатель должен постоянно прослушивать пакеты, что очень требовательно к энергопотреблению. Так что смело используйте этот тип коммуникации, если вам надо, чтобы получатель как можно быстрее израсходовал свою батарею.

В этом примере мы воспользуемся интерфейсной платой 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});
Примечание

По умолчанию Espruino считает, что команды отправляются из REPL, и поэтому шлёт обратно все отправленные ей символы (плюс результат выполнения команды). Как правило, это нежелательно, так как замедляет коммуникацию. О том, как отключить это эхо-отображение, читайте в третьем абзаце этой статьи.

Отправка команд (кастомный 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);
}

См.также

Внешние ссылки