ESP32－FreeRTOSにて、ジョイスティックのイベントを検出する

# はじめに

ジョイスティックの検出について記載します。<br>

![SampleJsPic_1_4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559938/b156ee6a-6d8e-8d26-b818-b4387ed94b56.jpeg)

# ジョイスティック - HW-504

ジョイスティックは、HW-504を使用します。<br>
HW-504は以下のことができます。

- x軸, y軸の検出
    - analogRead()を使用して、読み込みます
- スイッチのON/OFFの読み取り
    - 今回は使用しません

# ESP32のピンアサイン

ESP32のピンアサインは、下記のようになっています。(DataSheetより抜粋)


|Name  |No.|Type  |Function  |備考  |
|---|---|---|---|---|
| ND	        |   1   |	P   |   Ground                                                                                  | - |
| 3V3	        |   2   |	P   |   Power supply                                                                            | - |
| EN	        |   3   |	I   |   Module-enable signal. Active high.                                                      | - |
| SENSOR_VP	    |   4   |	I   |   GPIO36, ADC1_CH0, RTC_GPIO0                                                             | - |
| SENSOR_VN	    |   5   |	I   |   GPIO39, ADC1_CH3, RTC_GPIO3                                                             | - |
| IO34	        |   6   |	I   |   GPIO34, ADC1_CH6, RTC_GPIO4                                                             | - |
| IO35	        |   7   |	I   |   GPIO35, ADC1_CH7, RTC_GPIO5                                                             | - |
| IO32	        |   8   |	I/O |   GPIO32, XTAL_32K_P (32.768kHz crystal oscillator input), ADC1_CH4, TOUCH9, RTC_GPIO9    | - |
| IO33	        |   9   |	I/O |   GPIO33, XTAL_32K_N (32.768kHz crystal oscillator output), ADC1_CH5, TOUCH8, RTC_GPIO8   | - |
| IO25	        |   10  |	I/O |   GPIO25, DAC_1, ADC2_CH8, RTC_GPIO6, EMAC_RXD0                                           | y軸取得 |
| IO26	        |   11  |	I/O |   GPIO26, DAC_2, ADC2_CH9, RTC_GPIO7, EMAC_RXD1                                           | x軸取得 |
| IO27	        |   12  |	I/O |   GPIO27, ADC2_CH7, TOUCH7, RTC_GPIO17, EMAC_RX_DV                                        | - |
| IO14	        |   13  |	I/O |   GPIO14, ADC2_CH6, TOUCH6, RTC_GPIO16, MTMS, HSPICLK, HS2_CLK, SD_CLK, EMAC_TXD2         | - |
| IO12	        |   14  |	I/O |   GPIO12, ADC2_CH5, TOUCH5, RTC_GPIO15, MTDI, HSPIQ, HS2_DATA2, SD_DATA2, EMAC_TXD3       | - |
| GND	        |   15  |	P   |   Ground                                                                                  | - |
| IO13	        |   16  |	I/O |   GPIO13, ADC2_CH4, TOUCH4, RTC_GPIO14, MTCK, HSPID, HS2_DATA3, SD_DATA3, EMAC_RX_ER      | - |
| SHD/SD2*	    |   17  |	I/O |   GPIO9, SD_DATA2, SPIHD, HS1_DATA2, U1RXD                                                | - |
| SWP/SD3*	    |   18  |	I/O |   GPIO10, SD_DATA3, SPIWP, HS1_DATA3, U1TXD                                               | - |
| SCS/CMD*	    |   19  |	I/O |   GPIO11, SD_CMD, SPICS0, HS1_CMD, U1RTS                                                  | - |
| SCK/CLK*	    |   20  |	I/O |   GPIO6, SD_CLK, SPICLK, HS1_CLK, U1CTS                                                   | - |
| SDO/SD0*	    |   21  |	I/O |   GPIO7, SD_DATA0, SPIQ, HS1_DATA0, U2RTS                                                 | - |
| SDI/SD1*	    |   22  |	I/O |   GPIO8, SD_DATA1, SPID, HS1_DATA1, U2CTS                                                 | - |
| IO15	        |   23  |	I/O |   GPIO15, ADC2_CH3, TOUCH3, MTDO, HSPICS0, RTC_GPIO13, HS2_CMD, SD_CMD, EMAC_RXD3         | - |
| IO2	        |   24  |	I/O |   GPIO2, ADC2_CH2, TOUCH2, RTC_GPIO12, HSPIWP, HS2_DATA0, SD_DATA0                        | - |
| IO0	        |   25  |	I/O |   GPIO0, ADC2_CH1, TOUCH1, RTC_GPIO11, CLK_OUT1, EMAC_TX_CLK                              | - |
| IO4	        |   26  |	I/O |   GPIO4, ADC2_CH0, TOUCH0, RTC_GPIO10, HSPIHD, HS2_DATA1, SD_DATA1, EMAC_TX_ER            | - |
| IO16	        |   27  |	I/O |   GPIO16, HS1_DATA4, U2RXD, EMAC_CLK_OUT                                                  | - |
| IO17	        |   28  |	I/O |   GPIO17, HS1_DATA5, U2TXD, EMAC_CLK_OUT_180                                              | - |
| IO5	        |   29  |	I/O |   GPIO5, VSPICS0, HS1_DATA6, EMAC_RX_CLK                                                  | - |
| IO18	        |   30  |	I/O |   GPIO18, VSPICLK, HS1_DATA7                                                              | - |
| IO19	        |   31  |	I/O |   GPIO19, VSPIQ, U0CTS, EMAC_TXD0                                                         | - |
| NC	        |   32  |	-   |   -                                                                                       | - |
| IO21	        |   33  |	I/O |   GPIO21, VSPIHD, EMAC_TX_EN                                                              | - |
| RXD0	        |   34  |	I/O |   GPIO3, U0RXD, CLK_OUT2                                                                  | - |
| TXD0	        |   35  |	I/O |   GPIO1, U0TXD, CLK_OUT3, EMAC_RXD2                                                       | - |
| IO22	        |   36  |	I/O |   GPIO22, VSPIWP, U0RTS, EMAC_TXD1                                                        | - |
| IO23	        |   37  |	I/O |   GPIO23, VSPID, HS1_STROBE                                                               | - |
| GND	        |   38  |	P   |   Ground                                                                                  | - |
| GND	        |   39  |	P   |   Ground                                                                                  | - |

