前提
M5stickCに心拍センサを接続し,心拍数とM5の電源電圧をBLEでRaspberry Pi3に送信しています。
下記のソースコードで順調に動いているのですが,ラズパイのUSBポートに有線LANのUSB変換アダプタを接続してから以下のエラーが出て,プログラムが終了するようになりました。
bluepy.btle.BTLEManagementError: Failed to execute management command 'pasvend'
エラーが10分ほど動かすと出てしまい,一度出ると再起動しないと治らない場合もあります。
実現したいこと
bluepy.btle.BTLEManagementErrorの回避
今回のシステムはデモとして動けばいいので,数分ほど安定した動作ができれば目的を達成できます。
発生している問題・エラーメッセージ
Traceback (most recent call last): File "/home/pi/Emulator/ble_test.py", line 50, in <module> scanner.scan(3.0, passive=True) # 3秒間スキャンする(この間他の処理は止まる) File "/usr/local/lib/python3.9/dist-packages/bluepy/btle.py", line 854, in scan self.stop() File "/usr/local/lib/python3.9/dist-packages/bluepy/btle.py", line 803, in stop self._mgmtCmd(self._cmd()+"end") File "/usr/local/lib/python3.9/dist-packages/bluepy/btle.py", line 312, in _mgmtCmd raise BTLEManagementError("Failed to execute management command '%s'" % (cmd), rsp) bluepy.btle.BTLEManagementError: Failed to execute management command 'pasvend'
該当のソースコード
python
1# -*- coding: utf-8 -*- 2# 心拍数、電圧を取得してBLEでアドバタイズ(ブロードキャスト) 3# M5StickCは4秒:アドバタイズ、1秒:Deep Sleep 4# ラズパイ側は常時スキャンし、データーを取得したらprintする 5 6from bluepy.btle import DefaultDelegate, Scanner, BTLEException 7import sys 8import struct 9from datetime import datetime 10import subprocess 11 12class ScanDelegate(DefaultDelegate): 13 def __init__(self): # コンストラクタ 14 DefaultDelegate.__init__(self) 15 self.lastseq = None 16 self.lasttime = datetime.fromtimestamp(0) 17 print("init") 18 19 def handleDiscovery(self, dev, isNewDev, isNewData): 20 if isNewDev or isNewData: # 新しいデバイスまたは新しいデータ 21 for (adtype, desc, value) in dev.getScanData(): # データの数だけデータスキャンを繰り返す 22 # 'ffff' = テスト用companyID 23 # M5stickCからのデータを見つけたら 24 if desc == 'Manufacturer' and value[0:4] == 'ffff': 25 __delta = datetime.now() - self.lasttime # 前回取得時刻からの差分をとる 26 # アドバタイズする間に複数回測定されseqが加算されたものは捨てる(最初に取得された1個のみを使用する) 27 if value[4:6] != self.lastseq and __delta.total_seconds() > 5: 28 self.lastseq = value[4:6] # Seqと時刻を保存 時刻はどこにあるんだ 29 self.lasttime = datetime.now() 30 (bpm, volt) = struct.unpack_from('<hh', bytes.fromhex(value[6:])) 31 print('心拍数 = {0} bpm, 電圧 = {1} V' .format(bpm, volt/100)) 32 if bpm > 80: 33 loss_netem = (bpm - 80) * 2 34 if loss_netem > 70: 35 loss_netem = 70 36 cmd_netem = "sudo tc qdisc change dev eth0 root netem loss " + str(loss_netem) + "%" 37 subprocess.run( 38 cmd_netem, shell=True) 39 print("loss: " + str(loss_netem) + "%") 40 else: 41 subprocess.run(['sudo tc qdisc change dev eth0 root netem loss 0%'], shell=True) 42 43# 有線'eth0'から出ていくパケットにネットワークエミュレーションを追加 44subprocess.run(["sudo tc qdisc add dev eth0 root netem"], shell=True) 45 46scanner = Scanner().withDelegate(ScanDelegate()) 47try: 48 while True: 49 # try: 50 # スキャンする。デバイスを見つけた後の処理はScanDelegateに任せる 51 print("scan") 52 scanner.scan(5.0) # 5秒間スキャンする(この間他の処理は止まる) 53except KeyboardInterrupt as e: 54 print("Aborted!") 55finally: 56 # ネットワークエミュレータの削除 57 # ここで遅延を元に戻してから終了する 58 subprocess.run(["sudo tc qdisc del dev eth0 root"], shell=True)
試したこと
python
1scanner.scan(5.0, passive = True)
パッシブスキャンをTrueにしたのですが,却ってエラーの頻度が増えました。。
また,エラーで強制終了したタイミングでbluetoothctlを開いて確認したところ,
[bluetooth]# scan on Failed to start discovery: org.bluez.Error.InProgress
と出ています。参考にしたページのコードよりもアドバタイズやスキャンの秒数を短くしているので、個人的にはスキャン中に追いスキャンしてしまってる(?)のかなと思ってます。。
補足情報(FW/ツールのバージョンなど)
M5stickCのソースコード(送信部分のみ抜粋)
心拍数を扱うにあたって、連続的に値を測定・送信したいと考え,deep sleepはさせずにloop()内でアドバタイズさせています。
C++
1#define T_PERIOD 4 // アドバタイジングパケットを送る秒数 2#define S_PERIOD 1 // Sleepする秒数 T+S秒ごとに送ることになる 3 4void setAdvData(BLEAdvertising *pAdvertising) 5{ // アドバタイジングパケットを整形する 6 BLEAdvertisementData oAdvertisementData = BLEAdvertisementData(); 7 8 oAdvertisementData.setFlags(0x06); // BR_EDR_NOT_SUPPORTED | General Discoverable Mode 9 // oAdvertisementData.setFlags(0x05); // BR_EDR_NOT_SUPPORTED | Limited Discoverable Mode 10 11 std::string strServiceData = ""; 12 strServiceData += (char)0x08; // 長さ(12Byte → 7Byte 0x07の方がいいかも): 0 13 strServiceData += (char)0xff; // AD Type 0xFF: Manufacturer specific data : 1 14 strServiceData += (char)0xff; // Test manufacture ID low byte : 2 15 strServiceData += (char)0xff; // Test manufacture ID high byte : 3 16 strServiceData += (char)seq; // シーケンス番号 : 4 17 strServiceData += (char)(myBPM & 0xff); // 心拍数の下位バイト(下位を前にして保存するリトルエンディアン形式) : 5 18 strServiceData += (char)((myBPM >> 8) & 0xff); // 心拍数の上位バイト : 6 19 strServiceData += (char)(vbat & 0xff); // 電池電圧の下位バイト 20 strServiceData += (char)((vbat >> 8) & 0xff); // 電池電圧の上位バイト 21 22 oAdvertisementData.addData(strServiceData); 23 pAdvertising->setAdvertisementData(oAdvertisementData); 24} 25 26 27void setup(){ 28 29 // M5と心拍センサの初期化等 30 31 // ここからBLE通信 32 BLEDevice::init("blepub-01"); // デバイスを初期化 33 BLEServer *pServer = BLEDevice::createServer(); // サーバーを生成 34 BLEAdvertising *pAdvertising = pServer->getAdvertising(); // アドバタイズオブジェクトを取得 35} 36 37void loop(){ 38 39 // センサ値測定後 40 41 BLEServer *pServer = BLEDevice::createServer(); // サーバーを生成 42 BLEAdvertising *pAdvertising = pServer->getAdvertising(); // アドバタイズオブジェクトを取得 43 setAdvData(pAdvertising); // アドバタイジングデーターをセット 44 45 pAdvertising->start(); // アドバタイズ起動 46 delay(T_PERIOD * 1000); // T_PERIOD秒アドバタイズする 47 // delay(20); // considered best practice in a simple sketch. 48 pAdvertising->stop(); // アドバタイズ停止 49 50 seq++; // シーケンス番号を更新 51 // delay(10); 52 delay(S_PERIOD * 1000); // considered best practice in a simple sketch. 53 54}
参考文献

バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2022/08/01 07:01 編集