pyblenoライブラリを使ってBLE Peripheralの作成」でRapberryPi Zero WをBLE Peripheralとして作成しましたが、Windows PCに接続できませんでした。今回は、NotifyによりRapberryPi Zero WからWindows PCにデータを送信します。Python言語でBLE Peripheralを作成するために前回と同様にpyblenoライブラリを使用します。

作成するBLE Peripheralプログラムの仕様

使用するUUIDを次に示します。
0000ec00-0000-1000-8000-00805f9b34fb:サービスUUID
0000ec0f-0000-1000-8000-00805f9b34fb:Notifyによるデータの読み込み

1秒ごとに文字「20」をNotifyにより通知します。

BLE Peripheralプログラムの作成

RapberryPi Zero W上で、Python言語により作成したBLE Peripheralプログラムを次に示します。

  • 56行目で 、クラス「EchoCharacteristic」をインスタンス化して、41行目でサービスUUIDとしてまとめます。
  • 各クラスのCHARACTERISTIC UUIDを11行目等、propertiesを12行目等にそれぞれ設定し、必要な「onxxx」を定義します。
  • 61行目でNotifyが受信できる状態化を確認し、64行目でNotifyによりデータを送信します。

bleperipheralPC.py

from pybleno import *
import sys
import signal
import time


class EchoCharacteristic(Characteristic):

    def __init__(self, uuid):
        Characteristic.__init__(self, {
            'uuid': uuid,
            'properties': ['read', 'write', 'notify'],
            'value': None
        })
        self._value = array.array('B', [0] * 0)
        self._updateValueCallback = None

    def onSubscribe(self, maxValueSize, updateValueCallback):
        print('EchoCharacteristic - onSubscribe')
        self._updateValueCallback = updateValueCallback

    def onUnsubscribe(self):
        print('EchoCharacteristic - onUnsubscribe');

        self._updateValueCallback = None


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

    if (state == 'poweredOn'):
        bleno.startAdvertising('bleperipheralPC', ['0000ec00-0000-1000-8000-00805f9b34fb'])
    else:
        bleno.stopAdvertising();


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

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


if __name__ == '__main__':
    print('bleno - echo');
    bleno = Bleno()
    bleno.on('stateChange', onStateChange)
    bleno.on('advertisingStart', onAdvertisingStart)
    tomosoftCharacteristic_read = EchoCharacteristic('0000ec0f-0000-1000-8000-00805f9b34fb')
    bleno.start()

    while True:
        time.sleep(1)
        if tomosoftCharacteristic_read._updateValueCallback:
            print('Sending notification with value-cmd : ')
            notificationBytes = str(20).encode()
            tomosoftCharacteristic_read._updateValueCallback(notificationBytes)

BLE Centrallプログラムの作成

Windows PC上で、C#言語により作成したBLE Centrallプログラムを次に示します。

  • 23行目にはRapberryPi Zero Wのデバイス名、25行目にはNotifyのUUIDを設定します。
  • RapberryPi Zero Wが検出されると、75行目に制御が移ります。
  • 90行目でCCCD への書き込みを行います。この設定によりNotifyでパソコンに通知(コールバック)されます。
  • 95行目で、データが変化したときに実行されるコールバック関数「Changed_data」を登録します。
  • 106行目のコールバック関数「Changed_data」では、受信したデータの最初のデータを表示します。
using System;
using System.Linq;
using System.Threading;
using Windows.Devices.Bluetooth;
using Windows.Devices.Bluetooth.Advertisement;
using Windows.Devices.Bluetooth.GenericAttributeProfile;

namespace BleCentral
{
    class Program
    {
       static SensorModule sensormodule;
 
        static void Main(string[] args)
        {
            sensormodule = new SensorModule();
            sensormodule.Start();
        }
    }

    class SensorModule
    {
        const string SENSOR_NAME = "raspberrypi";
        // Bluetooth®サービス
        static Guid UUID_TEMP_SERV = new Guid("0000ec00-0000-1000-8000-00805f9b34fb");
        // 計測センサデータやモジュール内部ステータスをパソコンに通知
        static Guid UUID_TEMP_DATA = new Guid("0000ec0f-0000-1000-8000-00805f9b34fb");

        public BluetoothLEAdvertisementWatcher advWatcher;

