M5StickC(ESP32)による「ELEGOO Arduino用UNO R3スターターキット」を利用したArduino入門 その10 アナログジョイスティックモジュール

現時点の情報です。最新情報はM5StickC非公式日本語リファレンスを確認してみてください。

概要

前回は温湿度センサーでした。今回はアナログジョイスティックになります。

アナログジョイスティックとは?

秋月さんだと上記のような商品になります。PS2以降に搭載されているジョイスティックなどと同じような操作になります。スティックを左右、上下に動かすことができて、指を離すと中央に戻ります。スティック自体を押し込むことでボタンとしても操作可能です。

構造的には可変抵抗がX軸とY軸で2つはいっており、動かすことで抵抗値がかわり、結果として信号ピンに電圧が変化して測定可能です。スティックの押し込みに関しては、押し込むことでスイッチ用の信号線がGNDと接触して、信号が落ちます。そのためスティックの押し込みボタンを使う場合にはプルアップされている必要があります。

接続方法

図で使っているジョイスティックは、キットに付属していたのとピン配置が違っているので注意してください。

ジョイスティック端子用途M5StickC
GNDGNDGND
+5V基準電圧3V3
VRxX軸電圧GPIO26
VRyY軸電圧GPIO36
SWスイッチGPIO0

気をつける必要があるのがVCCです。ジョイスティックには+5Vと書かれていますが、ジョイスティックを端まで倒した場合に、抵抗が0となりその電圧が信号PINに流れてきます。ESP32は3.3Vで動いているので、接続も3.3Vにする必要があります。5Vを接続すると、ジョイスティック中央の場合2.5Vが信号線に戻ってきますので、中心がずれてしまいます。

スケッチ

void setup() {
  Serial.begin(115200);
  pinMode(26, ANALOG);
  pinMode(36, ANALOG);
  pinMode(0, INPUT); // GPIO0はすでにプルアップ状態、他のPINだとINPUT_PULLUPにする
}
void loop() {
  Serial.printf("X=%d, y=%d, sw=%d\n", analogRead(26), analogRead(36), digitalRead(0));
  delay(100);
}

単純な動作検証用のコードになります。今回スイッチはGPIO0に接続しています。GPIO0は特殊なPINで、基本的に常にプルアップされている端子になります。起動時にLOWの場合にファームウエアの書き込みモードになる端子なので、スイッチを押し込んだままESP32を再起動すると、以下のメッセージが表示されます。

rst:0x1 (POWERON_RESET),boot:0x0 (DOWNLOAD_BOOT(UART0/UART1/SDIO_FEI_FEO_V2))
waiting for download

基本的には使わない方がよい端子なのですが、M5StickCの場合には端子数が少ないので仕方ありません。ただし、起動時にLOWになっていると、正常に起動しなくなるのを覚えておいてください。

GPIO0以外に接続した場合には、プルアップしておく必要があるのでpinModeをINPUT_PULLUPにするのを忘れないでください。もしくは、外部回路でプルアップをする必要があります。

動作させてみる

X=4095, y=4095, sw=1
X=2058, y=4095, sw=1
X=1962, y=4095, sw=1
X=0, y=4095, sw=1
X=0, y=4095, sw=1
X=0, y=3639, sw=1
X=0, y=0, sw=1
X=1186, y=0, sw=1
X=1968, y=0, sw=1
X=4095, y=0, sw=1
X=4095, y=0, sw=1
X=1968, y=1847, sw=1
X=1968, y=1847, sw=1
X=1968, y=1851, sw=1
X=1967, y=1847, sw=1
X=1968, y=1849, sw=1
X=1968, y=1847, sw=0
X=1968, y=1849, sw=0
X=1969, y=1849, sw=1
X=1968, y=1847, sw=1
X=1968, y=1850, sw=1
X=1968, y=1847, sw=1

上記のような出力がシリアル出力に出たと思います。X軸とY軸はセンサーによって方向が違うので注意してください。

X軸とY軸は何もしていないと中央値の2048前後、端っこまで倒すと最小値の0から、最大値の4095まで変化していました。スティックのボタンはいつもは1ですが、押し込むと0になっています。

気をつけないといけないのは、結構中央値などがずれてしまいます。2048を中央と定義して、差分で移動してしまうと常に移動し続けてしまうことになります。

補正版(-8から8までの17段階)

