LoginSignup
16
27

More than 1 year has passed since last update.

bleno / pybleno で Raspberry Pi を BLE Peripheral として動かしてみる

Last updated at Posted at 2019-05-30

前置き

Raspberry Pi でセンシングしたデータを Android に送信するために BLE 通信 (Central : Android, Peripheral : Raspberry Pi) を実装します。

この記事では

  • Node.js のライブラリ bleno
  • Python のライブラリ pybleno

を利用して Raspberry Pi 3 Model B を BLE Peripheral として動作させます。

Peripheral : Raspberry Pi

bleno も pybleno も処理の流れはほとんど同じです。

  1. Characteristic を作成
  2. Characteristic に handler(onReadRequest, onWriteRequest, onSubscribe, onUnsubscribe, etc.) を設定
  3. PrimaryService に Characteristic を追加
  4. setServices で Peripheral が利用可能な PrimaryService を設定
  5. Advertising を開始

bleno (Node.js)

blenoのインストール

bleno の README.md (Prerequisites, Install) を参照して下さい。
Node.js v9.11.1 で動作を確認しました。

$ sudo apt-get install bluetooth bluez libbluetooth-dev libudev-dev
$ npm install bleno

サンプルプログラム

このプログラムでは、センシングしたデータに変化がある度に Peripheral : Raspberry Pi から Central : Android に通知(notify)する仕組みを想定しています。デバッグ用にreadも実装しています。

実際に Raspberry Pi からセンシングしたデータを利用するのではなく、センサーから得られる値としては、プログラム上で1秒毎に値が +1 される counter を利用します。

bleno_peripheral.js
var util = require('util');
var bleno = require('bleno');

const APPROACH_SERVICE_UUID = '13A28130-8883-49A8-8BDB-42BC1A7107F4';
const APPROACH_CHARACTERISTIC_UUID = 'A2935077-201F-44EB-82E8-10CC02AD8CE1';

let counter = 0;

var Characteristic = bleno.Characteristic;

var ApproachCharacteristic = function() {
    ApproachCharacteristic.super_.call(this, {
        uuid : APPROACH_CHARACTERISTIC_UUID,
        properties: ['read', 'notify'],
        value : null
    });

    this._value = 0;
    this._updateValueCallback = null;
};

util.inherits(ApproachCharacteristic, Characteristic);

ApproachCharacteristic.prototype.onReadRequest = function(offset, callback) {
    console.log(counter);
    callback(this.RESULT_SUCCESS, this._value);
}

ApproachCharacteristic.prototype.onSubscribe = function(maxValueSize, updateValueCallback) {
    console.log('ApproachCharacteristic - onSubscribe');

    this._updateValueCallback = updateValueCallback;
};

ApproachCharacteristic.prototype.onUnsubscribe = function(maxValueSize, updateValueCallback) {
    console.log('ApproachCharacteristic - onUnsubscribe');

    this._updateValueCallback = null;
};

var PrimaryService = bleno.PrimaryService;

bleno.on('stateChange', function(state) {
    console.log('on -> stateChange: ' + state);

    if (state === 'poweredOn') {
        bleno.startAdvertising('Approach', [APPROACH_SERVICE_UUID]);
    } else {
        bleno.stopAdvertising();
    }
});

var approachCharacteristic = new ApproachCharacteristic();

bleno.on('advertisingStart', function(error) {
    console.log('on -> advertisingStart: ' + (error ? 'error ' + error : 'success'));

    if(!error) {
        bleno.setServices([
            new PrimaryService({
                uuid: APPROACH_SERVICE_UUID,
                characteristics: [
                    approachCharacteristic
                ]
            })
        ]);
    }
});

setInterval(()=>{
    counter++;
    approachCharacteristic._value = counter;
    if (approachCharacteristic._updateValueCallback) {
        console.log(`Sending notification with value : ${approachCharacteristic._value}`);

        const notificationBytes = Buffer.from(String(approachCharacteristic._value));
        approachCharacteristic._updateValueCallback(notificationBytes);
    }
}, 1000);

実行結果

$ sudo bleno_peripheral.js
on -> stateChange: poweredOn
on -> advertisingStart: success
ApproachCharacteristic - onSubscribe
Sending notification with value 18
Sending notification with value 19
Sending notification with value 20
Sending notification with value 21

pybleno (Python)

pyblenoのインストール

pybleno の README.md (Install) を参照して下さい。

$ sudo pip3 install pybleno

サンプルプログラム

このプログラムでは、センシングしたデータに変化がある度に Peripheral : Raspberry Pi から Central : Android に通知(notify)する仕組みを想定しています。デバッグ用にreadも実装しています。

実際に Raspberry Pi からセンシングしたデータを利用するのではなく、センサーから得られる値としては、プログラム上で1秒毎に値が +1 される counter を利用します。

pybleno_peripheral.py
from pybleno import *

bleno = Bleno()

APPROACH_SERVICE_UUID = '13A28130-8883-49A8-8BDB-42BC1A7107F4'
APPROACH_CHARACTERISTIC_UUID = 'A2935077-201F-44EB-82E8-10CC02AD8CE1'


