PCブラウザに接続したUSBゲームパッドを操作する。
ラズパイ側は python の websocket で待ち受け・受信する。
[ゲームパッド]ー[PCブラウザ]ー[Wi-Fi]ー[ラズパイ websocket]
その1.PCブラウザのHTML
ラズパイのwebディレクトリに 新しいファイル[gamepad_websocket.html]を作成する。
<!DOCTYPE html>
<html>
<head><title>Gamepad to WSL</title></head>
<body>
<h2>Gamepad Input for Node-RED</h2>
<pre id="output"></pre>
<script>
let socket = new WebSocket("ws://razpi43.local:8765/gamepad");
let lastData = null; // 前回送信したデータを保持
function pollGamepad() {
const gp = navigator.getGamepads()[0];
if (gp) {
const data = {
buttons: gp.buttons.map(b => b.pressed),
axes: gp.axes
};
const jsonData = JSON.stringify(data);
document.getElementById("output").textContent = jsonData;
// データが変化している場合のみ送信
if (socket.readyState === WebSocket.OPEN && jsonData !== lastData) {
socket.send(jsonData);
lastData = jsonData;
}
}
requestAnimationFrame(pollGamepad);
}
window.addEventListener("gamepadconnected", () => {
console.log("Gamepad connected");
pollGamepad();
});
</script>
</body>
</html>
その2.ラズパイ側のpythonコード
ラズパイ側では websocket 待ち受けし、PCブラウザからのwebsocket送信を待つ新しいファイル[gamepad_receive.py]を作成する。
import asyncio
import websockets
import json
# デッドゾーン(微小な揺れを無視)
DEADZONE = 0.2
def get_left_stick_direction(axes):
x = axes[0] if len(axes) > 0 else 0
y = axes[1] if len(axes) > 1 else 0
direction = []
if abs(x) > DEADZONE:
direction.append("← 左" if x < 0 else "→ 右")
if abs(y) > DEADZONE:
direction.append("↑ 上" if y < 0 else "↓ 下")
return direction if direction else ["ニュートラル"]
async def handler(websocket):
async for message in websocket:
try:
data = json.loads(message)
axes = data.get("axes", [])
direction = get_left_stick_direction(axes)
print(f"方向: {', '.join(direction)}")
except Exception as e:
print(f"⚠️ エラー: {e}")
async def main():
print("🎧 WebSocketサーバー起動中(ポート8765)...")
async with websockets.serve(handler, "0.0.0.0", 8765):
await asyncio.Future() # 永久待機
asyncio.run(main())
その3.接続検査する
まずラズパイ側で websocket 待ち受け[$ python gamepad_receive.py]を実行する。
PCブラウザではUSBゲームパッドを接続したら、[http://razpi**.local/gamepad_websocket.html]にアクセスする。
USBゲームパッドの左スティックを操作すると:
PC側ブラウザの画面表示は
Gamepad Input for Node-RED
{“buttons”:[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],”axes”:[0.000015259021896696368,-0.000015259021896696368,0.000015259021896696368,-0.000015259021896696368]}
また DevTools のコンソールには下図 ” Gamepad Connected ” が表示される

ラズパイのターミナル表示は下記:
🎧 WebSocketサーバー起動中(ポート8765)...
方向: → 右
方向: ニュートラル
方向: → 右
方向: ニュートラル
方向: ← 左
方向: ニュートラル
方向: ニュートラル
以上。そしてもっとゲームパッドのキーを有効にするには:
ラズパイ側 python コードをゲームパッド全ボタン有効化
ラズパイ側の新しいファイル[gamepad_receive2.py]を作成する。
import asyncio
import websockets
import json
DEADZONE = 0.2
BUTTON_NAMES = {
0: "A", 1: "B", 2: "X", 3: "Y",
4: "LB", 5: "RB", 6: "LT", 7: "RT",
8: "バック", 9: "スタート", 10: "左スティック押し込み",
11: "右スティック押し込み", 12: "十字 ↑", 13: "十字 ↓",
14: "十字 ←", 15: "十字 →", 16: "ガイド"
}
def get_stick_direction(name, x, y):
direction = []
if abs(x) > DEADZONE:
direction.append(f"{name} ← 左" if x < 0 else f"{name} → 右")
if abs(y) > DEADZONE:
direction.append(f"{name} ↑ 上" if y < 0 else f"{name} ↓ 下")
return direction if direction else [f"{name} ニュートラル"]
def get_pressed_buttons(buttons):
return [
BUTTON_NAMES.get(i, f"ボタン{i}")
for i, pressed in enumerate(buttons)
if pressed
] or ["なし"]
async def handler(websocket):
async for message in websocket:
try:
data = json.loads(message)
axes = data.get("axes", [])
buttons = data.get("buttons", [])
# 左スティック(axes[0], axes[1])
lx = axes[0] if len(axes) > 0 else 0
ly = axes[1] if len(axes) > 1 else 0
left_direction = get_stick_direction("左スティック", lx, ly)
# 右スティック(axes[2], axes[3])
rx = axes[2] if len(axes) > 2 else 0
ry = axes[3] if len(axes) > 3 else 0
right_direction = get_stick_direction("右スティック", rx, ry)
pressed = get_pressed_buttons(buttons)
print(f"左スティック: {', '.join(left_direction)}")
print(f"右スティック: {', '.join(right_direction)}")
print(f"ボタン: {', '.join(pressed)}")
except Exception as e:
print(f"⚠️ エラー: {e}")
async def main():
print("🎮 WebSocketサーバー起動中(ポート8765)...")
async with websockets.serve(handler, "0.0.0.0", 8765):
await asyncio.Future()
asyncio.run(main())
検査結果
🎮 WebSocketサーバー起動中(ポート8765)...
右スティック: 右スティック ↑ 上
右スティック: 右スティック ← 左, 右スティック ↑ 上
右スティック: 右スティック ← 左, 右スティック ↑ 上
右スティック: 右スティック ← 左, 右スティック ↑ 上
右スティック: 右スティック ← 左, 右スティック ↑ 上
右スティック: 右スティック ↑ 上
ボタン: B
ボタン: X
ボタン: RT
ボタン: LT, RT
ボタン: LT
ボタン: LB
ボタン: RB
右スティック: 右スティック ↑ 上
右スティック: 右スティック ← 左, 右スティック ↑ 上
右スティック: 右スティック ← 左, 右スティック ↑ 上
右スティック: 右スティック ← 左, 右スティック ↑ 上
以上