← ラジコンカー概要に戻る Mini 02 HW 完了

ミニ②:ホール効果ジョイスティック入力

データシートのない謎のジョイスティックに挑む、ラジコンカー製作の第2章。 テスター探偵から配線・プログラミングまで、Trial & Error をそのまま記録していきます。

2026年6月 | PlatformIO / M5Unified

Step 0: 構想と遭遇、テスター探偵

ホールセンサーを使った、JoystickなるものをECサイトで発見。PS5, Switchの交換部品?? どうせJoycon作るのならば、よくある抵抗分圧センサより、ホールセンサーなるもの攻略してみることにした。

3ピンなので、V, S, Gでしょうと、適当に3.7V電源で、テスター当てました。
向かって、左から G, S, V でした。

Chocofon(B):
抵抗センサーと違って、当然、極性がありました。
電源と GND を逆につないだ瞬間、天国に召されました。
→ 機能不全となりましたので、分解してみたのが、下の写真右です。

▼ 今回使用したパーツ と JoyStick(ホールセンサー)分解写真

今回使用したパーツとJoyStick分解写真

Step 1: JoyStick(ホールセンサー)モジュール作成

基板は自作。抵抗分圧センサータイプと合わせる形で、入出力ピンを配置。

Chocofon(B):
加工作業中に手袋しなかったので、出血、、、

▼ JoyStick(ホールセンサー)モジュール

JoyStick(ホールセンサー)モジュール

Step 2: 配線検討

ピン配置が判明したところで、M5StickC Plus2 との結線を検討します。アナログ値(ADC)が読み取れるピンを探し、以下の通りに配線することにしました。

ディスプレイに座標表示させるだけじゃなく、サーボ(FS90)も動かしてみます。

  • x軸: G36
  • y軸: G32
  • push: G33
  • servo sig: G0

▼ 配線図(ミニ②)

配線図(ミニ②)

Step 3: プログラミング(第1段階:素の値を読む)

方針として、いきなり複雑なコードは書きません。まずはノイズ対策などを一切入れず、ジョイスティックから送られてくる「生のデータ」を読み取るシンプルなテストプログラムを作成しました。

💻 テストプログラム(main.cpp)

#include <Arduino.h>
#include <M5Unified.h>

static constexpr int PIN_JOY_X = 36;
static constexpr int PIN_JOY_Y = 32;
static constexpr int PIN_JOY_SW = 33;

void loop() {
  const int x = analogRead(PIN_JOY_X);
  const int y = analogRead(PIN_JOY_Y);
  const int sw = (digitalRead(PIN_JOY_SW) == LOW) ? 1 : 0;

  Serial.printf("X:%d, Y:%d, SW:%d\n", x, y, sw);
  delay(100);
}

Step 4: 実装テストで Trial & Error

さあ、M5StickC Plus に書き込んでみます!ジョイスティックを倒すと、シリアルモニタの数値はどう変わるでしょうか?

Chocofon(B):
無事に値が取れました!…が、シリアルプロッタで波形を見てみると課題が山積みです。
・触っていないのに値が常にプルプル波打っている(ノイズ)
・中心値が理想の「2048」ではなく、X は 1870 付近、Y は 1950 付近にズレている。
これぞアナログセンサー。そのままサーボに繋いだら、常にサーボがジリジリと震え続けてしまいますね。

▼ シリアルプロッタで見た「素の」アナログ値

シリアルプロッタのノイズ波形

Step 5: 改善版プログラム(ノイズ撃退&サーボ連動)

シリアルプロッタで可視化された「中心値のズレ」と「ノイズ」という2つの課題。これらをソフトウェアの力でねじ伏せ、サーボを綺麗に動かす第2段階のプログラムを実装しました!

💻 改善版のキーコード抜粋

// 1. 起動時キャリブレーション(中心値の自動補正)
void calibrateCenter() {
  long sumX = 0, sumY = 0;
  for (int i = 0; i < CAL_SAMPLES; ++i) {
    sumX += analogRead(PIN_JOY_X);
    sumY += analogRead(PIN_JOY_Y);
    delay(20);
  }
  centerX = sumX / CAL_SAMPLES;
  centerY = sumY / CAL_SAMPLES;
}

// 2. デッドゾーン(遊び)の設定
int applyDeadzone(int raw, int center) {
  if (abs(raw - center) <= DEADZONE) { // DEADZONE = 200
    return center;
  }
  return raw;
}

