This post is a continuation of the series on Bluetooth with bleno.

Bluetooth with Raspberry Pi and bleno – part 1: iBeacon

Bluetooth with Raspberry Pi and bleno – part 2: GATT

In this post I’ll show how to send notifications to a connected client.

Notifications

Sometimes it is useful to push information to a connected client instead of waiting for it to pull it. An example could be a heartbeat monitor that pushes information about the reading after each detected change. Fortunately, GATT provides us with a way to implement such scenario. A characteristic may support notifyaccess mode, so that a client can subscribe to it.

Counter

I’ll use a simple counter as an example in this post. The counter will increase it’s value once per second. The client will be able to subscribe to it and observe the values as they change.

Let’s start with some UUIDs.

const COUNTER_SERVICE_UUID = "00010000-9FAB-43C8-9231-40F6E305F96D";
const COUNTER_CHAR_UUID = "00010001-9FAB-43C8-9231-40F6E305F96D";

Next step is to declare the counter characteristic:

class CounterCharacteristic extends bleno.Characteristic {
    constructor() {
        super({
            uuid: COUNTER_CHAR_UUID,
            properties: ["notify"],
            value: null
        });

        this.counter = 0;
    }

    onSubscribe(maxValueSize, updateValueCallback) {
        console.log(`Counter subscribed, max value size is ${maxValueSize}`);
        this.updateValueCallback = updateValueCallback;
    }

    onUnsubscribe() {
        console.log("Counter unsubscribed");
        this.updateValueCallback = null;
    }    

    sendNotification(value) {
        if(this.updateValueCallback) {
            console.log(`Sending notification with value ${value}`);

            const notificationBytes = new Buffer(2);
            notificationBytes.writeInt16LE(value);

            this.updateValueCallback(notificationBytes);
        }
    }

    start() {
        console.log("Starting counter");
        this.handle = setInterval(() => {
            this.counter = (this.counter + 1) % 0xFFFF;
            this.sendNotification(this.counter);
        }, 1000);
    }

    stop() {
        console.log("Stopping counter");
        clearInterval(this.handle);
        this.handle = null;
    }
}

There are two methods: onSubscribeand onUnsubscribe, which will be called when a client subscribes or unsubscribes from the characteristic. The former gives us opportunity to store a callback, that will be later used to notify the client about changes in the data. The sendNotificationmethod is called from the timer and makes use of the callback.

To start the peripheral:

let counter = new CounterCharacteristic();
counter.start();


bleno.on("stateChange", state => {

    if (state === "poweredOn") {
        
        bleno.startAdvertising("Counter", [COUNTER_SERVICE_UUID], err => {
            if (err) console.log(err);
        });

    } else {
        console.log("Stopping...");
        counter.stop();
        bleno.stopAdvertising();
    }        
});

bleno.on("advertisingStart", err => {

    console.log("Configuring services...");
    
    if(err) {
        console.error(err);
        return;
    }

    let service = new bleno.PrimaryService({
        uuid: COUNTER_CHAR_UUID,
        characteristics: [counter]
    });

    bleno.setServices([service], err => {
        if(err)
            console.log(err);
        else
            console.log("Services configured");
    });
});

Again, we can use the generic client app to connect to the device and see the result.

You can find full source code for the sample application here.

Summary

This short post explains how to deal with notifications initiated by the peripheral Bluetooth device. In the upcoming posts, we’ll show how to connect and communicate with peripheral devices from mobile apps.