void setup() {
  Serial.begin(115200);
  pinMode(26, ANALOG);
  pinMode(36, ANALOG);
  pinMode(0, INPUT); // GPIO0はすでにプルアップ状態、他のPINだとINPUT_PULLUPにする
}
void loop() {
  int x = analogRead(26);
  int xd = (x - 2048) / 255;
  int y = analogRead(36);
  int yd = (y - 2048) / 255;
  Serial.printf("X=%d(%d), y=%d(%d), sw=%d\n", x, xd, y, yd, digitalRead(0));
  delay(100);
}

-8から8までの移動量に補正をしたスケッチです。中央がそれほどずれていないジョイスティックの場合には中央値は2048で、そこからの差分を少し丸めて取得すればそれっぽい数値になるはずです。

このコードの場合、ほんの少し動かした場合には反応しませんので、微妙な入力で使う場合には、起動時にチュートラルの場合の数値を保存しておき、その値を中央地にしてから補正したほうが正確になります。

ただし、アナログ値なので動かしていなくても結構数値は動くのは、あまり厳密な用途には向いていません。

センター補正スケッチ(-128から128)

int xc;
int yc;
void setup() {
  Serial.begin(115200);
  pinMode(26, ANALOG);
  pinMode(36, ANALOG);
  pinMode(0, INPUT); // GPIO0はすでにプルアップ状態、他のPINだとINPUT_PULLUPにする
  xc = analogRead(26);
  yc = analogRead(36);
}
void loop() {
  int x = analogRead(26);
  int xd = (x - xc) / 14; // 補正によって極値にならない可能性があるので14
  int y = analogRead(36);
  int yd = (y - yc) / 14; 
  // 14で割っているので、極値以上になっているのを極値に修正
  if (xd < -128) {
    xd = -128;
  }
  if (128 < xd) {
    xd = 128;
  }
  if (yd < -128) {
    yd = -128;
  }
  if (128 < yd) {
    yd = 128;
  }
  Serial.printf("X=%4d(%4d), y=%4d(%4d), sw=%d\n", x, xd, y, yd, digitalRead(0));
  delay(100);
}

起動時に取得した数値をセンターとして補正します。本当は複数回取得して平均をとったほうがいいと思います。また、センターがずれるとはじまで移動しても値が中途半端になる可能性があるので、判定の幅も小さくする必要があります。

ある程度はうまく動くと思いますが、アナログでジャンパー接続をしているとノイズなどの影響で変な値を拾ってしまうので、動かしていなくても-2などがでてくることがあります。

その他のセンサー

上記はM5Stack社のジョイスティックユニットです。M5StickのHY2.04P端子(Grove端子)にケーブルで接続することができます。ケーブルで接続する以外にも、直接本体に接続するジョイスティックHATもあります。

このユニットの中身はアナログジョイスティックなのですが、中に制御用ICがはいっていて、I2Cで接続しています。そのため、複数のI2Cユニットを接続することも可能です。

原理的には同じなのですが、基板で配線されているのでノイズの影響を受けにくい分ユニットのほうが精度は高いかもしれません。

まとめ

ジョイスティックは個体差があるので、すこし緩めの制御にしたほうが安全だと思います。厳し目の設定にすると、移動していないのに動いたり、端に移動しているつもりなのに移動できていなかったりと問題が発生してしまいます。

続編

コメント

  1. けん より:

    いつも参考にさせていただいています。ありがとうございます。
    一つ教えていただきたいのですが、
    センター補正スケッチのところで、差分を割るのは14であるのはなぜでしょうか?
    2048を128にしようとすると16で割るのかな、と思いまして、
    int xd = (x – xc) / 14;

    int xd = (x – xc) / 16;
    のような感じを考えていたので。。
    よろしかったらお教え願えませんでしょうか。

    • たなかまさゆき より:

      センターを補正すると、最大まで倒しても-128などにならない場合があります
      そのため、若干大きめの数字がでるように計算するために14で割っています
      そして、範囲外に出たときに最大値にする補正も必要になっています

      この辺はどこまで補正するかは難しいところです、、、
      このへん本文で書いていなかったので補足しておきたいと思います

      • 匿名 より:

        教えていただきありがとうございます。
        14が決まった値ではなく、個体差である程度中央値が理論値からずれてしまうときの補正なので14あたり、なのですね。(16で割ると確かに中央値ちょうどですね、、)
        とても参考になりました。今後とも応援しています。どうぞ引き続き、教えてください、よろしくおねがいします!!

        • たなかまさゆき より:

          気になったことがあったらお気軽に質問おねがいます!
          補正はいろいろ調整が必要で、起動時にニュートラルじゃない場合などはおかしな場所が中心になったりします
          ある程度補正の幅を制限するか、補正をしないで絶対値で計算をしたほうが安全だとは思います