// 3. サーボモーターへの変換(map関数)
int xToServoUs(int correctedX) {
  const int maxTravel = min(centerX, 4095 - centerX);
  const int delta = constrain(correctedX - centerX, -maxTravel, maxTravel);
  return map(delta, -maxTravel, maxTravel, 500, 2500);
}

💡 3つの改善ポイント

  • キャリブレーション: 起動時に手を離した状態の値を10回測って平均化し、そこを「真の中心」として記憶させます。これで個体差もクリア!
  • デッドゾーン: 中心から「±200」の細かいブレは「動いていない(中心のまま)」とみなすことで、サーボのブルブル(微振動・ハム音)を防ぎます。
  • サーボ連動: 補正された綺麗なX軸の値を、サーボのパルス幅(500〜2500μs)に map() 関数で変換。スティックの動きにピタッと追従させます。
Chocofon(B):
すごいよ!波形がとてもスティーディ(一直線)になった!!サーボの微振動もピタッと止まりました。
でも、X と Y のセンターのズレが大きい…(X=1871, Y=1955)。
AIチューター:
これは安いアナログパーツによくある個体差です。起動時のキャリブレーションが 「今の中心」を学習するので、ズレていても制御上は問題ありません。ソフトウェアの力、恐るべし…!

▼ デッドゾーンとキャリブレーション適用後の「美しい一直線」

シリアルプロッタの安定した波形

Step 6: 思わぬ罠!サーボが動かない(G0ピンの反抗)

完璧なアナログ値が取れるようになったので、いざサーボモーターを接続して動かそうとしたところ… ピクリとも動かないという事件が発生!

テスターでサーボの信号線(今回は G0 ピンに接続)を測ってみると、PWM(パルス)信号が出ているはずなのに、電圧が 3.3V に固定(HIGHのまま張り付き)になっていました。

Chocofon(B):
プログラムは合っているはずなのに動かない。この「なぜだ!?」という瞬間がたまりません(笑)。
AIチューター:
G0(GPIO0) は ESP32 のブートストラップピンです。 ESP32Servo ライブラリでは PWM がうまく出ないことがあります。 解決策は LEDC で 50Hz のパルスを直接生成すること。起動時に gpio_reset_pin も忘れずに。

💻 解決策:LEDCで直接PWMを叩き出す!

ライブラリがダメなら、ESP32 に内蔵されているハードウェアPWM機能「LEDC」を使って、直接 50Hz のパルスを作り出す作戦に変更しました。

// ESP32Servoライブラリを使わず、内蔵のLEDCで直接50HzのPWMを生成する
static constexpr int SERVO_LEDC_CHANNEL = 5;
static constexpr int SERVO_FREQ_HZ = 50;
static constexpr int SERVO_RESOLUTION_BITS = 14;

void setupServoPwm() {
  gpio_reset_pin(static_cast<gpio_num_t>(PIN_SERVO)); // ピンを一度リセット!
  ledcSetup(SERVO_LEDC_CHANNEL, SERVO_FREQ_HZ, SERVO_RESOLUTION_BITS);
  ledcAttachPin(PIN_SERVO, SERVO_LEDC_CHANNEL);
}
Chocofon(B):
コードを書き換えて再書き込み。起動直後に「ウィーン、ウィーン」とスイープテストが動いた瞬間は感動しました!
ジョイスティックを倒すと、サーボが指の動きに吸い付くように滑らかに追従します。アナログ入力と物理モーターが繋がった、最高の瞬間です!
管理人:
ミニ② HW 完了。Hub 公開・動画掲載 GO。次はミニ③ ESP-NOW — フェーズ1 作戦会議から。

動画

ジョイスティックとサーボの完璧な連動(YouTube)

まとめ

ミニ②では、データシートのないホール効果ジョイスティックを M5StickC Plus に接続し、 X/Y/Push の ADC 読み取りから FS90 サーボ連動まで一気通貫で突破しました。

  • 配線 v4(X=G36 / Y=G32 / Push=G33)+ GROVE 端子の活用
  • 中央値キャリブレーション + デッドゾーンで入力を安定化
  • G0 サーボは LEDC 直接 PWM で制御(ESP32Servo は使わない)

Trial & Error ログ

次のステップ

前: ミニ① モータ & サーボ | 次: ミニ③ ESP-NOW 無線プロジェクト概要

ジョイスティックの裏側を知る

この部品の中で磁石とセンサーがどうやってアナログ値を作っているのか?
ADC(アナログ・デジタル変換)の基礎を図解で解説しています。

マイ・メイカーズ書庫:ホールセンサー編へ