analogRead()を使用するため、ADC*_CH*のピンを選択する必要があります。

- IO26
    - ADC2_CH9
    - x軸の読み出し
- IO25
    - ADC2_CH8
    - y軸の読み出し


# 開発環境

以下が確認した環境となります。

* windows10 64bit
* Arduino IDE 1.8.10
  * Esplora Built-In by Arduino 1.0.4
  * FreeRTOS by Richard Barry 10.2.0-3
* vscode 1.41.1
  * PlatformIO - Home 3.0.1, Core 4.1.0

※Arduino IDEでも動くことは確認しましたが、
　Arduino IDEで動かす場合は、main.cppを.inoファイルに置き換える必要があります。


# 機材

※型名が購入先のリンクになっています。

|項目  |型名|備考  |
|---|---|---|
|ESP32-WROOM-32 開発ボード  |[NodeMCU-32S ESP32-WROOM-32](https://www.amazon.co.jp/gp/product/B07R87Z178/ref=ppx_yo_dt_b_asin_title_o06_s00?ie=UTF8&psc=1)   | -   |
|ジョイスティック        |[ジョイスティック ブレークアウト シールドモジュール ジョイスティック ゲーム コントローラ モジュール PS2]((https://www.amazon.co.jp/gp/product/B00M0F21KU/ref=ppx_yo_dt_b_asin_title_o00_s01?ie=UTF8&psc=1))   | -  |
|ブレッドボード  | 指定なし   |  |
|ジャンパーケーブル| 指定なし   |  |


# 接続図

![SampleJs_ブレッドボード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559938/fdd9451d-20ca-7b48-94bb-01dc6ad5bd75.png)

※今回、スイッチ（SW）は使用しないので、未接続になっています。

# シーケンス図

作成するプログラムのシーケンス図は、以下となります。


![. out_seq000_js.png.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559938/33d011c6-916d-dbf5-9cf7-05a5e7e9462c.png)


# コードの配置場所

コードは、下記に配置してあります。
[esp32Samples - SampleJs](https://github.com/kotaproj/esp32Samples/tree/master/SampleJs)

# シーケンスとコードとの紐づけ

シーケンスとコードの関連箇所を抜粋して記載します。

## 初期化処理

### JoyStickのPIN設定

* 使用するPINの定義

```Sytem_Js.h
#define JS_ASGN_AXIS_X		(26)
#define JS_ASGN_AXIS_Y		(25)
```

* PINの設定

```ApiJs.cpp
pinMode(JS_ASGN_AXIS_X, INPUT);
pinMode(JS_ASGN_AXIS_Y, INPUT);
```

### ジョイスティック用タイマーの生成

```ApiJs.cpp
#define JS_CHECK_TIME			(50)		//JoyStickチェック間隔:50msec
...

static TimerHandle_t s_xTimerJs = NULL;

...

ErType_t xInitJs(void)
{
	...
	s_xTimerJs = xTimerCreate
					(	cName,						//text name
						JS_CHECK_TIME,				//timer period
						pdTRUE,						//auto-reload
						( void * ) 0,				//number of times
						vTimerCallbackJs				//callback
					);

xTimerReset(	s_xTimerJs, JS_CHECK_TIME)

```

* auto-reload : pdTRUE
  * 50msec毎に定期的にコールするために指定


## ジョイスティックの状態の検出

### ジョイスティック状態の取得


```System_Js.h
/**
 * @enum JsCode_t
 * @brief JoyStick識別子
 * @note -		
 */
typedef enum
{
	JS_CODE_CENTER  = (0),
	JS_CODE_UP ,
	JS_CODE_DOWN ,
	JS_CODE_RIGHT ,
	JS_CODE_LEFT ,
	JS_CODE_ILL = (0xFF),
} JsCode_t;
```

ジョイスティックの識別子の定義


```ApiJs.cpp
/**
 * @brief JoyStickのタイマーコールバック関数
 */
static void vTimerCallbackJs( TimerHandle_t xTimer)
{
    int32_t x = analogRead(JS_ASGN_AXIS_X);
	int32_t y = analogRead(JS_ASGN_AXIS_Y);

	JsCode_t xJsCode;
	if( x < 500 )
	{
		xJsCode = JS_CODE_LEFT;
	}
	else if( x > 3600 )
	{
		xJsCode = JS_CODE_RIGHT;
	}
	else
	{
		if( y < 500 )
		{
			xJsCode = JS_CODE_DOWN;
		}
		else if( y > 3600 )
		{
			xJsCode = JS_CODE_UP;
		}
		else
		{
			xJsCode = JS_CODE_CENTER;
		}
	}


	// 状態の判定
	if( s_xBakJsCrd.xJsCode != xJsCode )
	{
		vDebugJsMsg(__func__, xJsCode, x, y);
		s_xBakJsCrd.xJsCode = xJsCode;
	}
```

x, yには、0-4095の値で取得されます。
今回の接続だと以下のような値が取得されます。


|ジョイスティック状態  |x値 | y値  |
|---|---|---|
|Idle   | 2048付近 | 2048付近 |
|左[←]  | 0付近    | 2048付近 |
|右[→]  | 4095付近 | 2048付近 |
|上[↑]  | 2048付近 | 4095付近 |
|下[↓]  | 2048付近 | 0付近    |

前回の判定と異なったら、vDebugJsMsg()にて、シリアル通信で通知します。


# 実行結果例

- 左 → 上 → 右 → 下の順に操作する
  - ログフォーマット：
  - vTimerCallbackJs - [判定した方向] - ([x値], [y値])


```
vTimerCallbackJs - CENTER - (1915, 1872)
vTimerCallbackJs - LEFT - (0, 1280)
vTimerCallbackJs - CENTER - (1808, 1766)
vTimerCallbackJs - UP - (1919, 4095)
vTimerCallbackJs - CENTER - (2063, 1957)
vTimerCallbackJs - RIGHT - (4095, 1871)
vTimerCallbackJs - CENTER - (2907, 1933)
vTimerCallbackJs - DOWN - (1894, 0)
vTimerCallbackJs - CENTER - (1909, 1869)
```


# 参考

* [FreeRTOS Software Timers](https://www.freertos.org/FreeRTOS-Software-Timer-API-Functions.html)