Arduino Leonardoとジョイスティックでマウスカーソルを動かす

##1. はじめに
[aitendo](http://www.aitendo.com)で[ジョイスティックモジュール [JOY10K-5P]](http://www.aitendo.com/product/10187)を見つけて、これを[Arduino Leonardo](https://www.google.co.jp/search?hl=ja&as_q=arduino+leonardo)に接続してマウスカーソルを動かしてみようと思いました。Arduinoに[Joystick Mouse Control](https://www.arduino.cc/en/Tutorial/JoystickMouseControl)というサンプルコードがありますがジョイスティックの傾きとマウスカーソルの移動量の関係を任意に設定できるようにしたくてプログラムを起こしました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/221758/9385ded8-c00e-8ebf-e716-2100612c8463.png)

##2. 仕組み
- このジョイスティックはX軸、Y軸それぞれに可変抵抗の回転部分が連結されていて、ジョイスティックの傾きに連動して摺動子(英：Wiper、中：[电刷](https://www.google.co.jp/search?hl=ja&as_q=电刷))が動いてVRx、VRyの電圧が変化します。手を離すと自動で中立しVRx、VRyの電圧は電源電圧の1/2になります。
![image.jpg](https://qiita-image-store.s3.amazonaws.com/0/221758/30545de1-1013-d8d6-d475-e10f031c2e70.jpeg)

- VRx、VRyをArduino LeonardoのANALOG IN端子に接続して[A/D変換](https://www.google.co.jp/search?hl=ja&as_q=arduino+A%2FD変換)を行い、読取った値に応じてマウスカーソルを移動させます。

##3. 接続
|ジョイスティック|Arduino Leonardo|
|--------------+----------------|
|GND           |GND|
|+5V           |5V|
|VRx           |ANALOG IN A1|
|VRy           |ANALOG IN A2|
|SW            |NC(接続しない※1)|

※1：このジョイスティックは押下で短絡するタクトスイッチも備えていますが今回は使用しません。

##4. プログラム
- プログラムは以下の3ステップです。

1. VRx、VRyの電圧を[analogRead()](https://www.arduino.cc/reference/jp/language/functions/analog-io/analogread/)で[A/D変換](https://www.google.co.jp/search?hl=ja&as_q=arduino+A%2FD変換)する。
2. A/D変換して得た値からマウスカーソルの移動量を決定する。
3. [Mouse.move()](https://www.arduino.cc/en/Reference/MouseMove/)に移動量をセットして実行する。

- 1と3はmove_mouse_cursor_with_joystick()で処理します。
- 2はX軸、Y軸で共通としget_mouse_cursor_movement_amount_by_joystick()で処理します。
- Mouse.move()実行前に[Mouse.begin()](https://www.arduino.cc/en/Reference/MouseBegin/)を実行してください(例：[setup()](https://www.arduino.cc/reference/en/language/structure/sketch/setup/)の中)。

``` c
/*
 * val    : 0(0x0000)～1023(0x03FF)の整数
 * return : valに応じた移動量
 */
signed char get_mouse_cursor_movement_amount_by_joystick(int val)
{
    signed char amount[16] = { -8, -4, -2, -2, -2, -1, -1,  0,
                                0,  1,  1,  2,  2,  2,  4,  8 };
    unsigned int range = ((unsigned int)val&0x03ff) / 64;
    return amount[range];
}

void move_mouse_cursor_with_joystick(void)
{
    unsigned long response_delay = 10;
    int x_pin = 1;
    int y_pin = 2;
    int x_reading = 0;        /* analogRead()の戻り値を格納する */
    int y_reading = 0;        /* analogRead()の戻り値を格納する */
    signed char x_amount = 0; /* Mouse.move()の xの値を格納する */
    signed char y_amount = 0; /* Mouse.move()の yの値を格納する */

    x_reading = analogRead(x_pin);
    y_reading = analogRead(y_pin);
    x_amount = get_mouse_cursor_movement_amount_by_joystick(x_reading);
    y_amount = get_mouse_cursor_movement_amount_by_joystick(y_reading);
    if( x_amount || y_amount )
    {
        Mouse.move(x_amount, y_amount, 0);
        delay(response_delay);
    }
}
```

###4.1 move_mouse_cursor_with_joystick()
- [analogRead()](https://www.arduino.cc/reference/jp/language/functions/analog-io/analogread/)はANALOG INのピン番号を引数として渡し、戻り値はint型で0-1023です。
- [Mouse.move()](https://www.arduino.cc/en/Reference/MouseMove/)はsigned char型で引数を渡し、戻り値はありません。
- get_mouse_cursor_movement_amount_by_joystick()で得た移動量がX軸、Y軸のいずれかが非ゼロの場合にMouse.move()を実行します。ノーウェイトだとマウスカーソルがすっ飛ぶので[delay()](https://www.arduino.cc/reference/en/language/functions/time/delay/)を入れています。

###4.2 get_mouse_cursor_movement_amount_by_joystick()
- 引数で与えられた値を0以上1023以内に収めるためunsigned intで[キャスト](https://www.google.co.jp/search?hl=ja&as_q=C言語+キャスト)し0x3ffで[ビットマスク](https://www.google.co.jp/search?hl=ja&as_q=ビットマスク)しています。
- その後64で割って16段階に区切り、各段階に対応するマウスカーソルの移動量を返します。
  - 負の数にならないようにあらかじめ処理しているので右へ6[ビットシフト](https://www.google.co.jp/search?hl=ja&as_q=ビットシフト)しても良いのだけど64で割る方が可読性が良いかなと。
  - objdumpで逆アセンブルしたところ64で割るのと6ビット右シフトするのとで差異はありませんでした。
- ジョイスティックの傾きに応じて移動量が変わるようにしています。
  - 傾きと移動量の関係は配列amount[]で任意に設定可能です。
  - 無操作時にマウスカーソルがふらつかないよう移動量がゼロの区間を設けています。

##5. おわりに
- 数百円のジョイスティックと数十行のプログラム追加でArduino Leonardoでマウスカーソルを動かすことができました。
- 可変抵抗の真ん中の端子を英語でWiper、中国語で电刷と呼ぶのは中国メーカーのデータシートを見て知りました。电刷、つまりエレクトリックなブラシってそのまんまですね。