前置き
Raspberry Pi でセンシングしたデータを Android に送信するために BLE 通信 (Central : Android, Peripheral : Raspberry Pi) を実装します。
この記事では
を利用して Raspberry Pi 3 Model B を BLE Peripheral として動作させます。
Peripheral : Raspberry Pi
bleno も pybleno も処理の流れはほとんど同じです。
- Characteristic を作成
- Characteristic に handler(onReadRequest, onWriteRequest, onSubscribe, onUnsubscribe, etc.) を設定
- PrimaryService に Characteristic を追加
- setServices で Peripheral が利用可能な PrimaryService を設定
- 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 を利用します。
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 を利用します。
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通信ざっくりまとめ のソースコードに追記して実装しました。下のソースコードに実装した関数を載せています。
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 としての役割を果たせます。
- Androd : BLE Scanner: Read,Write,Notify
- iPhone : LightBlue® Explorer
まとめ
この記事では、bleno で Raspberry Pi に BLE Peripheral の実装を行いました。
Raspberry Pi でセンシングしたデータを BLE で扱う際の参考にして頂ければと思います。