【RaspberryPi】Minecraft Pi Editionのコントローラーとしてmicro:bitを使えないか考えてみる【前編】

先日、Minecraft Java版Minecraft 統合版(Windows10)を購入したのですが、サバイバルモードではゾンビたちにズタボロにされて1日の命もなかった自分ですが、ようやくポイントがわかってきたので、数日過ごせる(というかほぼ死ななくなった)状況になりました。ただ、クリア(?)まではあまりにも長い道のりになってきたので、そろそろ目線を変えたいなあ(オープンワールド系はあんまり集中力が続かない…)と思い始めました。

uepon.hatenadiary.com

ってことで、折角だしプログラミングできないかなと思うようになりました。できればブロック言語ではなく、テキストベースでできると嬉しい感じです。電子工作的な要素もあるとさらにうれしい。

ネットで検索すると、RaspberryPi版Minecraftmcpi(pythonライブラリ)を使うとプログラミングができるということだったので、それを更に拡張して物理的な動きをMinecraftに持ってこれないかなと思いました。コントローラーに関してはいろいろありますが、子どもたちに自慢したいのでmicro:bitを使うことにします。

個人的には下にあるGameControllerizerの方が楽しいのではないかと思いますがチャレンジの意味を込めて、まずはmicro:bitで試してみようと思います(チャレンジは成功するとはいっていない)。

www.switch-science.com

図にするとこんな感じになるかなと思います。

f:id:ueponx:20200310131222p:plain

このなかで今回行うのは以下の部分になります。

  • micro:bitBluetoothシリアル通信処理作成
  • Minecraft Pi Editionのインストール
  • RaspberryPiBluetoothシリアル通信処理作成(ライブラリインストール含む)
  • micro:bitRaspberryPiBluetoothシリアル通信テスト

ではやってみます!

今回のエントリーは以下を参考にしています。

qiita.com

qiita.com

micro:bit側の事前設定とプログラミング

今回のmicro:bit側のプログラムはmicro:bitの持っているボタンやセンサーが反応(トリガ)したらBluetooth UARTで文字列を送信するというものになります。 機能をまとめると以下のようになります。

  • Bluetooth UARTが準備できたらLEDにスマイルを表示する
  • Bluetooth接続が成功したらLEDにハートを表示する
  • Bluetooth接続が失敗したらLEDに×を表示する
  • Aボタンを押したら文字列"A"を送信する
  • Bボタンを押したら文字列"B"を送信する
  • micro:bit振ったら文字列"S"を送信する
  • Bluetoothシリアルでデータを受信したら、受信文字列をLED表示する

まずは、ブロックプログラミングを行うのでmakecodeにアクセスします。

makecode.microbit.org

makecodeにアクセスしたら、【新しいプロジェクト】をクリックします。

f:id:ueponx:20200309221434p:plain

すると新しいプロジェクトが作成されます。

f:id:ueponx:20200309222142p:plain

デフォルトのブロックエリアにはBluetoothのブロックは含まれていないので、【高度なブロック】をクリックしメニューを展開し

f:id:ueponx:20200309222433p:plain

更に展開された中から、【拡張機能】をクリックします。

f:id:ueponx:20200309222702p:plain

拡張機能の一覧の表示に変わります。その中から【bluetooth】を選択してクリックします。

f:id:ueponx:20200309223341p:plain

すると、以下のようなダイアログが表示されます。デフォルトでセットされている【radio】ブロックと【bluetooth】ブロックは機能的に同時に使用できないようです。 今回はBluetoothを使用するので、【一部の拡張機能を削除してbluetoothを追加する】をクリックします。

f:id:ueponx:20200309223507p:plain

すると【radio】ブロックが【bluetooth】ブロックに変更されます。

f:id:ueponx:20200309224717p:plain

Bluetoothシリアル(以下Bluetooth UARTと記載)が接続できるようにしておきます。 【bluetooth】のブロックの中から、【Bluetooth接続されたとき】【Bluetoothが切断されたとき】をプログラミングエリアにドロップします。

f:id:ueponx:20200309233457p:plain

f:id:ueponx:20200309233511p:plain