        public void Start()
        {
            Console.WriteLine("Start");
            this.advWatcher = new BluetoothLEAdvertisementWatcher();
            this.advWatcher.ScanningMode = BluetoothLEScanningMode.Passive;

            this.advWatcher.Received += this.Watcher_Received;

            // スキャン開始
            this.advWatcher.Start();
            Thread.Sleep(500000);
            this.advWatcher.Stop();
            Console.WriteLine("Stop");
        }

        private void Watcher_Received(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
        {
            this.CheckArgs(args);
        }

        public async void CheckArgs(BluetoothLEAdvertisementReceivedEventArgs args)
        {
            Console.WriteLine("Receivedi:{0}", args.BluetoothAddress);
            BluetoothLEDevice dev = await BluetoothLEDevice.FromBluetoothAddressAsync(args.BluetoothAddress);

            Console.WriteLine("---Received---");
            var bleServiceUUIDs = args.Advertisement.ServiceUuids;

            Console.WriteLine("Found");
            Console.WriteLine("MAC:" + args.BluetoothAddress.ToString());
            Console.WriteLine("NAME:" + args.Advertisement.LocalName.ToString());
            Console.WriteLine("ServiceUuid");
            foreach (var uuidone in bleServiceUUIDs)
            {
                Console.WriteLine(uuidone.ToString());
            }
            Console.WriteLine("---END---");
            Console.WriteLine("");

            if (dev.Name == SENSOR_NAME)
            {
                // 検出
                try
                {
                    Console.WriteLine($"Service Find!");

                    // スキャンStop
                    this.advWatcher.Stop();

                    BluetoothLEDevice device = await BluetoothLEDevice.FromBluetoothAddressAsync(args.BluetoothAddress);

                    var services = await device.GetGattServicesForUuidAsync(UUID_TEMP_SERV);
                    var characteristics = await services.Services[0].GetCharacteristicsForUuidAsync(UUID_TEMP_DATA);

                    if (characteristics.Status == GattCommunicationStatus.Success)
                    {
                        var gattCharacteristic = characteristics.Characteristics.First();

                        // CCCD への書き込みが必要。これがないとイベントハンドラが呼ばれない。
                        GattCommunicationStatus status
                            = await gattCharacteristic.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify);

                        if (status == GattCommunicationStatus.Success)
                        {
                            gattCharacteristic.ValueChanged += Changed_data;
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Exception...{ex.Message})");
                }
            }
        }

        public void Changed_data(GattCharacteristic sender, GattValueChangedEventArgs eventArgs)
        {
            Console.WriteLine($"characteristicChanged...Length={eventArgs.CharacteristicValue.Length}");
            byte[] data = new byte[eventArgs.CharacteristicValue.Length];
            Windows.Storage.Streams.DataReader.FromBuffer(eventArgs.CharacteristicValue).ReadBytes(data);
            Console.WriteLine($"characteristicChanged...{data} " + data[0]); ;

            return;
        }
    }
}

作成したプログラムの実行

RapberryPi Zero W上で次のコマンドによりBLE Peripheralプログラムを実行します。

  • Notify要求を受け取ると「EchoCharacteristic – onSubscribe」が表示されます。
  • Notifyでデータを送信すると「Sending notification with value-cmd」が表示されます。
$ sudo python3 bleperipheralPC.py
bleno - echo
on -> stateChange: poweredOn
on -> advertisingStart: success
EchoCharacteristic - onSubscribe
Sending notification with value-cmd :
Sending notification with value-cmd :
Sending notification with value-cmd :
Sending notification with value-cmd :
Sending notification with value-cmd :
Sending notification with value-cmd :
Sending notification with value-cmd :
Sending notification with value-cmd :
Sending notification with value-cmd :
Sending notification with value-cmd :

Windows PC上でBLE Centrallプログラムを実行し、BLE Peripheralとペアリングできると、次のように受信されたデータを表示します。

Start
Receivedi:202481601467365
---Received---
Found
MAC:202481601467365
NAME:
ServiceUuid
0000ec00-0000-1000-8000-00805f9b34fb
---END---

Service Find!
characteristicChanged...Length=2
characteristicChanged...System.Byte[] 50
characteristicChanged...Length=2
characteristicChanged...System.Byte[] 50
characteristicChanged...Length=2
characteristicChanged...System.Byte[] 50
characteristicChanged...Length=2
characteristicChanged...System.Byte[] 50
characteristicChanged...Length=2
characteristicChanged...System.Byte[] 50
characteristicChanged...Length=2
characteristicChanged...System.Byte[] 50