Introduction: Intel Edison: BLE Controlled Lights

BLE Lights

A while ago I built a DIY for the Raspberry Pi that could control lights over Wifi. While Intel’s Edison can pull off the same feat, it does one more things. It talks BLE. This is hands-on tutorial on turning your Intel Edison board into a BLE peripheral that you could control from your smartphone. As always, Node.js will be my weapon of choice.

The Setup

Before getting started, make sure you have your Intel Edison running the Yocto build. If you haven’t setup your Edison follow this guide. Once installed the Yocto build, SSH to your board. You will have preinstalled Node.js.

For the development setup, I prefer to use Putty for SSH/Serial and WinSCP to move code from my machine to Edison.

Enable Bluetooth

This is a on boot ritual that you might have to repeat to enable BLE on Edison. You can automate with a shell script. Use the following 4 commands to enable BLE.

bluetooth_rfkill_event &
rfkill unblock bluetooth
hciconfig dev
hciconfig hci0 up

Hardware setup

We will be using the Arduino breakout board with Intel Edison development board to interface with a LED (my setup has a relay). This will be our BLE peripheral.

Step 1: Node.js Package Config

Create a directory for your Node.js app and configure the package.json to install necessary modules. Your package.json should have the following dependencies - async, noble, bleno, util.

{  "name": "ble-light",
  "description": "",
  "version": "0.0.1",
  "main": "app.js",
  "engines": {
    "node": ">=0.10.0"
  },
  "dependencies": {
      "async": "latest",
      "noble": "latest",
      "bleno": "latest",
      "util": "latest",
      "mraa": "latest"
  }
}

Step 2: BLE App and Your Services

For a BLE peripheral, you need to register services that you will be exposing for other hosts. Using the bleno module, you can register your service and start advertising capabilities.

var bleno = require('bleno');<br>var BlenoPrimaryService = bleno.PrimaryService;
var FirstCharacteristic = require('./characteristic');<br><br>bleno.on('stateChange', function(state) {<br>console.log('BLE State: ' + state);
  if (state === 'poweredOn') {
    bleno.startAdvertising('BLE Light', ['fc00']);
  }
  else {
    if(state === 'unsupported'){
      error.log("BLE error. Check board configuration.");
    }
    bleno.stopAdvertising();
  }
});

bleno.on('advertisingStart', function(error) {console.log('Advertising:' + (error ? 'error ' + error : 'success')); if (!error) { bleno.setServices([ new BlenoPrimaryService({ uuid: 'fc00', // Custom BLE Service characteristics: [] // TODO: Add characteristic }) ]); } });

console.log("BLE app initiated...");

The is the basic skeleton which will form your app.js. We are yet to add Characteristics to our service, which we will be doing in the next section.

Step 3: Create BLE Characteristic

Every BLE peripheral exposes services that can be consumed by host devices. You find a comprehensive list of standard services here. Each service has a range of characteristics that used to interact with the service. We will be creating one such characteristic to operate our BLE light through a custom service.

Use the following code for your characteristic.js. The Characteristic below is plain vanilla definition carrying out read, write and notify operations on a variable in memory. We will be modifying this Characteristic in next steps.

var util = require('util');
var bleno = require('bleno');
var BlenoCharacteristic = bleno.Characteristic;

// Initialize BLE Characteristic
var FirstCharacteristic = function() {
FirstCharacteristic.super_.call(this, {
uuid: 'fc0f',
properties: ['read', 'write', 'notify'],
value: null<br> });
this._value = new Buffer("OFF", "utf-8");
console.log("Characterisitic's value: "+this._value);
this._updateValueCallback = null;<br>};

// Inherit the BlenoCharacteristic
util.inherits(FirstCharacteristic, BlenoCharacteristic);

// BLE Read request FirstCharacteristic.prototype.onReadRequest = function(offset, callback) {
console.log('FirstCharacteristic - onReadRequest: value = ' + this._value.toString("utf-8"), offset);
callback(this.RESULT_SUCCESS, this._value);
};

// BLE write request
FirstCharacteristic.prototype.onWriteRequest = function(data, offset, withoutResponse, callback) {
this._value = data;
console.log('FirstCharacteristic - onWriteRequest: value = ' + this._value.toString("utf-8"));
if (this._updateValueCallback) {
console.log('FirstCharacteristic - onWriteRequest: notifying');
this._updateValueCallback(this._value);
}
callback(this.RESULT_SUCCESS);
};

// BLE subscribe
FirstCharacteristic.prototype.onSubscribe = function(maxValueSize, updateValueCallback) {
console.log('FirstCharacteristic - onSubscribe');
this._updateValueCallback = updateValueCallback;
};

// BLE unsubscribe
FirstCharacteristic.prototype.onUnsubscribe = function() {
console.log('FirstCharacteristic - onUnsubscribe');
this._updateValueCallback = null;
};

module.exports = FirstCharacteristic;

Step 4: Modify Characteristic for Light Control

Modify characteristic.js to perform GPIO operation using the mraa module.

The characteristic initialization needs to define and initialize the GPIO pins. I have a relay connected to my setup on the Arduino digital pin 3.

// Initialize BLE Characteristic
var FirstCharacteristic = function() {
  FirstCharacteristic.super_.call(this, {
    uuid: 'fc0f',
    properties: ['read', 'write', 'notify'],
    value: null
  });
  this._value = new Buffer("0", "utf-8");
  console.log("Characterisitic's value: "+this._value);
  this._light = new mraa.Gpio(3);
  this._light.dir(mraa.DIR_OUT);
  this._light.write(0);
  this._updateValueCallback = null;
}

util.inherits(FirstCharacteristic, BlenoCharacteristic);

The BLE write request will read the BLE data for a string. If the string equates to "1" we turn the light on. Else we switch it off. Quick and dirty.

// BLE write request
FirstCharacteristic.prototype.onWriteRequest = function(data, offset, withoutResponse, callback) {
  this._value = data;
  if (data == "1") {
    this._light.write(1);
  }
  else {
    this._light.write(0);
  }
  console.log('FirstCharacteristic - onWriteRequest: value = ' + this._value.toString("utf-8"));
if (this._updateValueCallback) { console.log('FirstCharacteristic - onWriteRequest: notifying');
this._updateValueCallback(this._value); }
callback(this.RESULT_SUCCESS); };

Do not forget to require the mraa module in the JS if you missed reading between the lines. ;)

Step 5: Try It Out

Run the node app.

node app.js

Download the BLE Scanner or another equivalent app for your smartphone. Connect to "BLE Light" and use the "Custom Service". You can now read, write and subscribe to notifications from your peripheral. To turn the light on, send string "1" and "0" to turn off.

The source code of this DIY is available to fork on github here.

For any questions, suggestions feel free to get back to me on twitter.