部品 · 読了まで5分

ジョイスティックモジュールの使い方:ArduinoでX・Y軸とボタンを読み取る

アナログジョイスティックモジュール(KY-023)はどう動くのか。VRx・VRy・SWピン、Arduinoへの配線、デッドゾーンとキャリブレーション、方向の判定、ジョイスティックでのサーボ制御、ESP32とPicoのコードを解説します。

ジョイスティックモジュールは、ゲームコントローラーのアナログスティックをArduinoで使いやすくしたものです。スティックを左右や前後に動かすと2つの軸で位置を知らせ、スティックを押し込むと中のボタンが反応します。ロボットカーのリモコン、パン・チルトのカメラ台、LEDマトリクスのゲーム、ディスプレイのメニューに、いちばん手軽に使える入力部品です。もっとも一般的なモデルはKY-023とも呼ばれます。

KY-023アナログジョイスティックモジュール
アナログジョイスティックモジュール:2つの軸のための2つのポテンショメーターと、スティックを押すと反応するボタンが入っています。ピンはGND、+5V、VRx、VRy、SWの5本です。

ジョイスティックモジュールはどう動く?

スティックの下には、互いに直角に置かれた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 UnoESP32Raspberry Pi Pico
GNDグラウンドGNDGNDGND
+5V電源5V3V33V3
VRxX軸(アナログ)A0GPIO 34GP26
VRyY軸(アナログ)A1GPIO 35GP27
SWボタン(押すとGNDにつながる)D2GPIO 25GP15
ESP32とPicoでは、モジュールを5Vではなく3V3ピンから給電してください。ピンに「+5V」と書かれていても、モジュールは3.3 Vで問題なく動きます。5 Vから給電すると、アナログピンに3.3 Vを超える電圧がかかり、ボードが壊れることがあります。ESP32では、34〜39番ピンをSWに使わないでください。これらのピンには内部プルアップ抵抗がありません。

Arduinoでジョイスティックを読み取る

下のコードは両方の軸とボタンを読み取り、シリアルモニタに表示します。ボタンは押すとGNDにつながり、ほとんどのモジュールにはプルアップ抵抗がないので、SWピンはINPUT_PULLUPに設定する必要があります。こうすると、離しているときはHIGH、押したときはLOWになります。

C++ (Arduino IDE)
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}
シリアルプロッタを開くと、両方の軸を同じグラフで見られます。スティックをゆっくり回しながら2本の線の変化を見るのが、各軸の向きを理解するいちばんの近道です。

デッドゾーンとキャリブレーション

実際には、スティックから手を離しても値はぴったり512にはならず、495や525のような値で数ずつ揺れます。これを直さないと、ジョイスティックに触れていないのにロボットカーが少しずつ前に進んだり、サーボがぴくぴく動いたりします。対策は2段階です。起動時に中央の値を測って保存し(キャリブレーション)、中央に近い小さなずれは無視します(デッドゾーン)。下の関数は、各軸を中央がぴったり0になる-100〜+100のきれいな値に変えます。

C++ (Arduino IDE)
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ステップだけ進んでほしいので、コードでは短い待ち時間を入れるか、スティックが中央に戻るまで新しいステップを無視します。

C++ (Arduino IDE)
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つのサーボを使えば、カメラを左右と上下に向けられるパン・チルト台になります。サーボの配線と電源の注意点はサーボモーターとは?を参照してください。

C++ (Arduino IDE)
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の値を返します。

MicroPython
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を制御
#59中級
プレミアム

ジョイスティックでLEDを制御

ジョイスティックのスティックをいずれかの方向へいっぱいに倒すと、その方向のLEDが点灯します。右、左、上、下にそれぞれ異なる4色のLEDがあります。スティックを離すと中央に戻り、すべてのLEDが消灯します。ロボットカーや画面上のゲームを操作するための第一歩です。

4.7(50)
67
ジョイスティックでサーボモーターを制御
#60中級
プレミアム

ジョイスティックでサーボモーターを制御

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

4.7(42)
58
ピクセルスネーク:8x8マトリクス
#88上級
プレミアム

ピクセルスネーク:8x8マトリクス

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

4.7(39)
57
マトリクススネーク16x16
#89上級
プレミアム

マトリクススネーク16x16

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

4.8(52)
67
ESP32サーボモーター(ポテンショメーター制御)
#118中級
プレミアム

ESP32サーボモーター(ポテンショメーター制御)

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

4.3(29)
42
パドルボールゲーム(SSD1306)
#86中級
プレミアム

パドルボールゲーム(SSD1306)

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

4.5(47)
65
OLED T-Rexラン:Arduino恐竜ゲーム
#87上級
プレミアム

OLED T-Rexラン:Arduino恐竜ゲーム

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

4.7(33)
53
Arduino LCDランナーゲーム
#98中級
プレミアム

Arduino LCDランナーゲーム

16x2文字のLCDディスプレイで動作する横スクロール型のエンドレスランナーゲームです。キャラクターは自動で走り、ボタンを押すとジャンプして障害物を飛び越えます。障害物は上段と下段にランダムに現れます。パッシブブザーで開始時のメロディー、ジャンプ音、50点獲得を祝う音、ゲーム終了時のメロディーを鳴らします。起動時には「BlueGrays — Arduino Projects」の画面を表示します。スコアは画面右上でリアルタイムに更新されます。

4.6(32)
55