LightBlue Beans and JavaScript

The LightBlue Bean communicates with BLE. It comes with a built-in accelerometer, temperature sensor, and RGB LED. And it is Arduino compliant.

At the beginning, I wanted to install Firmata on it to play wit Johnny 5 or CylonJS, but unfortunately, impossible to compile the code(sketch) of an “adapted” Firmata version: https://github.com/jacobrosenthal/arduino/blob/bean/examples/StandardFirmata/StandardFirmata.ino. See this http://citizengadget.com/post/96226562047/firmata-on-lightblue-bean for more informations.

Finally, after a little digging, the author @jacobrosenthal of CitizenGadget is the creator of ble-bean.

@jacobrosenthal explains that:

You can program regular Arduino sketches on the Arduino, but the BLE transceiver is available no matter what sketch you have on the bean.

which means that you can chat directly with the toy …

Preparation

First of all, you have to

Now, some JavaScript

I’m using NodeJS v6, and ble-bean (do npm install ble-bean).

We have 3 files:

  • beans-discover.js, the main file
  • broker.js, a kind of messages broker that allows notifications between objects
  • bean,js, a kind of “wrapper” of ble-bean and noble-device**

beans-discover.js will scan bluetooth beans, connect to them and request for temperature and acceleration, and blink the leds.

The broker: broker.js

"use strict";

class Broker {
  constructor() {
    this.subscriptions = [];
  }

  addSubscription(topic, object) {
    this.subscriptions.push({topic: topic, subscriber: object});
  }

  removeSubscription(topic, object) {/*TODO*/}

  notify(topic, message) {
    if(this.log) console.info(topic, message);
    this.subscriptions
      .filter(item => item.topic == topic)
      .forEach(item => {
        let getOnMessageMethod = item.subscriber.onMessage !== undefined
          ? () => item.subscriber.onMessage(topic, message)
          : () => {throw Error(`${item.subscriber.tagName.toLowerCase()}: onMessage method is undefined!`);};
        getOnMessageMethod();
      });
  }
}
module.exports = Broker ;

For example, if you want that an object subscribe to an event/topic:

let broker = new Broker();
let o = {};
o.onMessage = (topic, data) => {
  console.log(topic, data);
}
broker.addSubscription("temperature", o);

// notify on the topic
broker.notify("temperature", {temperature:42});

The bean: bean.js

"use strict";
// largely inspired of https://github.com/jacobrosenthal/ble-bean/blob/master/examples/bean_example.js
class Bean {
  constructor(connectedBean, broker, delayInterval) {
    this.id = connectedBean._peripheral.id;
    this.address = connectedBean._peripheral.address;
    this.localName = connectedBean._peripheral.advertisement.localName;
    this.txPowerLevel = connectedBean._peripheral.advertisement.txPowerLevel;

    connectedBean.on("accell", (x, y, z, valid) => {
      this.onAccelleration(x, y, z, valid, broker)
    });
    connectedBean.on("temp", (temp, valid) => {
      this.onTemperature(temp, valid, broker)
    });

    connectedBean.on("disconnect", () => {
      this.onDisconnect(connectedBean, broker)
    });

    let getRandomInt = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;

    connectedBean.connectAndSetup(() => {

      this.intervalId = setInterval(()=> {

        //set random led colors between 0-255. I find red overpowering so red between 0-64
        connectedBean.setColor(
          new Buffer([getRandomInt(0,64),getRandomInt(0,255),getRandomInt(0,255)]),
          () => {} // led color sent
        );
        connectedBean.requestAccell(() => {} /* request accell sent */);
        connectedBean.requestTemp(() => {} /* request temp sent */);

      }, delayInterval);

    });
  }
  onAccelleration(x, y, z, valid, broker) {
    broker.notify("accelleration", {
      id: this.id,
      address: this.address,
      localName: this.localName,
      txPowerLevel: this.txPowerLevel,
      status: valid ? "valid" : "invalid",
      accelleration: {
        x: x, y: y, z: z
      }
    });
  }
  onTemperature(temp, valid, broker) {
    broker.notify("temperature", {
      id: this.id,
      address: this.address,
      localName: this.localName,
      txPowerLevel: this.txPowerLevel,
      status: valid ? "valid" : "invalid",
      temperature: temp
    });
  }

  onDisconnect(connectedBean, broker) {
    clearInterval(this.intervalId);
    // Turning off led...
    connectedBean.setColor(new Buffer([0x0,0x0,0x0]), () => {});
    //no way to know if succesful but often behind other commands going out, so just wait 2 seconds
    // Disconnecting from Device...
    setTimeout(connectedBean.disconnect.bind(connectedBean, () => {}), 2000);
    broker.notify("disconnect", {
      id: this.id,
      address: this.address,
      localName: this.localName,
      txPowerLevel: this.txPowerLevel
    });
  }
}

module.exports = Bean ;

The main file: beans-discover.js

"use strict";
let BeansMother = require('ble-bean');
let Bean = require('./bean.js');
let Broker = require('./broker.js');

let beans = [];
beans.onMessage = (topic, data) => {
  console.log(topic, data);
  if(topic=="disconnect") { // remove bean from beans
    beans.slice(
      beans.indexOf(beans.find((bean) => bean.id = data.id)),
      1
    );
  }
}

let messagesBroker = new Broker();

messagesBroker.addSubscription("disconnect", beans);
messagesBroker.addSubscription("temperature", beans);
messagesBroker.addSubscription("accelleration", beans);

console.log("discovering beans ...")
BeansMother.discoverAll(connectedBean => {
  let bean = new Bean(connectedBean, messagesBroker, 1000);
  beans.push(bean);
  console.log("---------------------------------------");
  console.log(bean);
  console.log("---------------------------------------");
});

And now , run it: node beans-discover.js

PS: I’ve done the test with 2 beans.

My next step: run it on a RaspberryPI 3 with some additional goodies. To be continued…

You can find the source here https://github.com/k33g/bean-soup.

That’s all for the moment…

blog comments powered by Disqus

Related posts