ジョイスティックモジュールの使い方:ArduinoでX・Y軸とボタンを読み取る
アナログジョイスティックモジュール(KY-023)はどう動くのか。VRx・VRy・SWピン、Arduinoへの配線、デッドゾーンとキャリブレーション、方向の判定、ジョイスティックでのサーボ制御、ESP32とPicoのコードを解説します。
ジョイスティックモジュールは、ゲームコントローラーのアナログスティックをArduinoで使いやすくしたものです。スティックを左右や前後に動かすと2つの軸で位置を知らせ、スティックを押し込むと中のボタンが反応します。ロボットカーのリモコン、パン・チルトのカメラ台、LEDマトリクスのゲーム、ディスプレイのメニューに、いちばん手軽に使える入力部品です。もっとも一般的なモデルはKY-023とも呼ばれます。

ジョイスティックモジュールはどう動く?
スティックの下には、互いに直角に置かれた10 kΩのポテンショメーターが2つあります。1つはX軸、もう1つはY軸の動きを検知します。各ポテンショメーターは分圧回路として働き、スティックが中央にあるとワイパーは約2.5 Vを出し、analogRead()は約512を読み取ります。スティックを一方の端まで倒すと値は0に近づき、反対側に倒すと1023に近づきます。スティックを押し込むと、小さなボタンがSWピンをGNDにつなぎます。
| スティックの位置 | VRx(X) | VRy(Y) |
|---|---|---|
| 中央(手を離した状態) | 約512 | 約512 |
| いっぱいまで左 | 約0 | 約512 |
| いっぱいまで右 | 約1023 | 約512 |
| いっぱいまで上 | 約512 | 約0 |
| いっぱいまで下 | 約512 | 約1023 |
どの方向で0になり、どの方向で1023になるかは、モジュールの持ち方によって変わります。ピンを手前に向けたときに方向がこの表と違っていても心配いりません。コードで値を1023から引けば、軸を簡単に反転できます。
ジョイスティックのピン配置と配線
| ピン | 機能 | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|---|
| GND | グラウンド | GND | GND | GND |
| +5V | 電源 | 5V | 3V3 | 3V3 |
| VRx | X軸(アナログ) | A0 | GPIO 34 | GP26 |
| VRy | Y軸(アナログ) | A1 | GPIO 35 | GP27 |
| SW | ボタン(押すとGNDにつながる) | D2 | GPIO 25 | GP15 |
Arduinoでジョイスティックを読み取る
下のコードは両方の軸とボタンを読み取り、シリアルモニタに表示します。ボタンは押すとGNDにつながり、ほとんどのモジュールにはプルアップ抵抗がないので、SWピンはINPUT_PULLUPに設定する必要があります。こうすると、離しているときはHIGH、押したときはLOWになります。
1const int X_PIN = A0;
2const int Y_PIN = A1;
3const int SW_PIN = 2;
4
5void setup() {
6 pinMode(SW_PIN, INPUT_PULLUP); // basınca LOW / LOW when pressed
7 Serial.begin(9600);
8}
9
10void loop() {
11 int x = analogRead(X_PIN); // 0–1023, ortada ~512 / ~512 at center
12 int y = analogRead(Y_PIN);
13 bool pressed = digitalRead(SW_PIN) == LOW;
14
15 Serial.print("X: "); Serial.print(x);
16 Serial.print(" Y: "); Serial.print(y);
17 Serial.print(" Buton / button: ");
18 Serial.println(pressed ? "basılı / pressed" : "-");
19 delay(100);
20}デッドゾーンとキャリブレーション
実際には、スティックから手を離しても値はぴったり512にはならず、495や525のような値で数ずつ揺れます。これを直さないと、ジョイスティックに触れていないのにロボットカーが少しずつ前に進んだり、サーボがぴくぴく動いたりします。対策は2段階です。起動時に中央の値を測って保存し(キャリブレーション)、中央に近い小さなずれは無視します(デッドゾーン)。下の関数は、各軸を中央がぴったり0になる-100〜+100のきれいな値に変えます。
1int centerX, centerY;
2const int DEAD_ZONE = 40;
3
4int readAxis(int pin, int center) {
5 int v = analogRead(pin) - center;
6 if (abs(v) < DEAD_ZONE) return 0; // ölü bölge / dead zone
7 if (v > 0) return map(v, DEAD_ZONE, 1023 - center, 0, 100); // sağ ya da aşağı / right or down
8 return map(v, -center, -DEAD_ZONE, -100, 0); // sol ya da yukarı / left or up
9}
10
11void setup() {
12 Serial.begin(9600);
13 centerX = analogRead(A0); // açılışta joystick'e dokunma / don't touch it at startup
14 centerY = analogRead(A1);
15}
16
17void loop() {
18 Serial.print(readAxis(A0, centerX));
19 Serial.print(", ");
20 Serial.println(readAxis(A1, centerY));
21 delay(50);
22}方向を判定する:上、下、左、右
ゲームやメニューでは、正確な位置ではなく方向だけが必要なことがよくあります。値を2つのしきい値と比べれば、ジョイスティックを4方向の十字キーとして使えます。メニューの移動では1回倒すごとに1ステップだけ進んでほしいので、コードでは短い待ち時間を入れるか、スティックが中央に戻るまで新しいステップを無視します。
1void loop() {
2 int x = analogRead(A0);
3 int y = analogRead(A1);
4 if (x < 300) Serial.println("Sol / left");
5 else if (x > 700) Serial.println("Sağ / right");
6 else if (y < 300) Serial.println("Yukarı / up");
7 else if (y > 700) Serial.println("Aşağı / down");
8 delay(150);
9}ジョイスティックでサーボモーターを制御する
ジョイスティックの1つの軸を0〜180度に対応させれば、サーボモーターを手で動かせます。2つの軸と2つのサーボを使えば、カメラを左右と上下に向けられるパン・チルト台になります。サーボの配線と電源の注意点はサーボモーターとは?を参照してください。
1#include <Servo.h>
2
3Servo panServo;
4
5void setup() {
6 panServo.attach(9);
7}
8
9void loop() {
10 int x = analogRead(A0);
11 int angle = map(x, 0, 1023, 0, 180); // joystick → açı / joystick → angle
12 panServo.write(angle);
13 delay(15);
14}ESP32とRaspberry Pi Picoでジョイスティックを使う
ESP32のADCは12ビットで0〜4095の値を返しますが、ADCが直線的ではないため、中央の値は2048ではなく1800〜1950あたりになることがよくあります。そのため、ESP32では上のキャリブレーションの方法がさらに重要になります。Wi-Fiを使う場合は、軸をADC1のピン(GPIO 32〜39)につないでください。Picoのアナログ入力はGP26〜GP28で、read_u16()は0〜65535の値を返します。
1from machine import ADC, Pin
2import time
3
4x_axis = ADC(26) # GP26 → VRx
5y_axis = ADC(27) # GP27 → VRy
6button = Pin(15, Pin.IN, Pin.PULL_UP) # SW, basınca 0 / 0 when pressed
7
8while True:
9 x = x_axis.read_u16() # 0–65535, ortada ~32768 / ~32768 at center
10 y = y_axis.read_u16()
11 print(x, y, "BASILI / PRESSED" if button.value() == 0 else "")
12 time.sleep(0.1)ジョイスティックのプロジェクトアイデア
- ロボットカーのリモコン:ジョイスティックを送信側のボードにつなぎ、nRF24L01やBluetoothで車に命令を送ります。
- パン・チルトカメラ:2つのサーボと1つのジョイスティックで、カメラを好きな方向に向けます。
- LEDマトリクスのスネークゲーム:8x8のマトリクス上のヘビをジョイスティックで操作します。
- ディスプレイのメニュー:上下で選択肢を移動し、ボタンで決定します。ディスプレイ側はI2C LCDディスプレイの使い方を参照してください。
- カラーピッカー:X軸で色を、Y軸で明るさを設定してRGB LEDを操作します。
- パソコンのマウス:Arduino LeonardoやPro Microを使い、ジョイスティックを本物のUSBマウスにします。
よくあるミスと直し方
- 押していないのにボタンが押されたように見える:SWピンがINPUT_PULLUPになっていません。ピンがフローティング状態なので、ランダムな値を読み取ります。
- 止まっているときに値が512にならず揺れる:これは正常です。起動時にキャリブレーションし、デッドゾーンを加えてください。
- 軸が逆、または入れ替わっている:モジュールの向きの問題です。値を1023から引くか、VRxとVRyの線を入れ替えてください。
- ESP32で中央が2048にならない:ESP32のADCは直線的ではありません。起動時に中央を測り、それをもとに計算してください。
- サーボががたつく、またはArduinoがリセットする:サーボがArduinoの5 Vに負担をかけすぎています。サーボは別の5 V電源から給電し、GNDどうしをつないでください。
ジョイスティックは、実は2つのポテンショメーターと1つのボタンにすぎません。ポテンショメーターとは?とArduinoに押しボタンをつなぐ方法を読むと、ここで学んだことがさらに身につきます。BlueGraysアプリには、ジョイスティックでのLEDとサーボの制御、LEDマトリクスのスネークゲームのプロジェクトが回路図とコード付きで載っています。
よくある質問
ジョイスティックモジュールの各ピンは何をしますか?
GNDと+5Vは電源ピンです。VRxとVRyはX軸とY軸の位置をアナログ電圧で出し、アナログピンにつなぎます。SWはスティックを押すとGNDにつながるボタンの出力で、INPUT_PULLUPに設定したデジタルピンにつなぎます。
中央にあるのにジョイスティックの値がぴったり512にならないのはなぜですか?
ポテンショメーターや機械部品は完璧ではないので、止まっているときの値はたいてい490〜530のどこかにあり、数ずつ揺れます。起動時に中央の値を測って保存し、±30〜50のデッドゾーンを使えば解決します。
SWピンがLOWとHIGHの間でランダムに切り替わるのはなぜですか?
ほとんどのジョイスティックモジュールにはボタン用のプルアップ抵抗がないので、ピンは何もつながっていないかのようにフローティング状態になります。pinMode(SW_PIN, INPUT_PULLUP)でArduinoの内部プルアップを有効にすれば、離しているときはHIGH、押したときはLOWになります。
1つのジョイスティックでいくつのサーボを制御できますか?
ジョイスティックには2つの軸があるので、2つのサーボを独立して制御でき、パン・チルトカメラにぴったりです。さらにサーボを増やすなら、2つ目のジョイスティックを追加します。Arduino Unoのアナログピン6本で、ジョイスティック3つまで足ります。
ジョイスティックを無線のリモコンとして使えますか?
はい。ジョイスティックを1台のArduinoにつなぎ、nRF24L01無線モジュール、HC-05 Bluetoothモジュール、または2台のESP32間のESP-NOWで、値を別のボードに送ります。ロボットカーやリモコンのプロジェクトの多くは、この方法で作られています。
ジョイスティックモジュールはESP32やRaspberry Pi Picoで使えますか?
はい。ただし3V3ピンから給電してください。値の範囲はESP32で0〜4095、Picoで0〜65535です。ESP32ではWi-Fiがオンでも動くADC1のピン(GPIO 32〜39)を使い、起動時に中央の値をキャリブレーションしてください。
この記事を読んだ後に作れるプロジェクト
全プロジェクトを見る
ジョイスティックでLEDを制御
ジョイスティックのスティックをいずれかの方向へいっぱいに倒すと、その方向のLEDが点灯します。右、左、上、下にそれぞれ異なる4色のLEDがあります。スティックを離すと中央に戻り、すべてのLEDが消灯します。ロボットカーや画面上のゲームを操作するための第一歩です。

