【Python】BLE通信の接続時のタイムアウト設定がない場合の対応

Python

BLE接続時の利用するクラスと関数

以前の記事でBLE通信の基本的な内容をまとめました。
参考になれば幸いです。

上記のページでは接続処理をする時に、Peripheralクラスconnect()を使いました。
仕様は以下です。

connect関数の仕様
connect(deviceAddress[, addrType=ADDR_TYPE_PUBLIC[, iface=None]])

Peripheralクラスのconnect関数ドキュメント

上記に記されていますが、これを利用していると問題点がありました。(後述)

connect関数の問題点とは?

まずはBLE接続の基本プログラムです。

from bluepy import btle
##任意のMACアドレスに変更して下さい
MAC_ADDRESS        "xx:xx:xx:xx:xx:xx"


# create instance
peripheral = btle.Peripheral()
# connect
peripheral.connect(MAC_ADDRESS)

connect関数を利用して、接続が成功する場合は問題ありません。

connect関数の問題点

問題点は、connect()を使って接続処理をする時に、接続失敗してタイムアウトが発生する場合です。
確認したときは、処理が終了するのに約40秒程かかっていました。
※上記リンク先のドキュメントを見てもtimeoutに関する引数は記載されていません。

タイムアウトにかかる時間が40秒もあれば長すぎます、、、そんなに待てないです。
解決方法がないかと探していたら同じことを相談している記事がgithubにありました。

Added option for timeout while connecting to peripheral by GunnarI · Pull Request #374 · IanHarvey/bluepy
Added option to pass timeout parameter to the peripheral constructor to be used as timeout for "trying to connect" period. Possible resolution for issue #303

上記の内容を見ていると、ドキュメントに記載はなかったが、
timeoutパラメーターが新規追加されているみたいなので、それを使えば解決できるようです。

ポイント

timeoutに関する引数が追加されているので、それを使えば接続失敗時のタイムアウトについて設定可能!

timeoutパラメータを使う!エラー?

タイムアウトパラメータが追加されているようなので、
引数に追加すればOKということで追加したプログラムです。

from bluepy import btle
##任意のMACアドレスに変更して下さい
MAC_ADDRESS        "xx:xx:xx:xx:xx:xx"

# create instance
peripheral = btle.Peripheral()
# connect タイムアウトはとりあえず3秒で設定しています。
peripheral.connect(MAC_ADDRESS timeout=3)

実行してみるとエラーで実行できません。
タイムアウトに該当する引数は存在しないとのこと…

importモジュールの中身を確認

人によって異なるかもしれませんが、以下を確認してみてください。
存在しない場合は、pythonバージョンが異なっていることも考えられるので利用している環境に応じてパスは修正してください。

cd /usr/local/lib/python3.8/dist-packages/bluepy
cat btle.py | grep _connect

上のコマンドbtle.pyファイルからconnect関数を検索しました。
以下は、connect関数周辺のプログラムです。

#引数を確認してもtimeout引数はない。
def _connect(self, addr, addrType=ADDR_TYPE_PUBLIC, iface=None):
        if len(addr.split(":")) != 6:
            raise ValueError("Expected MAC address, got %s" % repr(addr))
        if addrType not in (ADDR_TYPE_PUBLIC, ADDR_TYPE_RANDOM):
            raise ValueError("Expected address type public or random, got {}".format(addrType))
        self._startHelper(iface)
        self.addr = addr
        self.addrType = addrType
        self.iface = iface
        if iface is not None:
            self._writeCmd("conn %s %s %s\n" % (addr, addrType, "hci"+str(iface)))
        else:
            self._writeCmd("conn %s %s\n" % (addr, addrType))
        rsp = self._getResp('stat')
        while rsp['state'][0] == 'tryconn':
            rsp = self._getResp('stat')
        if rsp['state'][0] != 'conn':
            self._stopHelper()
            raise BTLEDisconnectError("Failed to connect to peripheral %s, addr type: %s" % (addr, addrType), rsp)

インストールしたモジュールが古かった?ためtimeoutパラメータがありませんでしたので、
timeoutパラメータが追加されたモジュールをインストールしていきます。

timeoutが追加されたbluepyインストール

timeoutパラメータが追加された新しいバージョンのbluepyをインストールします。

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

タイムアウトパラメータが追加されていることを確認

インストール完了後、以下のフォルダを確認してください。
※ディレクトリのpythonバージョンは利用環境に応じて変更してください。

#python3.9のところはそれぞれにあったバージョンを確認
cat /usr/local/lib/python3.9/dist-packages/bluepy-1.3.0-py3.8.egg/bluepy/btle.py -n| grep _connect
   409	            self._connect(deviceAddr.addr, deviceAddr.addrType, deviceAddr.iface, timeout)
   411	            self._connect(deviceAddr, addrType, iface, timeout)
   441	    def _connect(self, addr, addrType=ADDR_TYPE_PUBLIC, iface=None, timeout=None):
   472	            self._connect(addr.addr, addr.addrType, addr.iface, timeout)
   474	            self._connect(addr, addrType, iface, timeout)

441行目connect関数の引数を確認するとtimeoutが追加されています。

あとは今インストールしたモジュールを利用できればOKです。

インポートしたモジュールの位置を変更

pythonコードから利用しやすいようにファイルの置かれているディレクトリーとディレクトリ名変更します。

モジュール名を「bluepy」から「bluepy2」に変更

cd /usr/local/lib/python3.8/dist-packages/bluepy-1.3.0-py3.8.egg
sudo mv bluepy bluepy2

bluepy2ディレクトリの位置を変更

cd /usr/local/lib/python3.9/dist-packages
sudo mv bluepy-1.3.0-py3.8.egg/bluepy2/ bluepy2

実装

from bluepy2 import btle
##任意のMACアドレスに変更して下さい
MAC_ADDRESS        "xx:xx:xx:xx:xx:xx"

# create instance
peripheral = btle.Peripheral()
# connect
peripheral.connect(MAC_ADDRESS timeout=3)
チェック

BLE通信時にconnect関数を使ってタイムアウト設定に関する引数が存在しない場合、
timeoutパラメータが追加されているモジュールがあるのでインストールしましょう。

コメント

タイトルとURLをコピーしました