daigouin
@daigouin

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

Raspberry Piと無線IMUをシリアル通信で接続させたいです

Raspberry Piと無線IMUをシリアル通信で接続させたいです

質問を見てくださりありがとうございます。
お力添えいただけますと幸いです。

Bluetoothで接続する無線IMUで測定した角速度をシリアル通信でラズベリーパイに送信し、
その角速度をもとにモータを回転させる機械を開発したいと考えています。
windosPCでは、同じプログラム(シリアル通信の設定を除く)でシリアル通信ができたのですが、
LINUXのラズベリーパイではシリアル通信ができず、研究が行き詰まっている状態です。

発生している問題・エラー

例外が発生しました: SerialException
device reports readiness to read but returned no data (device disconnected or multiple access on port?)
  File "/home/osumi/Desktop/program09_2024/AMWS.py", line 43, in <module>
    ser.read(1000)
serial.serialutil.SerialException: device reports readiness to read but returned no data (device disconnected or multiple access on port?)

上記のエラーコードが以下のプログラムで実行した際に発生してしまいます。
エラーの内容を調べたところ、シリアルポートデバイスが通信可能な状態であるにもかかわらず、
IMUからデータが送られてきていないことを示しているようです。
プログラムコードを最後に記載しますので、解決の参考にしていただければと思います。

該当するソースコード

ser.read(1000)

自分で試したこと

まず、rfcommのバインドの確認を行いました。
rfcommコマンドを用いて、Bluetoothデバイスの仮想的なシリアルポートとしてシステムにバウンドし、
バインドが成功したか確認すると、

rfcomm0: C9:0B:89:87:CC:4C channel 1 closed [tty-attached]

と表示されました。
このclosedは、バインドが成功していてもBluetooth接続ができていないことから、
通信が開始されていない状態を示しているようです。

先程のエラーから、IMUのBluetooth通信の確認を行いました。
Bluetoothctlより確認すると、正常にペアリングし、接続されていることが確認できました。
このように、Bluetoothの接続がされていないと表示される場合と、
接続がされているという矛盾した結果が出ており、
調べても解決方法が出てこないことから、皆様のお力添えをいただきたいです。

一応、タイムアウト時間を延長しましたが、変わらず通信はできませんでした。

また、試しにワイヤレスイヤホンBluetooth接続させたところ、正常に音声が聞こえたため、
ラズベリーパイのBluetoothが使用できないというわけではないようです。

プログラムコード

import os
import sys
import serial
import time
import struct
import binascii
import ctypes

