アルプスのセンサネットワークモジュールをラズパイ3と3GPIで使ってみる(其の2 C言語によるBLEソフト)」でC言語により、センサネットワークモジュールの環境センサを使ったプログラムを作成しましたが、今回は、Pythonインタフェース「bluepy」を使用して、Raspberry Pi 3に接続したアルプスのセンサネットワークモジュールの地磁気センサと加速度センサからモーションデータを取得します。アルプスのセンサネットワークモジュールは、Bluetooth Low Energy(BLE)を用いて、気圧、温度・湿度などの環境データと共に、モーションデータを取得できます。今回は、センサネットワークモジュールを、BLEを用いて、Raspberry Pi 3と接続しました。

なお、今回使用するbluepyに関しては、「IanHarvey/bluepy」を参照してください。

Pythonインタフェース「bluepy」のインストール

次のコマンドでソースコードを取得して、インストールしてビルドします。

$ sudo apt-get install python-pip libglib2.0-dev
$ sudo pip install bluepy
$ sudo apt-get install git build-essential libglib2.0-dev
$ git clone https://github.com/IanHarvey/bluepy.git
$ cd bluepy
$ sudo python setup.py build
$ sudo python setup.py install

bluepyには、sensortag用のサンプルコードsensortag.pyが用意されています。しかし、そのまま実行するとimport処理でエラーとなるので、「bluepy/bluepy/sensortag.py」の1行目の「from .btle import UUID, Peripheral, DefaultDelegate」を「from btle import UUID, Peripheral, DefaultDelegate」に変更します。

アルプスのセンサネットワークモジュールでモーションデータの取得

インストールされた「btle.py」を用いて、センサネットワークモジュールで地磁気センサ(Geo-Magnetic)と加速度センサ(Acceleration)を用いて、100msごとにモーションデータを取得します。

次に、Raspberry Pi 3上で、アルプスのセンサネットワークモジュールからBLEを用いてモーションデータを取得する「alpsensor.py」のコードを示します。btle.pyファイルの「Peripheral」クラスを継承した「AlpsSensor」を使用します。BLE通信は「writeCharacteristic」メソッドを使用し、センサネットワークモジュールのコマンドガイド「Sensor Network Module評価キットApplication Note Command Guide」に従って、100msごとにモーションデータを送信してくるように設定します。Notificationメッセージは、setDelegateメソッドにより設定された NtfyDelegateメソッドにより受信します。

alpsensor.py

# -*- coding: utf-8 -*- 
from btle import Peripheral
import struct 
import btle
import binascii

class NtfyDelegate(btle.DefaultDelegate):
    def __init__(self, params):
        btle.DefaultDelegate.__init__(self)
        # ... initialise here

    def handleNotification(self, cHandle, data): 
        # ... perhaps check cHandle
        # ... process 'data'
        cal = binascii.b2a_hex(data)
        #print u'handleNotification : {0}-{1}:'.format(cHandle, cal)
        
        if int((cal[0:2]), 16) == 0xf2:
            #print 'cal:{0}'.format(type(cal)) 
            GeoMagnetic_X = int((cal[6:8] + cal[4:6]), 16) * 0.15
            GeoMagnetic_Y = int((cal[10:12] + cal[8:10]), 16) * 0.15
            GeoMagnetic_Z = int((cal[14:16] + cal[12:14]), 16) * 0.15
            print 'Geo-Magnetic X:{0:.3f} Y:{1:.3f} Z:{2:.3f}'.format(GeoMagnetic_X, GeoMagnetic_Y, GeoMagnetic_Z)
            Acceleration_X = int((cal[18:20] + cal[16:18]), 16) / 4096
            Acceleration_Y = int((cal[22:24] + cal[20:22]), 16) / 4096
            Acceleration_Z = int((cal[26:28] + cal[24:26]), 16) / 4096
            print 'Acceleration X:{0:.3f} Y:{1:.3f} Z:{2:.3f}'.format(Acceleration_X, Acceleration_Y, Acceleration_Z)

class AlpsSensor(Peripheral):
    def __init__(self,addr):
        Peripheral.__init__(self,addr)
        self.result = 1

def main():
    alps = AlpsSensor("28:A1:83:E1:58:96")
    alps.setDelegate( NtfyDelegate(btle.DefaultDelegate) )

    #モーション(動き)検知 100ms間隔(モーション系センサのみ)
    alps.writeCharacteristic(0x0013, struct.pack('<bb', 0x01, 0x00), True)
    alps.writeCharacteristic(0x0016, struct.pack('<bb', 0x01, 0x00), True)
    
    alps.writeCharacteristic(0x0018, struct.pack('<bbb', 0x2F, 0x03, 0x03), True)
    alps.writeCharacteristic(0x0018, struct.pack('<bbb', 0x01, 0x03, 0x03), True)
    alps.writeCharacteristic(0x0018, struct.pack('<bbb', 0x04, 0x03, 0x01), True)
    alps.writeCharacteristic(0x0018, struct.pack('<bbbb', 0x06, 0x04, 0x64, 0x00), True) # 100msec
    
    alps.writeCharacteristic(0x0018, struct.pack('<bbb', 0x2F, 0x03, 0x01), True)
    alps.writeCharacteristic(0x0018, struct.pack('<bbb', 0x20, 0x03, 0x01), True)
    
