複数のボタンや方向キーの情報を同時に送るには、「データをカンマ(,)で区切った1つの文字列」にして送信し、Arduino側でそれを分解して読み取る方法(CSV形式)が確実で簡単です。
例えば、方向キーのX軸、Y軸、ボタン0〜3の状態を 1,0,1,0,0,0\n のようなテキストデータとして送受信します。
以下に、方向キー(D-pad)とボタン0〜3(A, B, X, Yなど)をPCから送信し、Arduinoで受信して各変数に格納するプログラムを作成しました。
Arduino側のプログラム(C++)
送られてきたカンマ区切りの文字列を読み取り、sscanf という関数を使って6つの変数(X軸、Y軸、ボタン4つ)に切り分けます。
// PCからカンマ区切りのデータを受信し、各ボタン・キーの状態を読み取るプログラム
void setup() {
Serial.begin(9600);
// 文字列の読み取り待ち時間(タイムアウト)を短くして動作の遅れを防ぐ
Serial.setTimeout(20);
}
void loop() {
// PCからデータが届いているか確認
if (Serial.available() > 0) {
// 改行コード ('\n') が来るまで文字列として読み取る
String msg = Serial.readStringUntil('\n');
// 読み取ったデータを入れる変数
int dpad_x = 0;
int dpad_y = 0;
int btn0 = 0;
int btn1 = 0;
int btn2 = 0;
int btn3 = 0;
// sscanfを使って、カンマ区切りの文字列から6つの整数(%d)を抽出する
// うまく6つのデータが取り出せた場合のみ処理を実行
if (sscanf(msg.c_str(), "%d,%d,%d,%d,%d,%d", &dpad_x, &dpad_y, &btn0, &btn1, &btn2, &btn3) == 6) {
// ==========================================
// ここにモーターやLEDを動かす処理を書きます
// ==========================================
// 【テスト用】受信したデータをPCに送り返す(Python側で表示させるため)
Serial.print("受信成功 -> ");
Serial.print("方向X:"); Serial.print(dpad_x);
Serial.print(" 方向Y:"); Serial.print(dpad_y);
Serial.print(" B0:"); Serial.print(btn0);
Serial.print(" B1:"); Serial.print(btn1);
Serial.print(" B2:"); Serial.print(btn2);
Serial.print(" B3:"); Serial.println(btn3);
}
}
}
PC側のプログラム(Python)
Pygameでは、一般的な十字の方向キーは「ハットスイッチ(Hat)」として認識され、(X軸, Y軸) のタプル形式(-1, 0, 1 のいずれか)で取得できます。これをボタンの情報と合体させて送信します。
import pygame
import serial
import time
# --- 環境に合わせて書き換える設定 ---
COM_PORT = 'COM3' # Mac/Linuxの場合は '/dev/tty.usbserial-XXXX' など
BAUD_RATE = 9600
# シリアル通信の初期化
try:
ser = serial.Serial(COM_PORT, BAUD_RATE, timeout=0.1)
time.sleep(2) # Arduinoの再起動待ち
print("Arduinoと接続しました!")
except serial.SerialException:
print(f"エラー: {COM_PORT} が開けません。")
exit()
# Pygameの初期化
pygame.init()
pygame.joystick.init()
if pygame.joystick.get_count() == 0:
print("ゲームパッドが接続されていません。")
ser.close()
exit()
joystick = pygame.joystick.Joystick(0)
joystick.init()
print(f"「{joystick.get_name()}」を使用します。")
print("方向キーとボタン0〜3を操作してください。ターミナルにArduinoからの返信が表示されます。")
print("終了するには Ctrl+C を押してください。")
try:
while True:
pygame.event.pump() # ゲームパッドの状態を更新
# 方向キー(ハットスイッチ)の状態を取得
# hat_data は (X, Y) の形式。左が-1、右が1、上が1、下が-1(※コントローラーによる)
if joystick.get_numhats() > 0:
hat_data = joystick.get_hat(0)
dpad_x = hat_data[0]
dpad_y = hat_data[1]
else:
dpad_x, dpad_y = 0, 0 # ハットスイッチが無い場合
# ボタン0〜3の状態を取得 (押していれば 1, 離していれば 0)
btn0 = joystick.get_button(0)
btn1 = joystick.get_button(1)
btn2 = joystick.get_button(2)
btn3 = joystick.get_button(3)
# データをカンマ区切りの文字列にする(最後に改行 \n を入れる)
# 例: "1,0,1,0,0,0\n"
send_str = f"{dpad_x},{dpad_y},{btn0},{btn1},{btn2},{btn3}\n"
# Arduinoへ送信(バイト列に変換して送る)
ser.write(send_str.encode('utf-8'))
# 【デバッグ用】Arduinoから送り返されてきた文字があれば画面に表示する
while ser.in_waiting > 0:
response = ser.readline().decode('utf-8', errors='replace').strip()
print(response)
# データの送りすぎを防ぐため、少し待機 (0.05秒 = 秒間20回送信)
time.sleep(0.05)
except KeyboardInterrupt:
print("\nプログラムを終了します。")
finally:
ser.close()
pygame.quit()
動作確認のポイント
Pythonのプログラムを動かしている間は、Arduino IDEの「シリアルモニタ」は開けません(通信ポートの取り合いになるため)。
そのため、この構成ではArduino側で Serial.print した内容を、Python側が読み取ってターミナル(黒い画面)に表示する仕組みにしてあります。方向キーやボタンを押して、ターミナルに 受信成功 -> 方向X:1 方向Y:0 B0:1... のように表示されれば、Arduinoまでデータが完璧に届いています。