class ApproachCharacteristic(Characteristic):

    def __init__(self):
        Characteristic.__init__(self, {
            'uuid': APPROACH_CHARACTERISTIC_UUID,
            'properties': ['read', 'notify'],
            'value': None
        })

        self._value = str(0).encode()
        self._updateValueCallback = None

    def onReadRequest(self, offset, callback):
        print('ApproachCharacteristic - onReadRequest')
        callback(result=Characteristic.RESULT_SUCCESS, data=self._value)

    def onSubscribe(self, maxValueSize, updateValueCallback):
        print('ApproachCharacteristic - onSubscribe')

        self._updateValueCallback = updateValueCallback

    def onUnsubscribe(self):
        print('ApproachCharacteristic - onUnsubscribe')

        self._updateValueCallback = None


def onStateChange(state):
    print('on -> stateChange: ' + state)

    if (state == 'poweredOn'):
        bleno.startAdvertising(name='Approach', service_uuids=[APPROACH_SERVICE_UUID])
    else:
        bleno.stopAdvertising()


bleno.on('stateChange', onStateChange)

approachCharacteristic = ApproachCharacteristic()


def onAdvertisingStart(error):
    print('on -> advertisingStart: ' + ('error ' + error if error else 'success'))

    if not error:
        bleno.setServices([
            BlenoPrimaryService({
                'uuid': APPROACH_SERVICE_UUID,
                'characteristics': [
                    approachCharacteristic
                ]
            })
        ])


bleno.on('advertisingStart', onAdvertisingStart)

bleno.start()


import time

counter = 0

def task():
    global counter
    counter += 1
    approachCharacteristic._value = str(counter).encode()
    if approachCharacteristic._updateValueCallback:

        print('Sending notification with value : ' + str(approachCharacteristic._value))

        notificationBytes = str(approachCharacteristic._value).encode()
        approachCharacteristic._updateValueCallback(data=notificationBytes)


while True:
    task()
    time.sleep(1)

実行結果

$ sudo python3 pybleno_peripheral.py
on -> stateChange: poweredOn
on -> advertisingStart: success
ApproachCharacteristic - onSubscribe
Sending notification with value : 22
Sending notification with value : 23
Sending notification with value : 24
Sending notification with value : 25

Central : Android

【Android】BLE通信ざっくりまとめ
こちらの記事が大変参考になりました。

  • scan : 対象のデバイスと connect する処理
  • onServicesDiscovered : 対象の Characteristic を検索する処理
  • onCharacteristicChanged : 対象の Characteristic の更新されたデータを受け取る処理

【Android】BLE通信ざっくりまとめ のソースコードに追記して実装しました。下のソースコードに実装した関数を載せています。

追記部分.java
    public void scan(boolean enable) {
        scanCallback = initScanCallback();

        if (enable) {
            stopScanHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    isScanning = false;
                    bluetoothLeScanner.stopScan(scanCallback);
                    for (BluetoothDevice device : deviceList) {

                        Log.e("scan", device.getAddress());
                        // BD Addressを指定
                        if ("B8:27:EB:B1:5F:BD".equals(device.getAddress())){
                            Log.d("scan", "***** Discoverd Raspberry Pi *****");
                            connect(getApplicationContext(), device);
                        }
                    }
                }
            }, SCAN_PERIOD);

            isScanning = true;
            bluetoothLeScanner.startScan(scanCallback);
        } else {
            isScanning = false;
            stopScan();
        }
    }

            @Override
            public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
                super.onCharacteristicChanged(gatt, characteristic);

                if ("a2935077-201f-44eb-82e8-10cc02ad8ce1".equals(characteristic.getUuid().toString())) {
                    byte[] notified_value = characteristic.getValue();
                    String notified_message = new String(notified_value);
                }
            }

            @Override
            public void onServicesDiscovered(BluetoothGatt gatt, int status) {
                super.onServicesDiscovered(gatt, status);

                List<BluetoothGattService> serviceList = gatt.getServices();

                for (BluetoothGattService service : serviceList) {
                    List<BluetoothGattCharacteristic> characteristicList = service.getCharacteristics();

                    for (BluetoothGattCharacteristic characteristic : characteristicList) {
                        String characteristicUuid = characteristic.getUuid().toString();
                        if ("a2935077-201f-44eb-82e8-10cc02ad8ce1".equals(characteristicUuid)) {
                            // Notificationを要求する
                            boolean registered = gatt.setCharacteristicNotification(characteristic, true);

                            String CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID = "00002902-0000-1000-8000-00805f9b34fb";

                            // CharacteristicのNotificationを有効化する
                            if (registered) {
                                BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
                                        UUID.fromString(CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID)
                                );
                                descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                                gatt.writeDescriptor(descriptor);
                            }
                        }
                    }
                }
            }

BD Address の確認

上記のソースコードでは、対象のデバイスと接続するために BD Address を指定していますが、Raspberry Pi の BD Address は hciconfig で確認することができます。

$ hciconfig
hci0:   Type: Primary  Bus: UART
    BD Address: B8:27:EB:B1:5F:BD  ACL MTU: 1021:8  SCO MTU: 64:1
    UP RUNNING 
    RX bytes:2277 acl:23 sco:0 events:174 errors:0
    TX bytes:4041 acl:40 sco:0 commands:114 errors:0

BLE デバッグ用のアプリ

Central : Android のプログラムを用意しなくても、下記のアプリが Central としての役割を果たせます。

まとめ

この記事では、bleno で Raspberry Pi に BLE Peripheral の実装を行いました。
Raspberry Pi でセンシングしたデータを BLE で扱う際の参考にして頂ければと思います。

16
27
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
27