if __name__ == '__main__':

        # Serial port 
        ser = serial.Serial()
        ser.port = "/dev/rfcomm0"  # 
        ser.timeout=9.0                                
        ser.baudrate = 115200                          

        # Serial port Open
        ser.open() 
        
        #加/角速度計測設定
        header = 0x9A
        cmd = 0x16              #加/角速度計測設定コマンド
        data = 0x01             #計測周期 1ms
        data1 = 0x0A            #計測データ送信の平均回数 10回
        data2 = 0x00            #計測データ記録の平均回数
        
        check = header ^ cmd
        check = check ^ data
        check = check ^ data1
        check = check ^ data2

        print(ser)
	
        list = bytearray([header,  cmd,  data,  data1, data2 , check])

        ser.read(1000)
        ser.write(list)

        str = ser.readline()

        print('CmdRes:' + repr(str))
	
        # 計測開始/計測予約
        header = 0x9A
        cmd    = 0x13           #計測開始/計測予約コマンド
        smode  = 0x00           #計測時刻の設定状態(0:未設定,1:設定あり)
        syear  = 0x00           #開始年
        smonth = 0x01           #開始月
        sday   = 0x01           #開始日
        shour  = 0x00           #開始時
        smin   = 0x00           #開始分
        ssec   = 0x00           #開始秒
        emode  = 0x00
        eyear  = 0x00           #終了年
        emonth = 0x01           #終了月
        eday   = 0x01           #終了日        
        ehour  = 0x00           #終了時
        emin   = 0x00           #終了分
        esec   = 0x00           #終了秒

        #チェックサム
        check = header ^ cmd
        check = check ^ smode
        check = check ^ syear
        check = check ^ smonth
        check = check ^ sday
        check = check ^ shour
        check = check ^ smin
        check = check ^ ssec
        check = check ^ emode
        check = check ^ eyear
        check = check ^ emonth
        check = check ^ eday
        check = check ^ ehour
        check = check ^ emin
        check = check ^ esec
	
        list = bytearray([header,  cmd, smode, syear, smonth, sday, shour, smin, ssec, emode, eyear, emonth, eday, ehour, emin, esec, check])
  
        ser.read(100)
        ser.write(list)
	
        str = ser.readline()
	
        str =ser.read(1)
	
        while ord(str) != 0x9A:
                str = ser.read(1)

        str = ser.read(1)
	
        if ord(str) == 0x80:            #加速度角速度計測データ通知
                
                str = ser.read(4)
                
                data1 = ser.read(1)
                data2 = ser.read(1)
                data3 = ser.read(1)

                if ord(data3) & 0x80:   #加速度角速度計測データ通知
                        data4 = b'\xFF'
                else:
                        data4 = b'\x00'
		
                print(binascii.b2a_hex(data1))
                print(binascii.b2a_hex(data2))
                print(binascii.b2a_hex(data3))
                print(binascii.b2a_hex(data4))
		
                accx = ord(data1)
                accx += ord(data2)<<8
                accx += ord(data3)<<16
                accx += ord(data4)<<24

                print("accx = %d" % (ctypes.c_int(accx).value))
	
        ser.close();

以上、よろしくお願いいたします。

0

2Answer

BluetoothのSPP(Serial Port Profile)サービスおよびその前提としてのSDP(Service Discovery Protocol)サーバが有効になっていないのではないでしょうか?

1Like

Comments

  1. @daigouin

    Questioner

    コメントありがとうございます。

    BluetoothのSPPは有効になっているのですが、SDPの状態を確認したところ、
    有効でなかったので、有効にする手順に沿ってコマンドを実行したのですが、
    sdptool browse local を実行してもエラーが出てしまっている状態です。
    こちらで他の方法も調べて試しているのですが、
    もし、明日になってもSDPの有効化ができないようであれば、
    お力添えをいただけますと幸いです。

    以上、よろしくお願いいたします。

最近の状況にあまりついていけてないのですが、SDPサーバ(sdptool)はdeprecatedになっているようですね…
(だからbluezに --compat オプションなんてものがあるのかな?)

それとは別に過去の質問スレッドに気付きましたので目を通してみました。
まずは確認ですがRaspberry Piは4Bですよね?
(機種やOS類の環境依存のケースもあるので念のため)

その中で少し気になったのが

sudo sdptool browse

を実行した結果として

Protocol Descriptor List:
  "L2CAP" (0x0100)
  "RFCOMM" (0x0003)
    Channel: 2

が、ありましたが、バインドには以下のコマンドを用いて行いましたとありました。

sudo rfcomm bind /dev/rfcomm0 C9:0B:89:87:CC:4C 1

上記のDescriptor情報からするとRFCOMMのChannelは 2 で、書式としては、

rfcomm bind <DEV> <BDADDR> <CHANNEL>

なので、

sudo rfcomm bind /dev/rfcomm0 C9:0B:89:87:CC:4C 2

ではないでしょうか?

1Like

