mattintosh note

どこかのエンジニアモドキの備忘録

Raspberry Pi と Bluetooth 経由でシリアル通信する

前回は USB シリアル変換アダプタによる通信だったので今回は Bluetooth で。

メインの Ubuntu から Raspberry PiBluetooth で接続してシェルで操作できるようにするまで。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 17.04
Release:        17.04
Codename:       zesty
pi@raspberrypi:~$ lsb_release -a
No LSB modules are available.
Distributor ID: Raspbian
Description:    Raspbian GNU/Linux 8.0 (jessie)
Release:        8.0
Codename:       jessie

とりあえず bluetoothctl でのペアリングが終わってるものとする。

まず、Bluetooth でシリアル通信を行うには bluetoothd-C オプション付きで起動しなければならない。-C オプションが無い場合、sdptool が使えない。

サービスとして起動している bluetoothd を一旦止める。

pi@raspberrypi:~$ sudo systemctl stop bluetooth

bluetoothd を手動でバックグラウンド起動。アダプタが落ちていることがあるので立ち上げ直しておく。

pi@raspberrypi:~$ sudo bluetoothd -C &
pi@raspberrypi:~$ sudo hciconfig hci0 up

この時点ではまだシリアル通信ができない。

pi@raspberrypi:~$ sudo sdptool browse local | grep -i serial

sdptool で SPP を追加する。add --channel={n} で任意のチャンネルも指定できる。

pi@raspberrypi:~$ sudo sdptool add SP
pi@raspberrypi:~$ sudo sdptool browse local | grep -i serial
Service Name: Serial Port
  "Serial Port" (0x1101)
  "Serial Port" (0x1101)

/dev/rfcomm0 に端末を割り当てて待機状態にする。rfcomm コマンドの使い方がまだ掴めていない。gettyagetty へのリンク。

書式とオプションは以下の通り。

RFCOMM configuration utility ver 5.43
Usage:
        rfcomm [options] <command> <dev>

Options:
        -i, --device [hciX|bdaddr]     Local HCI device or BD Address
        -h, --help                     Display help
        -r, --raw                      Switch TTY into raw mode
        -A, --auth                     Enable authentication
        -E, --encrypt                  Enable encryption
        -S, --secure                   Secure connection
        -M, --master                   Become the master of a piconet
        -L, --linger [seconds]         Set linger timeout
        -a                             Show all devices (default)

Commands:
        bind     <dev> <bdaddr> [channel]       Bind device
        release  <dev>                          Release device
        show     <dev>                          Show device
        connect  <dev> <bdaddr> [channel]       Connect device
        listen   <dev> [channel [cmd]]          Listen
        watch    <dev> [channel [cmd]]          Watch

普通にログインさせる場合。

pi@raspberrypi:~$ sudo rfcomm watch 0 1 agetty rfcomm0 115200 linux

ユーザ pi でオートログインするさせる場合。

pi@raspberrypi:~$ sudo rfcomm watch 0 1 agetty rfcomm0 115200 linux -a pi

認証無しでルートログインさせる場合。

pi@raspberrypi:~$ sudo rfcomm watch 0 1 agetty -n -l /bin/bash rfcomm0 115200 linux

watch ではなく listen もあるが、こっちは接続を切るとサーバ側の rfcomm も終了する。

agetty のオプションがよくわからんのだが、ログイン後に下記のエラーが表示される。apt-get update なんかをやると躓く。

-bash: cannot set terminal process group (1825): Inappropriate ioctl for device
-bash: no job control in this shell

接続元からは screen コマンドなどで接続しにいくので rfcomm connect ではなく rfcomm bind しておく。

■書式

bind <dev> <bdaddr> [channel]

チャンネルは指定しなくても勝手に見つけてくれる。

$ sudo rfcomm bind 0 01:23:45:67:89:AB
$ rfcomm
rfcomm0: 01:23:45:67:89:AB channel 1 clean

これで接続元の /dev/rfcomm0 を使って接続する準備ができた。

screen コマンドで rfcomm bind してある /dev/rfcomm0 に接続する。

$ screen /dev/rfcomm0 115200
Raspbian GNU/Linux 8 raspberrypi rfcomm0

raspberrypi login: 

別の端末で watch なんかで状態のチェックをしておくといいかもしれない。

$ watch rfcomm -a
rfcomm0: B8:27:EB:0D:F6:C4 channel 1 connected [tty-attached]

無事 Raspberry Pi に接続できたが、接続する度に最初に AT AT ??? みたいな AT コマンドを何か送ってるっぽいのだが何だこれ?

USB シリアル変換アダプタで接続しているときは出ないので多分 rfcomm 関係だが AT というワードで探すのは辛い…。なんとか見つけた「Disable AT command on rfcomm connect」に書いてあった。

stackoverflow.com

ModemManager よ、またお前か…。

$ sudo systemctl stop ModemManager
$ sudo systemctl disable ModemManager

コンソールのサイズが小さいのは stty で調整。tmux を使っている場合は一回 detach してから。

$ stty rows 44 cols 148
$ tmux

とりあえずプロセスグループが設定できない問題以外はなんとかなったので /lib/systemd/system/bluetooth.service を変更して起動時に Bluetooth シリアル通信ができるようにしておく。

pi@raspberrypi:~$ diff -u <(zcat /lib/systemd/system/bluetooth.service.gz) /lib/systemd/system/bluetooth.service
--- /dev/fd/63  2017-07-02 14:53:53.958932366 +0900
+++ /lib/systemd/system/bluetooth.service       2017-07-02 13:35:31.721916663 +0900
@@ -5,7 +5,8 @@
 [Service]
 Type=dbus
 BusName=org.bluez
-ExecStart=/usr/lib/bluetooth/bluetoothd
+ExecStart=/usr/lib/bluetooth/bluetoothd -C
+ExecStartPost=/usr/bin/sdptool add SP
 NotifyAccess=main
 #WatchdogSec=10
 #Restart=on-failure
pi@raspberrypi:~$ sudo systemctl daemon-reload
pi@raspberrypi:~$ sudo systemctl restart bluetooth

/etc/systemd/system/rfcomm.service を作成。

[Unit]
Description=RFCOMM service
After=bluetooth.service
Requires=bluetooth.service
 
[Service]
ExecStart=/usr/bin/rfcomm watch 0 1 /sbin/agetty rfcomm0 115200 linux
 
[Install]
WantedBy=multi-user.target
pi@raspberrypi:~$ sudo systemctl daemon-reload
pi@raspberrypi:~$ sudo systemctl start rfcomm
pi@raspberrypi:~$ sudo systemctl enable rfcomm

プロセスグループが設定できない問題はよくわからんが、とりあえず Raspberry Pi にログインしたあとに tmux 起動すればいいっぽいので今日はここまで。(ctrl+a と ctrl+b が…)