Espruino:Примеры/Потоковая передача данных с Bangle.js на ПК

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску

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


Потоковая передача данных с Bangle.js на ПК[1]

В этом руководстве мы расскажем, как передавать потоки данных от Bangle.js на ПК. Для этого мы воспользуемся акселерометром, а также Web Bluetooth на ПК (при помощи библиотеки Puck.js).

Более подробно об использовании Web Bluetooth с Espruino читайте в этой статье.

Во-первых, нам нужно будет настроить хостинг веб-страницы либо на своем ПК (HTTP через localhost), либо онлайн через HTTPS. Об этом немного написано в статье о Web Bluetooth.

Затем скопируйте этот код и попробуйте его в браузере.

Примечание: Вы можете попробовать его прямо здесь, кликнув на кнопку «Попробуй!» справа внизу под кодом.

<html>
 <head>
  <title>Bangle.js Accelerometer streaming</title>
 </head>
 <body>
<script src="https://www.puck-js.com/puck.js"></script>
<button id="btnConnect">Connect</button>
<p>X: <span class="bar"><span id="barX"></span></span></p>
<p>Y: <span class="bar"><span id="barY"></span></span></p>
<p>Z: <span class="bar"><span id="barZ"></span></span></p>
<script>
// Код для загрузки на Bangle.js.
var BANGLE_CODE = `
Bangle.on('accel',function(a) {
  var d = [
    "A",
    Math.round(a.x*100),
    Math.round(a.y*100),
    Math.round(a.z*100)
    ];
  Bluetooth.println(d.join(","));
})
`;

// Когда мы кликаем на кнопку подключения...
var connection;
document.getElementById("btnConnect").addEventListener("click", function() {
  // ...отключаемся, если уже подключены.
  if (connection) {
    connection.close();
    connection = undefined;
  }
  // ...подключаемся.
  Puck.connect(function(c) {
    if (!c) {
      alert("Подключиться не удалось!");
      return;
    }
    connection = c;
    // Обрабатываем данные, пришедшие в ответ,
    // и вызываем onLine() каждый раз, когда получаем строчку.
    var buf = "";
    connection.on("data", function(d) {
      buf += d;
      var l = buf.split("\n");
      buf = l.pop();
      l.forEach(onLine);
    });
    // Во-первых, выполняем сброс Bange.js.
    connection.write("reset();\n", function() {
      // Ждём, пока Bangle.js не выполнят сброс самих себя.
      setTimeout(function() {
        // Теперь загружаем на них наш код.
        connection.write("\x03\x10if(1){"+BANGLE_CODE+"}\n",
          function() { console.log("Готово..."); });
      }, 1500);
    });
  });
});

// Получив строчку данных, проверяем её,
// и если она от акселерометра, обновляем её.
function onLine(line) {
  console.log("ПОЛУЧЕНО:"+line);
  var d = line.split(",");
  if (d.length==4 && d[0]=="A") {
    // Получили данные от акселерометра.
    var accel = {
      x : parseInt(d[1]),
      y : parseInt(d[2]),
      z : parseInt(d[3]),
    };    
    // Обновляем позиции в прямоугольниках.
    setBarPos("barX", accel.x);
    setBarPos("barY", accel.y);
    setBarPos("barZ", accel.z);
  }
}
// Задаём позиции во всех прямоугольниках.
function setBarPos(id,d) {
  var s = document.getElementById(id).style;
  if (d>150) d=150;
  if (d<-150) d=-150;
  if (d>=0) {
    s.left="150px";
    s.width=d+"px";
  } else { // меньше 0
    s.left=(150+d)+"px";
    s.width=(-d)+"px";
  }
}
</script>
<style>
/* Добавляем стили, чтобы прямоугольники
   для X, Y и Z выглядели покрасивее. */
.bar {
  width : 500px;
  height: 24px;
  background-color : #D0D0D0;
  position:relative;
  display: inline-block;
}
.bar span {
  width : 1px;
  height: 20px;
  background-color : red;
  position:absolute;
  display: inline-block;
  left: 150px;
  top: 2px;
}
</style>
 </body>
</html>

Кликаем на Connect, выбираем свои Bangle.js – после этого в браузере сразу же должны начать отображаться данные от акселерометра.