ブロックを取り出すと以下のようになります。

f:id:ueponx:20200309233557p:plain

接続状態をmicro:bitのLEDで表示させておきます。【基本】のブロックから【アイコンを表示】のブロックを選択し

f:id:ueponx:20200309233653p:plain

接続・切断の両方にいれて、それぞれ【ハート】と【×】にしておきます。

f:id:ueponx:20200309233742p:plain

あとは、Bluetoothで使用する機能を設定します。使用するのはBluetooth UARTの機能なので、【bluetooth】ブロッククリックし、 その下に現れる【その他】をクリックし、【Bluetooth UARTサービス】をプログラミングエリアにドロップして

f:id:ueponx:20200309233803p:plain

【最初だけ】のブロックの中に入れます。

f:id:ueponx:20200309233903p:plain

【最初だけ】のブロックの処理が終わったら【アイコンを表示】のブロックを使ってLEDで【スマイル】マークを表示するようにします。

f:id:ueponx:20200310090717p:plain

初期設定ができたので、あとは各種イベント(センサーの検出)が発生したらBluetooth UARTで文字列を送信する処理を作っていきます。 【入力】グループから【ボタン〇〇が押されたとき】と【ゆさぶられたの時】を選択してプログラミングエリアにドロップします。

f:id:ueponx:20200310091134p:plain

あとは【Bluetooth UART 文字列を書き出す()】をブロック追加して、

f:id:ueponx:20200310091512p:plain

以下のようにブロックを配置し、micro:bitのAボタンを押したら文字列”A"を、Bボタンを押したら文字列”B”を、micro:bitを揺さぶったら文字列”S”を送信するように変更をしていきます。

f:id:ueponx:20200310091622p:plain

あとはエコーバックの文字列データ受信したらLEDに表示する処理を追加します。 以下の3つのブロックを使用して

f:id:ueponx:20200310092217p:plain

f:id:ueponx:20200310092223p:plain

f:id:ueponx:20200310092433p:plain

以下のようにブロックを配置してBluetooth UARTデータを受信する処理を作成します。

f:id:ueponx:20200310225543p:plain

ここまで出来上がったら以下のような状態になっていると思います。

f:id:ueponx:20200310225459p:plain

ポイント

実はこのままでは、Bluetoothの接続はうまくできません。プロジェクトの設定からBluetoothの設定を変更する必要があります。 変更方法は以下のようになります。

画面の右上にある【歯車のマーク】をクリックして、プルダウンメニューの中から【プロジェクトの設定】をクリックします。

f:id:ueponx:20200310230523p:plain

すると以下のような画面に遷移します。初期状態では

f:id:ueponx:20200310231050p:plain

JustWorks pairing (default): Pairing is automatic once the pairing is initiated.】のスイッチが有効になっているので、 【No Pairing Required: Anyone can connect via Bluetooth.】を有効にします。(有効にした瞬間にもとの画面に戻ってしまいます) 再度確認すると以下のような画面になっているので問題ありません。

f:id:ueponx:20200310231103p:plain

ダウンロード

最後にこのデータ(HEXファイル)をダウンロードして、micro:bitに書き込みを行います。

まず、micro:bitをUSBケーブルでPCと接続します。すると、micro:bitはUSBストレージとして認識が行われます。 エクスプローラーからも以下の様に認識されます。

f:id:ueponx:20200310012242p:plain

認識が完了したらmakecodeの画面に戻って、プロジェクト名を適切なものに変更し(今回はBTTestとしました)、【ダウンロード】ボタンをクリックします。

f:id:ueponx:20200310232134p:plain

クリックするとそのままダウンロード先を指定するファイルダイアログが表示されるか

f:id:ueponx:20200310084607p:plain

以下のようなダイアログ化が出てくると思います。

f:id:ueponx:20200310084631p:plain

ダウンロードのファイルダイアログが表示されればそのままmicro:bitの認識されたドライブに保存し、後者のダイアログが表示されたら下ある緑色のHEXファイルのボタンをクリックします。どちらを行ってもmicro:bitにHEXファイルがダウンロードされます。