ジョイスティックでサーボモーターを制御
防犯カメラの向きを変えるように、ジョイスティックで2つのモーターを操作します。スティックを左右に倒すと下側のサーボモーターがゆっくり回り、上下に倒すと上側のサーボモーターが上下に傾きます。手を離すと、どちらもその位置で止まります。カメラやレーザーの台座など、2方向に動く装置の基礎を作ります。

ピクセルスネーク:8x8マトリクス
定番のスネークゲームを自分の手で作って遊びましょう。カラフルな8x8のLEDマトリクスで、ジョイスティックを使って緑のヘビを動かし、赤いリンゴを食べます。リンゴを食べるたびにヘビが長くなり、少しずつ速くなって、小さな数字ディスプレイのスコアも増えます。壁や自分のしっぽにぶつかると画面が赤く3回点滅し、ボタンを押すと再び始められます。

マトリクススネーク16x16
定番のスネークゲームを、今度は256個のLEDでできた大きなディスプレイで遊びます。ジョイスティックで緑のヘビを動かし、赤いリンゴを集めます。リンゴを食べるたびにヘビが長くなり、小さな数字ディスプレイのスコアが増え、ゲームも少しずつ速くなります。壁や自分の体にぶつかると画面が赤く点滅し、ボタンを押すと新しいゲームが始まります。

