Previously, I’ve shown how to enable Bluetooth Low Energy (BLE) connections using Python. In the past few months, I have been furiously learning JavaScript for an upcoming set of tutorials dealing with the Intel® Edison. Along the way, I needed to make a demo using BLE and JavaScript, which invariably led me to the bleno module.
Bleno is a great tool, but I found some of the examples a little confusing to follow (I get easily lost when a program is split into more than 2 files without a well-documented API). As a result, I constructed a simple echo server demo program that illustrates how characteristics work in BLE. This demo should work on anything that can run JavaScript and has a Bluetooth transceiver.
For this demo, our device (e.g. Edison or Raspberry Pi) is the “peripheral” and a smartphone will be the “central”. See here for more BLE terminology.
Edison Users
If you are planning to use the Edison as the peripheral, you will need to enable the Bluetooth Low Energy device through Linux. Create a serial connection or SSH to your Edison and enter:
rfkill unblock bluetooth hciconfig hci0 up
Next, modify the base-feeds.conf file:
vi /etc/opkg/base-feeds.conf
Press ‘i’ to insert text, and add the following lines (locations of repositories):
src/gz all http://repo.opkg.net/edison/repo/all src/gz edison http://repo.opkg.net/edison/repo/edison src/gz core2-32 http://repo.opkg.net/edison/repo/core2-32
Save and exit by pressing ‘esc’ and enter ‘:wq’. Start the Bluetooth radio with:
rfkill unblock bluetooth killall bluetoothd hciconfig hci0 up
You can verify that the Bluetooth radio has started with:
hcitool dev
That should give you the Bluetooth MAC address of the Edison.
The Code
First, install bleno:
npm install bleno
In a new file (e.g. “ble_echo.js”), enter:
/** * Simple bleno echo server * Author: Shawn Hymel * Date: November 22, 2015 * * Creates a Bluetooth Low Energy device using bleno and offers one service * with one characteristic. Users can use a BLE test app to read, write, and * subscribe to that characteristic. Writing changes the characteristic's * value, reading returns that value, and subscribing results in a string * message every 1 second. * * This example is Beerware (https://en.wikipedia.org/wiki/Beerware). */ // Using the bleno module var bleno = require('bleno'); // Once bleno starts, begin advertising our BLE address bleno.on('stateChange', function(state) { console.log('State change: ' + state); if (state === 'poweredOn') { bleno.startAdvertising('MyDevice',['12ab']); } else { bleno.stopAdvertising(); } }); // Notify the console that we've accepted a connection bleno.on('accept', function(clientAddress) { console.log("Accepted connection from address: " + clientAddress); }); // Notify the console that we have disconnected from a client bleno.on('disconnect', function(clientAddress) { console.log("Disconnected from address: " + clientAddress); }); // When we begin advertising, create a new service and characteristic bleno.on('advertisingStart', function(error) { if (error) { console.log("Advertising start error:" + error); } else { console.log("Advertising start success"); bleno.setServices([ // Define a new service new bleno.PrimaryService({ uuid : '12ab', characteristics : [ // Define a new characteristic within that service new bleno.Characteristic({ value : null, uuid : '34cd', properties : ['notify', 'read', 'write'], // If the client subscribes, we send out a message every 1 second onSubscribe : function(maxValueSize, updateValueCallback) { console.log("Device subscribed"); this.intervalId = setInterval(function() { console.log("Sending: Hi!"); updateValueCallback(new Buffer("Hi!")); }, 1000); }, // If the client unsubscribes, we stop broadcasting the message onUnsubscribe : function() { console.log("Device unsubscribed"); clearInterval(this.intervalId); }, // Send a message back to the client with the characteristic's value onReadRequest : function(offset, callback) { console.log("Read request received"); callback(this.RESULT_SUCCESS, new Buffer("Echo: " + (this.value ? this.value.toString("utf-8") : ""))); }, // Accept a new value for the characterstic's value onWriteRequest : function(data, offset, withoutResponse, callback) { this.value = data; console.log('Write request: value = ' + this.value.toString("utf-8")); callback(this.RESULT_SUCCESS); } }) ] }) ]); } });
Run the program with:
node ble_echo.js
And you should see output in the console stating that the BLE service is running. Use a BLE test app on your phone (for example, BLE Scanner on Android) to connect to the device.
Try writing to the characteristic, reading from it, and subscribing to it. The console on the device should let you know about incoming requests from the central device (i.e. the smartphone).
Epic.
Thanks, Rex 🙂 After playing with BLE some more, it seems that you don’t really need to put the unofficial repositories into base-feeds.conf (despite what the tutorial on Intel’s site says). From what I can tell, you did it on your tutorial to install bluez. Is that right?
Hi:
When I pair I get this:
home/pi/node_modules/bleno/lib/hci-socket/crypto.js:60
var output = new Buffer(input.length);
^
TypeError: Cannot read property ‘length’ of undefined
at swap (/home/pi/node_modules/bleno/lib/hci-socket/crypto.js:60:32)
at e (/home/pi/node_modules/bleno/lib/hci-socket/crypto.js:37:9)
at Object.c1 (/home/pi/node_modules/bleno/lib/hci-socket/crypto.js:22:9)
at Smp.handlePairingRandom (/home/pi/node_modules/bleno/lib/hci-socket/smp.js:130:12)
at Smp.onAclStreamData (/home/pi/node_modules/bleno/lib/hci-socket/smp.js:58:10)
at EventEmitter.emit (events.js:117:20)
at AclStream.push (/home/pi/node_modules/bleno/lib/hci-socket/acl-stream.js:26:10)
at BlenoBindings.onAclDataPkt (/home/pi/node_modules/bleno/lib/hci-socket/bindings.js:198:21)
at EventEmitter.emit (events.js:106:17)
at Hci.onSocketData (/home/pi/node_modules/bleno/lib/hci-socket/hci.js:455:14)
When I scan I see only 2 primary services, no characteristics, thoug I get connect/disconnect.
You should not see a connect/disconnect Bluetooth option. If you are using an Android phone/tablet, this is a bug. Try rebooting the phone to see if that allows you to connect via BLE.
Edit – I’m assuming that’s what you mean by connect/disconnect option. Otherwise, this seems to be an issue with bleno itself. I recommend taking a look through the issues on their GitHub repo.
Your tutorial helped me a lot, now I can now read/wrtie between my edison & android phone, but I want to edit onReadRequest function to be able to send a string (“Hi”) each sec, but when I use the following code
// Send a message back to the client with the characteristic’s value
onReadRequest : function(offset, callback) {
console.log(“Read request received”);
this.intervalId = setInterval(function() {
callback(new Buffer(“Hi!”));
}, 1000);
},
I get an error, I don’t want to use the onSubscribe function. I’m I missing something ?
What error do you get, and why don’t you want to use onSubscribe? Here is an example of some Edison code that reads a characteristic whenever it changes (using onWriteRequest()): https://github.com/sparkfun/Inventors_Kit_For_Edison_Experiments/blob/master/11-Accelerometer/XDK_11_Edison/main.js. Also, here is another example that sends out a notification over BLE whenever a value changes: https://github.com/sparkfun/Inventors_Kit_For_Edison_Experiments/blob/master/12-BLEController/XDK_12_Edison/main.js. Maybe one of those will help?
The Android side i use does not support the notification method, It only has the read and write characteristics linked with the code written in javascript.
So i want to modify the onReadRequest Method on the Edison to be able to write to the android each second…
The error i get is :
TypeError: value is out of bounds
at TypeError ()
at checkInt (buffer.js:705:11)
at Buffer.writeUInt8 (buffer.js:715:5)
at Gatt.errorResponse (/home/root/node_modules/bleno/lib/hci-socket/gatt.js:293:7)
at null. (/home/root/node_modules/bleno/lib/hci-socket/gatt.js:730:35)
at null. (/home/root/Echo10.js:82:30)
at wrapper [as _onTimeout] (timers.js:261:14)
at Timer.listOnTimeout [as ontimeout] (timers.js:112:15)
If u can help with this it would be greatly appreciated.
Thanks in advance.
The Android should support notify, as that is part of the BLE spec. Perhaps this issue will help: https://github.com/sandeepmistry/bleno/issues/86. Are you using a Buffer() in the callback in send your message?
I can trigger “onReadRequest” successfully on the edison sending only once to the android , The problem i have is in sending a reply to the android each sec using the interval () function, is it possible to do it in the onReadRequest?
onReadRequest : function(offset, callback) {
console.log(“Read request received”);
this.intervalId = setInterval(function() {
callback(new Buffer(“Hi!”));
}, 1000);
},
onReadRequest is only called whenever a device (your Android device, in this case) performs a read on the characteristic. You could have your Android device read the characteristic once per second, which forces the onReadRequest function to be called in the Edison.
I believe that what you want is the onSubscribe function (see the example code in this tutorial), where the callback starts an interval to update the characterstic’s value every second.
Thanks! This is the only tutorial that I’ve been successful with on my edison. I started off with the blePeripheral template from Intel’s XDK IoT edition but that kept failing due to errors on the binding.node. I’ve tried numerous other tutorials with no success. After finding this, I created a new project, copied your source into the main.js and then added “bleno” : “latest” to the package and it finally worked. Thanks again!
Awesome! Glad to hear it worked out for you.
I thought of sending read requests from my android but it is better for me the other way around.
I know onSubscribe/onUnsubscribe are great, I’m using the following android code to read/write successfully after changing service UUID , it also has the notify part I think, but I can’t make it work so I will try to search more on this issue.
Here is the android code: http://www.allaboutcircuits.com/projects/how-to-communicate-with-a-custom-ble-using-an-android-app/
I tried that android code but it fails. I was able to connect to the module but when I click on either button I get this error in the logcat and nothing in the terminal or the android device screen: com.example.android.bluetoothlegatt W/BluetoothLeService: Custom BLE Service not found. Prior to this I DO get Connected to GATT server, the right device is listed, services discovered and characteristics as well.
Hi, its my first time to use intel Edison and ive been following this tutorial to create a BLE peripheral of my device and i know this might be a stupid question but i reached the step where i should install bleno and add the code the a new file and i don’t know how to do so ! am i supposed to install bleno on the board after i create a serial connection with it or globally on my machine ? and how do i create a new file on the board and add the code to it ? i’m using OSX 10.11.6
Correct, you need to connect to your Edison throught an SSH or Serial connection in order to create files and install bleno on the Edison. Here is a guide that might be helpful.
Hi ! i have recently find your blog and its just awesome.Got a lot of unique and informative stuff here .
thanks a lot and keep sharing up.Good Luck!
Great article. I am in the process of using a raspi 3 as the peripheral. I can get read and write of the characteristic working. Essentially, it is the echo example with light blue android central client reading, writing, and notifying correctly. What I would like to do is write bytes from my console to the characteristic to display content on my ble central. What is the easiest way to do this?
It depends on how you want to display that information on your central, which I’m assuming is the Android phone. My first thought is to create an app that uses a BLE central library to show the characteristic’s data. I did something similar here. Specifically, look at the “Phone App” section under “The Code” to see how I used a BLE central Cordova plugin. Hope that helps!
Helloo, Thanks for the great article, just a small question , from my phone i can see the connection but i can’t connect.
I’ve seen things related to using blueZ > 5.14 and disabling bluetoothd, but they’re either outdated or didn’t work for me.
Did you have any issue from this kind ?
I vaguely remember running into issues with trying to connect on my Android a few years ago. I needed to restart my phone to get it to work, if I recall.
Thank you, it actually worked with me, what I had to do was always start it this way
hciconfig hci0 down
sudo HCI_CHANNEL_USER=1 node ble_echo.js
for some reason sometimes the service is not visible, but this is something i’ll dig up.. Thanks for the blog!