Comments

  1. @daigouin

    Questioner

    コメントありがとうございます。
    ご質問の回答としては、Raspberry Pi4 model Bを使用しています。
    SDPサービスを利用可能にすることができ、コメントにいただいたように、
    バインドのチャンネルをsdptool browseと一致させた状態で
    試しにプログラムを実行してみると、
    エラーコードは以下のようになりました。

    例外が発生しました: TypeError
    ord() expected a character, but string of length 0 found
      File "/home/osumi/Desktop/program09_2024/AMWS.py", line 97, in <module>
        while ord(str) != 0x9A:
    TypeError: ord() expected a character, but string of length 0 found
    

    以前までのエラーが出ていたコードを通過してこのエラーが発生しており、
    以下の出力結果を確認すると、今までも出力されていたシリアル通信の情報の後に、
    CmdRes:bが出力できていることから、
    問題が一つ解決して前進することができました。
    誠にありがとうございます。

    Serial<id=0xb4bd1fe8, open=True>(port='/dev/rfcomm0', baudrate=115200, bytesize=8, parity='N', stopbits=1, timeout=1.0, xonxoff=False, rtscts=False, dsrdtr=False)
    CmdRes:b''
    

    新しく発生したエラーコードについて調べたところ、
    ord()関数が空の文字行列に対して呼び出されたために発生しているとのことです。
    考えられる理由として、シリアルポートからデータが受信されていないことが挙げられるようです。
    試しにバインドのチャンネルを関係のない番号に変更してみたのですが、
    エラーの内容は変わりませんでした。

    WindowsPCの方でもこのエラーは発生することがあり、
    そういった際は、IMUを再接続すればエラーを解消することができるのですが、
    ラズベリーパイでは再接続しても、エラーが消えることなくのこってしまいます。
    もし、何かお気づきになったことがございましたら、
    コメントいただけますと幸いです。
    以上、よろしくお願いいたします。

  2. 通信接続は出来たようで何よりです。:grinning:

    新しく発生したエラーについてですが、直接現物を触れるわけでないので原因については何とも言えませんが、参考までに次の方針を提案するならば、

    1. 根本的な原因を対策したいのであれば、シリアルコンソール等で生のデータストリーム状態を観察したり、場合によってはRasPi基盤のBT-UART変換部にロジアナ(ロジックアナライザ)やオシロ(オシロスコープ)を繋いで観察する等しながら原因を探る。
      (さすがに電波状態要因の場合は観測機材が揃えにくいと思いますが、技術系学校であればワンチャンあるのかな??)
    2. 根底の原因は不明でもよいとか影響がないと考えるのであれば、ord()にデータを渡す前に空文字判定の処理を入れて空文字の場合は無視する。
    3. もう少し真面目な対処であれば try - except で例外処理のブロックを設けて、用途に見合った空文字の場合の処理を記述する。

    という感じでしょうか?

  3. @daigouin

    Questioner

    コメントありがとうございます。

    挙げていただいた方針をもとに、問題の要因について追及していきたいと考えています。
    3つの調査の中で行き詰った場合は、ご相談に乗っていただけますと幸いです。

    また、1つ目の方針の中でお聞きしたいことがあるのですが、
    RasPi基盤のBT-UART変換部とは、GPIOのピンに接続する
    といった解釈であっていますでしょうか?
    ご教示いただけますと幸いです。

    以上、よろしくお願いいたします。

  4. 自分が言った「RasPi基盤のBT-UART変換部」とは、CYW43455(WiFi/BT Module)とBCM2711(SoC)をつなぐUART信号線のことです。
    BCM2711
    CYW43455
    ちょっと検索はしてみましたが、詳細な回路図は出回っていないようなのでプローブがあてられる場所を特定するのは少し難しいかと思います。
    あと、上記の模式図で見る限り、多分GPIOピンにはつながっていないと思います。

    まあ、予算や時間が許すのであれば、GPIOのUARTに外付けのBTモジュールをつなげて、そこのUARTをロジアナ等で観察しながらデバッグするという手もあります。
    ただ、内蔵BTモジュール関係固有の原因であれば、現象は再現はしませんが…
    (逆説的には、現象が再現しなかったならば原因は内蔵BTモジュール関係固有のものであると言えます。)

  5. @daigouin

    Questioner

    コメントありがとうございます。
    こちらでも検索したのですが、私が求めている記事が見つからなかったので、
    画像とともにご教授いただき、誠にありがとうございます。

    現在の私では勉強不足であるため、研究室の者に相談したうえで、
    可能であればこの調査方法を進めつつも、
    他の2つの調査を進めていきたいと考えています。
    もし、Daru-IBN5100様がよろしければ、またご相談に乗っていただきたいのですが、
    私がこのサイトを始めたばかりであるため、使い方が把握できておらず、
    Qiita内で直接連絡する手段がわからないため、
    どのようにお声掛けしていいかわかりません。

    この質問にコメントをした際には、Daru-IBN5100様にも通知が行くようなシステムになっているのでしょうか?
    もし、そういったシステムになっているのであれば、
    調査の中で行き詰った際に、この質問に新たにコメントしますので、
    ご協力いただけますと幸いです。

    以上、よろしくお願いいたします。

  6. 基本的に質問スレッドは一問一答だと思うので、別の質問を次々と同スレッドに連投するのはあまり良くないかもしれませんが、まあ、当該のシリアル通信に関する限りにおいては大丈夫かな??
    一応、コメントは通知が届く設定にしているので気づくとは思います。
    (明示的にメンションで通知を飛ばすのもありかな?)
    ただ、即レスできない状況もあるかと思いますのでその辺はご了承を。 m(__)m

    ついでに老婆心でアドバイスをしておくと、こういうものを開発したりデバッグしたりする時は面倒くさがらずに仕様には良く目を通して動きやデータ内容をイメージできるようにしておくのが一番の解決策だと思います。

    AMWS020仕様

    で、問題解決の糸口を探るために一気に全部のコードを動かすのではなくて、例えば今回のケースであれば、まずは「加速度・角速度パラメータ設定(cmd = 0x16)」の範囲だけのコードに絞ってから試してみると良いと思います。

    とりあえず、仕様書の内容からすると、
    コマンド送信(Tx:)は 0x9A, 0x16, 0x01, 0x0A, 0x00, 0x87 のようなデータ列を送信して、
    AMWS020がコマンドを正常に受け付けた場合であれば、
    レスポンス受信(Rx:)は 0x9A, 0x8F, 0x00, 0x21 のようなデータ列を受信することになるのではないかと思います。
    (BCCの計算等間違ってたらごめんなさい)

    で、メーカーさんのサンプルコードですが、サイトに「※一連のサンプルプログラムはサンプルとして提供しているプログラムであり、無保証です。内容の説明や修正などを含むサポートは基本的に行っておりません。」とあるように本当にサンプルレベルのようです… (^^;

    まあ、自分はPythonに詳しいわけではありませんが、例えばバッファのクリアは、 ser.read(1000) よりも ser.reset_input_buffer() の方が良いのではないかと思います。
    また、コマンドレスポンスの受信も仕様書をよく読んで適切なバイト数を指定する方が良いかと思いますので、「加速度・角速度パラメータ設定(cmd = 0x16)」で言えば str = ser.readline() とするよりも recv_data = ser.read(4) の方が良いのではないかと思います。
    で、慣れないうちのデバッグ用途としては、 print('CmdRes:' + repr(str)) とするよりもまずは、 print(recv_data) と単純命令にして生の状態を確認してみる方が良いのではないかと思います。

    とまあ長くなりましたが、ひとつひとつ地道に確認をしていくことがこういったものの解決の一番の近道で、また地力もつくと思いますのでめげずに取り組んでみてください。:grinning:

  7. @daigouin

    Questioner

    コメントの通知は届くようになっているようで安心しました。
    もし、可能であればこちらにコメントをしますので、
    お手すきの際に読んでいただけますと幸いです。

    AMWS020のコマンドインターフェイスの仕様書まで読んでいただき、
    誠にありがとうございます。
    販売元の会社には、何回かお問い合わせをしているのですが、
    WindowsのPCでできている以上、IMUには問題がないことから、
    対応できかねるとのことなので、こちらで投稿するまでに至りました。

    一応ラズベリーパイでもUSB接続であれば、
    角速度を検出してデータを送信することができるまで進めることができたので、
    サンプルプログラムを基に作成したモータ制御のプログラムに
    Daru-IBN5100様からご教授いただいた修正を加え、
    地道に研究と共に自分の能力も高めていきたいと思います。

    今まで、お付き合いいただきありがとうございました。
    もし、行き詰った際にはご協力いただけますと幸いです。
    以上、よろしくお願いいたします。

Your answer might help someone💌