# Main loop --------
    while True:
        if alps.waitForNotifications(1.0):
            # handleNotification() was called
            continue

        print "Waiting..."
        # Perhaps do something else here
if __name__ == "__main__":
    main()

インストールされた「btle.py」と同じディレクトリで、上記の作成したpythonコードを実行します。Geo-Magnetic X,Y,ZとAcceleration X,Y,Zに、それぞれ磁気センサと加速度センサから取得したX,Y,Z方向のモーションデータが表示されます。

$ cd bluepy/bluepy
$ sudo python alpsensor.py
Geo-Magnetic X:9793.350 Y:9821.250 Z:11.100
Acceleration X:1.000 Y:0.000 Z:0.000
Geo-Magnetic X:9792.450 Y:9820.800 Z:10.800
Acceleration X:1.000 Y:0.000 Z:0.000
Geo-Magnetic X:9793.050 Y:9821.250 Z:11.250
Acceleration X:1.000 Y:0.000 Z:0.000
Geo-Magnetic X:9792.750 Y:9821.250 Z:11.400
Acceleration X:1.000 Y:0.000 Z:0.000
Geo-Magnetic X:9792.150 Y:9821.400 Z:10.950
Acceleration X:1.000 Y:0.000 Z:0.000
Geo-Magnetic X:9793.200 Y:9820.950 Z:10.950
Acceleration X:1.000 Y:0.000 Z:0.000
Geo-Magnetic X:9794.100 Y:9820.950 Z:11.100
Acceleration X:1.000 Y:0.000 Z:0.000
Geo-Magnetic X:9792.900 Y:9821.550 Z:11.100
Acceleration X:1.000 Y:0.000 Z:0.000
Geo-Magnetic X:9793.050 Y:9820.950 Z:11.400
Acceleration X:1.000 Y:0.000 Z:0.000
Geo-Magnetic X:9793.350 Y:9821.250 Z:10.950
Acceleration X:1.000 Y:0.000 Z:0.000
Geo-Magnetic X:9792.750 Y:9820.800 Z:11.250
Acceleration X:1.000 Y:0.000 Z:0.000
Geo-Magnetic X:9792.600 Y:9821.400 Z:11.100
Acceleration X:1.000 Y:0.000 Z:0.000
Geo-Magnetic X:9792.450 Y:9821.100 Z:10.950
Acceleration X:1.000 Y:0.000 Z:0.000
Geo-Magnetic X:9792.750 Y:9820.950 Z:11.400
Acceleration X:1.000 Y:0.000 Z:0.000
Geo-Magnetic X:9792.750 Y:9821.250 Z:10.500
Acceleration X:1.000 Y:0.000 Z:0.000
Geo-Magnetic X:9793.200 Y:9821.250 Z:11.400
Acceleration X:1.000 Y:0.000 Z:0.000
Geo-Magnetic X:9792.750 Y:9821.700 Z:10.950
Acceleration X:1.000 Y:0.000 Z:0.000
Geo-Magnetic X:9792.600 Y:9821.100 Z:11.400
Acceleration X:1.000 Y:0.000 Z:0.000

アルプスのセンサネットワークモジュールがGATTRequesterで接続できない件

Pythonを使ったアルプスのセンサネットワークモジュールの接続」でインストールしたpybluezの「GATTRequester」を使用して、アルプスのセンサネットワークモジュールを接続したところ、「self.connect()」の実行でエラー「RuntimeError: Could not update HCI connection: Input/output error」が表示されて接続できませんでした。ちなみに、TIのSensorTagでは正常に接続できました。このため、上記に示したようにbluepyを使用してアルプスのセンサネットワークモジュールに接続しています。

pythonのプログラムは「karulis/pybluez」からダウンロードしました。

#!/usr/bin/python
# -*- mode: python; coding: utf-8 -*-

# Copyright (C) 2014, Oscar Acena <oscaracena@gmail.com>
# This software is under the terms of GPLv3 or later.

from __future__ import print_function

import sys
from bluetooth.ble import GATTRequester

class Reader(object):
    def __init__(self, address):
        self.requester = GATTRequester(address, False)
        self.connect()
        self.request_data()

    def connect(self):
        print("Connecting...", end=' ')
        sys.stdout.flush()

        self.requester.connect(True)
        print("OK!")

    def request_data(self):
        data = self.requester.read_by_uuid(
                "00002a00-0000-1000-8000-00805f9b34fb")[0]
        try:
            print("Device name: " + data.decode("utf-8"))
        except AttributeError:
            print("Device name: " + data)


if __name__ == '__main__':
    if len(sys.argv) < 2:
        print("Usage: {} <addr>".format(sys.argv[0]))
        sys.exit(1)

    Reader(sys.argv[1])
print("Done.")

アルプスのセンサネットワークモジュールを実行した結果を次に示します。

$ sudo python sensor_tag.py 28:A1:83:E1:58:96
Connecting... Traceback (most recent call last):
  File "sensor_tag.py", line 40, in 
    Reader(sys.argv[1])
  File "sensor_tag.py", line 16, in __init__
    self.connect()
  File "sensor_tag.py", line 23, in connect
    self.requester.connect(True)
RuntimeError: Could not update HCI connection: Input/output error

TIのSensorTagでは、次のように表示され正常に終了しました。

 $ sudo python sensor_tag.py B4:99:4C:64:CD:DF
Connecting... OK!
Device name: TI BLE Sensor Tag
Done.