これでmicro:bitBluetoothの通信機能ができあがりました。 では一旦、micro:bitから離れてRaspberryPi側の設定に入っていきます。

念の為ここまでのブロック図とソースを書き出して起きます。

【ブロック図】 f:id:ueponx:20200311000715p:plain

ブロック図を参考に、そのまま同じものを作ってもいいですが、WebUIの上の方にJavaScriptソースを見るとスイッチがあるので、 切り替えると以下のようなプログラム表示されます。これをコピペしてもいいかもしれません。 (HEXファイルをドロップしてもいいのですが、サイズは大きいので、こっちのほうが楽かも)

Javascriptソース】

bluetooth.onBluetoothConnected(function () {
    basic.showIcon(IconNames.Heart)
})
bluetooth.onBluetoothDisconnected(function () {
    basic.showIcon(IconNames.No)
})
input.onButtonPressed(Button.A, function () {
    bluetooth.uartWriteString("A")
})
input.onButtonPressed(Button.B, function () {
    bluetooth.uartWriteString("B")
})
input.onGesture(Gesture.Shake, function () {
    bluetooth.uartWriteString("S")
})
bluetooth.onUartDataReceived(serial.delimiters(Delimiters.NewLine), function () {
    basic.showString(bluetooth.uartReadUntil(serial.delimiters(Delimiters.NewLine)))
})
bluetooth.startUartService()
basic.showIcon(IconNames.Happy)
basic.forever(function () {
    
})

RaspberryPi側の設定

micro:bitの事前設定が終わったのでRaspberryPiからBluetoothバイスとして検出ができるようになりました。 ここからはRaspberryPi側の設定を行っていきます。

作業の手順は2つになります。

  • Minecraft Pi Editionをインストール
  • BluetoothPythonモジュールのインストール

今回使用したのはRaspberryPi 3Bとなります。Raspbian2020-02-13-raspbian-buster.imgのイメージを使用しています。

f:id:ueponx:20200310004133p:plain

Minecraft Pi Editionのインストール

昔はデフォルトでMinecraft Pi Editionがデフォルトでインストールされていたような気がするのですが、 最近はデフォルトではインストールされていないようです。

では、OSの設定の設定は終わっているという前提で、Minecraft Pi Editionをインストールします。 Minecraft Pi EditionX上のプログラムなので、少なくとも起動後にX Window Systemが立ち上がるようにしておきます。

インストールは以下のようにします。

$ sudo apt update
$ sudo apt upgrade
$ sudo apt install minecraft-pi

インストールができたらメニューから起動ができるので起動してみましょう。 メニューのゲームカテゴリに進むと【Maincraft Pi】が追加されているのでそれをクリックします。

f:id:ueponx:20200310010922p:plain

すると、Maincraft Pi EDITIONと表示されたウインドウが開きます。 今後のテストもあるので【Start Game】ボタンをクリックして、

f:id:ueponx:20200310010936p:plain

ウィンドウの下にある、【Create new】ボタンをクリックして、新しいワールドを作成します。

f:id:ueponx:20200310010949p:plain

クリックすると新しいワールドを生成する処理が行われて

f:id:ueponx:20200310011009p:plain

開始することができました。

f:id:ueponx:20200310011023p:plain

ここまでできたらMinecraft Pi Editionのインストール作業は完了です。

bluepyのインストール

bluepyは、Bluetooth Low Energyバイスとの通信を可能にするPythonモジュールで、Linux上で実装されています。(内部的にはBlueZプロジェクトのコードを使用)

bluepy is a Python module which allows communication with Bluetooth Low Energy devices. The current implementation runs on Linux (internally it uses code from the BlueZ project), although it can be ported to other platforms.

こちらをインストールしてmicro:bitBluetooth接続を行っていきます。

ianharvey.github.io

インストールは以下のコマンドを実行します。python2系でも大丈夫ですが、python3系でも実行できるのでpip3を使用します。

$ sudo apt update
$ sudo apt upgrade
$ sudo apt install python3-pip libglib2.0-dev
$ sudo pip3 install bluepy

インストールすると、hcitoolコマンドが使用可能になるので、以下のコマンドを実行してBluetooth機器をスキャンする事ができます。