ESP32サーボモーター(ポテンショメーター制御)
このプロジェクトでは、ポテンショメーターでSG90サーボモーターを制御します。ポテンショメーターから読み取ったアナログ値を0~180度の範囲に変換し、サーボの角度を調整します。ESP32Servoライブラリは、ESP32の出力に対応した適切なGPIOでサーボ信号を生成します。このプロジェクトではGPIO 18を使います。ロボットアームやカメラ用ジンバル、自動ドアの機構に使われる基本的な制御方法です。

パドルボールゲーム(SSD1306)
ボール1個とパドル2本で遊ぶ、ビデオゲームの原点の1つである卓球ゲームを、小さなOLEDディスプレイで楽しめます。2つのボタンで右のパドルを上下に動かします。左のパドルはコンピューターがボールを追いながら操作し、ボールを打つたびにブザーが短く鳴ります。先に9点を取ると勝ちです。その後、新しいゲームが自動で始まります。

OLED T-Rexラン:Arduino恐竜ゲーム
インターネットが切れると現れる恐竜ゲームを、机の上で楽しめます。小さなOLEDディスプレイを恐竜が走り、迫るサボテンを1つのボタンでジャンプして避けます。横の4桁ディスプレイには得点がリアルタイムで表示され、100点ごとにブザーがお祝いの音を鳴らします。サボテンにぶつかるとゲーム終了です。ボタンを押すと、もう一度始められます。

Arduino LCDランナーゲーム
16x2文字のLCDディスプレイで動作する横スクロール型のエンドレスランナーゲームです。キャラクターは自動で走り、ボタンを押すとジャンプして障害物を飛び越えます。障害物は上段と下段にランダムに現れます。パッシブブザーで開始時のメロディー、ジャンプ音、50点獲得を祝う音、ゲーム終了時のメロディーを鳴らします。起動時には「BlueGrays — Arduino Projects」の画面を表示します。スコアは画面右上でリアルタイムに更新されます。
ほかの記事
BlueGraysアプリ
この回路をスマホでステップごとに作る
回路図、コード、部品リストを1つの画面で確認できます。Arduino、ESP32、Picoのプロジェクトを200件以上用意しています。

