Xbox のコントローラーを Raspberry Pi と Bluetooth 接続して Python から使う方法をまとめます。Python 実行時は pygame を使用しますが GUI は使用せずに実行可能です。本プログラム内では十字キーと B ボタンにのみ反応するようになっています。
まず Xbox のコントローラーを ペアリングモード にして Raspberry Pi の Bluetooth のアイコンをクリックし Add Device... をクリックします。
Xbox Wireless Controller と表示されるので選択して Piar ボタンを押します。Raspberry Pi と Xbox コントローラーが Bluetooth で無事接続されると Connection successfully と表示されるので OK ボタンを押します。
再度 Bluetooth アイコンをクリックすると Xbox Wireless Controller の左にチェックマークが表示され現在接続中であることが確認できます。
CLI で接続されていることを確認するには bluetoothctl コマンドを使います。Bluetooth 接続自体も CLI で行いたい場合は ここ や ここ などを参照してください。因みに後者では xboxdrv や joystick のインストールを行っていますが Raspberry Pi OS (bullseye) を使用し且つ Python の pygame から使用する範囲内では特にインストールせずに動作するようです。
$ bluetoothctl paired-devices
Device C8:3F:26:60:22:6A Xbox Wireless Controller
次に pygame がインストールされているか確認します。以下のコマンドの実行結果から該当の環境では pygame 1.9.6 がインストールされていることが確認できます。インストール方法は 本家 などを参照してください。
$ python3 -c "import pygame; print(pygame.version.ver)"
pygame 1.9.6
Hello from the pygame community. https://www.pygame.org/contribute.html
1.9.6
pygame を使用した Python の例は以下のようになります。
import sys, os
os.environ["SDL_VIDEODRIVER"] = "dummy"
os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide"
import pygame
def joy_event(joy):
hat = joy.get_hat(0) # Get status of cross key
if hat[0] == 0 and hat[1] == 1: # Up key pressed
print("up")
elif hat[0] == 0 and hat[1] == -1: # Down key pressed
print("down")
elif hat[0] == -1 and hat[1] == 0: # Left key pressed
print("left")
elif hat[0] == 1 and hat[1] == 0: # Right key pressed
print("right")
def main():
pygame.init()
try:
joy = pygame.joystick.Joystick(0)
joy.init()
print('Joystick Name: ', joy.get_name())
print('Number of buttons: ', joy.get_numbuttons())
except pygame.error:
print('Joystick 0 is not connected.')
sys.exit(1)
try:
while True:
event = pygame.event.wait() # Wait until getting event from queue
btn_b = joy.get_button(1) # B button to exit
if btn_b == 1:
break
joy_event(joy)
#print(event)
except KeyboardInterrupt:
print("Program interrupted by user")
joy.quit()
pygame.quit()
print("Exit")
return
if __name__=="__main__":
main()
プログラムを実行するとまずコントローラーの名前とボタンの数が表示されます。続いて Xbox コントローラーの十字キーを押すとそれに応じた出力がターミナル上に表示されます。B ボタンを押すとプログラムが終了します。
$ python3 ./test.py
Joystick Name: Xbox Wireless Controller
Number of buttons: 15
up
left
right
down
Exit
pygame.event.wait() でイベント (ここではコントローラのボタンが押される) を待ちイベントが発生した場合 joy_event() を呼び出しています。joy_event() 内で joy.get_hat(0) を呼び出し十字キーのステータスを取得しています。joy.get_hat() は 2 つの要素を持つタプルを返し例えば上が押された場合は (0, 1)、なにも押されていない場合は (0, 0) を返します。
print(event) をコメントから外すと以下のようにキーに対応した番号が取得できます。例えば A を押した際に以下のような出力となり 0 が対応していることがわかります。
<Event(10-JoyButtonDown {'joy': 0, 'button': 0})>
<Event(11-JoyButtonUp {'joy': 0, 'button': 0})>
アナログスティックは joy.get_axis() を呼び出すと x 軸方向や y 軸方向などの傾きに対応した値を -1.0 から 1.0 の範囲で得られるようです。詳しくは ここ などを参照してください。例はいろいろと web に転がっていますが最近では ChatGPT 先生 に聞くのが良いかもしれません。。
<Event(7-JoyAxisMotion {'joy': 0, 'axis': 0, 'value': 0.9060335093234047})>
一点要調査なのは Ctrl-C で以下の個所が思った通りに動かないことです。。pygame.event.wait() 内で待ってるからだと思いますが ChatGPT 先生に聞いても今のところ答えは分かりませんでした。。なのでプログラムは B ボタンを押して終了する必要があります。
except KeyboardInterrupt:
print("Program interrupted by user")
アナログスティックの傾きの値を得る場合はこんな感じ。
def joy_event(joy):
du = joy.get_axis(0) # Joy stick Down(+1.0)/Up(-1.0)
rl = joy.get_axis(1) # Joy stick Right(+1.0)/Left(-1.0)
print("D(+1.0)/U(-1.0): " + str(du) + ", R(+1.0)/L(-1.0): " + str(rl))