$ sudo hcitool lescan

上記のように実行すると【Ctrl+C】を押さないと終了しないので、以下のようにtimeoutコマンドと組み合わせることで一定時間で終了させることができます。(以下の場合は5秒でタイムアウト

$ sudo timeout 5s hcitool lescan

【実行例】

$ sudo timeout 5s hcitool lescan
LE Scan ...
(略)
DB:BE:XX:XX:XX:XX BBC micro:bit [tizup]
(略)

上記のようにmicro:bitが検出されます。今回はIDをDB:BE:XX:XX:XX:XXとしていますが、それぞれの環境で違う値になると思いますので この値を覚えておきましょう。[]中の文字列も異なると思います。

RaspberryPi側のプログラム

では、実際にRaspberryPi側のプログラムをドキュメントを参考にして作成していきます。

ianharvey.github.io

lancaster-university.github.io

使用するUUIDは以下の通りです。 ドキュメントのUUIDは”-”が含まれていないのですが、ソースコード側には”-”が含まれているので注意が必要です。

UUID
UART SERVICE 6E400001-B5A3-F393-E0A9-E50E24DCCA9E
UART SERVICE - CHARACTERISTICS(TX) 6E400002-B5A3-F393-E0A9-E50E24DCCA9E
UART SERVICE - CHARACTERISTICS(RX) 6E400003-B5A3-F393-E0A9-E50E24DCCA9E

【BtSerial.py】

from bluepy import btle

class MyDelegate(btle.DefaultDelegate):
  def __init__(self, TX, RX):
    btle.DefaultDelegate.__init__(self)
    self.TX = TX
    self.RX = RX

  def handleNotification(self, hd, data):
    print("notification: {},{}".format(hd,data))
    chRX.write("!\n".encode("utf-8"))

# 第一引数はhcitool lescanで得られたIDに変更すること、変更しないとエラーとなります。
per = btle.Peripheral("DB:BE:XX:XX:XX:XX", btle.ADDR_TYPE_RANDOM)

# UART Service
svcUART = per.getServiceByUUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
# TX Characteristics
chTX = svcUART.getCharacteristics("6E400002-B5A3-F393-E0A9-E50E24DCCA9E")[0]
ch_cccd=chTX.getDescriptors(forUUID=0x2902)[0]
ch_cccd.write(b"\x03\x00", False)

# RX Characteristics
chRX = svcUART.getCharacteristics("6E400003-B5A3-F393-E0A9-E50E24DCCA9E")[0]

dg = MyDelegate(chTX, chRX)
per.setDelegate(dg)

chRX.write("start!\n".encode("utf-8"))
while True:
  if per.waitForNotifications(0.1):
    continue

こちらのプログラムを動作させると以下のような結果を得ることができます。

$ python3 BTSerial.py 
notification: 39,b'S'
notification: 39,b'S'
notification: 39,b'A'
notification: 39,b'B'
notification: 39,b'A'

micro:bit側でエコーされたデータを受信し、LEDに文字表示している最中に、データを受信するとエラーが発生します。 実際に使用する場合にはmicro:bit側の文字列などの表示はしないようにした方がいいでしょう。

以下のようなエラーが発生します。

Traceback (most recent call last):
  File "BTSerial.py", line 31, in <module>
    if per.waitForNotifications(0.1):
  File "/usr/local/lib/python3.7/dist-packages/bluepy/btle.py", line 560, in waitForNotifications
    resp = self._getResp(['ntfy','ind'], timeout)
  File "/usr/local/lib/python3.7/dist-packages/bluepy/btle.py", line 407, in _getResp
    resp = self._waitResp(wantType + ['ntfy', 'ind'], timeout)
  File "/usr/local/lib/python3.7/dist-packages/bluepy/btle.py", line 362, in _waitResp
    raise BTLEDisconnectError("Device disconnected", resp)
bluepy.btle.BTLEDisconnectError: Device disconnected

おわりに

ここまででmicro:bitRaspberryPiBluetooth UART通信ができるようになりました。ここからはmcpiを使ってMinecraft Pi Editionと通信を行っていきます。中編に続きます。

/* -----codeの行番号----- */