Как это работает?

  • Когда вы кликаете на Connect, функция Puck.connect() создает соединение с Bangle.js при помощи Web Bluetooth.
  • Bangle.js выполняют сброс самих себя, после чего загружается содержимое BANGLE_CODE. То есть мы берём данные акселерометра и передаем их в браузер в формате A,x,y,z.
  • При получении каждой новой строчки вызывается функция onLine() – она определяет строчки в формате A,x,y,z и соответствующим образом обновляет данные в прямоугольниках.

Вы можете добавить в код для Bangle.js побольше функций Bluetooth.println(), чтобы затем снова обнаруживать их с помощью onLine().

Подводные камни

Через соединение типа Web Bluetooth можно передавать только около 2500 байт в секунду. То есть нужно быть осторожнее и не отправлять больше данных. По умолчанию частота передачи данных акселерометра составляет 12.5 Гц, и мы округляем это значение до круглого числа, чтобы не отправлять слишком много символов.

Бонус: трехмерный куб

3D-визуализация данных акселерометра выглядит ещё более впечатляюще. Код тот же, за исключением нижней части для прямоугольников, которая заменена на код из библиотеки Three.js, работающей на технологии WebGL – она используется для рендеринга куба.

<html>
 <head>
   <title>Потоковая передача данных акселерометра от Bangle.js c 3D-визуализацией</title>
 </head>
 <body style="margin:0px">
<script src="https://www.puck-js.com/puck.js"></script>
<button id="btnConnect" style="position:absolute;left:5px;top:5px;z-index:1000">Connect</button>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r99/three.min.js"></script>
<script>
// Код для загрузки на Bangle.js.
var BANGLE_CODE = `
Bangle.on('accel',function(a) {
  var d = [
    "A",
    Math.round(a.x*100),
    Math.round(a.y*100),
    Math.round(a.z*100)
    ];
  Bluetooth.println(d.join(","));
})
`;
var accel = new THREE.Vector3( 0, 0, 1 );

// Когда мы кликаем на кнопку подключения...
var connection;
document.getElementById("btnConnect").addEventListener("click", function() {
  // ...отключаемся, если уже подключились.
  if (connection) {
    connection.close();
    connection = undefined;
  }
  // ...подключаемся.
  Puck.connect(function(c) {
    if (!c) {
      alert("Подключиться не удалось!");
      return;
    }
    connection = c;
    // Обрабатываем данные, пришедшие в ответ,
    // и вызываем onLine() каждый раз, когда получаем строчку.
    var buf = "";
    connection.on("data", function(d) {
      buf += d;
      var l = buf.split("\n");
      buf = l.pop();
      l.forEach(onLine);
    });
    // Во-первых, выполняем сброс Bangle.js.
    connection.write("reset();\n", function() {
      // Ждём, пока Bangle.js выполнят сброс самих себя.
      setTimeout(function() {
        // Теперь загружаем на них наш код.
        connection.write("\x03\x10if(1){"+BANGLE_CODE+"}\n",
          function() { console.log("Готово..."); });
      }, 1500);
    });
  });
});

// Получив строчку данных, проверяем её,
// и если она от акселерометра, обновляем её.
function onLine(line) {
  console.log("ПОЛУЧЕНО:"+line);
  var d = line.split(",");
  if (d.length==4 && d[0]=="A") {
    // Мы получили данные от акселерометра.
    accel.x = parseInt(d[1])/100;
    accel.y = parseInt(d[2])/100;
    accel.z = parseInt(d[3])/100;
    render();
  }
}
// Шаблон WebGL.
var scene, camera, renderer, cube;
var WIDTH  = window.innerWidth;
var HEIGHT = window.innerHeight;
function init() {
    scene = new THREE.Scene();

    camera = new THREE.PerspectiveCamera(70, WIDTH / HEIGHT, 1, 10);
    camera.position.set(0, 3.5, 5);
    camera.lookAt(scene.position);

    cube = new THREE.Mesh(new THREE.CubeGeometry(2, 2, 2), new THREE.MeshNormalMaterial());
    scene.add(cube);

    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(WIDTH, HEIGHT);

    document.body.appendChild(renderer.domElement);
}
function render() {
    cube.lookAt(accel);
    renderer.render(scene, camera);
}

init();
render();
</script>
 </body>
</html>

См.также

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