ESP32－FreeRTOSにて、ロータリーエンコーダのイベントを検出する

# はじめに

前回、[スイッチのイベントの検出](https://qiita.com/kotaproj/items/e9b2f56a89ff0a6a3d3a)を記載しました。
今回は、ロータリーエンコーダの検出について記載します。

# 開発環境

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

* windows10 64bit
* Arduino IDE 1.8.10
  * Esplora Built-In by Arduino 1.0.4
  * FreeRTOS by Richard Barry 10.2.0-3
  * RotaryEncoder by Matthias Hertel 1.3.0 - [Github - RotaryEncoder](https://github.com/mathertel/RotaryEncoder)
* vscode 1.41.1
  * PlatformIO - Home 3.0.1, Core 4.1.0

本コードは、[RotaryEncoderライブラリ](https://github.com/mathertel/RotaryEncoder)を使用しています。<br>
./lib下に、RotaryEncoderのライブラリを配置する必要があります。<br>
```
 ./SampleRe/lib/RotaryEncoder-master/RotaryEncoder.cpp
 ./SampleRe/lib/RotaryEncoder-master/RotaryEncoder.h
```
※Arduino IDEでも動くことは確認しましたが、
　Arduino IDEで動かす場合は、main.cppを.inoファイルに置き換える必要があります。
　また、Arduino IDE時は、ライブラリマネージャーよりインストールすることができます。


# 機材と接続図

2種類のロータリーエンコーダで確認したので、それぞれ記載します。

* フルカラーRGB LED付ロータリーエンコーダを使用
* HiLetgo 回転式のエンコーダモジュールを使用

## フルカラーRGB LED付ロータリーエンコーダ時のの機材

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

|項目  |型名|備考  |
|---|---|---|
|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)   | -   |
|ロータリーエンコーダ        |[EC12PLRGBSDVBF-D-25K-24-24C-61]((http://akizukidenshi.com/catalog/g/gP-05773/))   |DIP化基板とはんだ付け必要  |
|ロータリーエンコーダDIP化基板 RECNV-2  |[AE-RECNV-2](http://akizukidenshi.com/catalog/g/gP-07240/)   |  |
|抵抗  | 10kΩ x 2   |  |
|ブレッドボード  | 指定なし   |  |
|ジャンパーケーブル| 指定なし   |  |

## フルカラーRGB LED付ロータリーエンコーダ時の接続図

![SampleRe_ブレッドボード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559938/ee40fad1-f974-4060-db14-3e588fe8e1b8.png)

↓が実際の写真です。ユニバーサルボードにのっていますが。

![ロータリーエンコーダ実写真_x1_4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559938/bd00e894-4023-9a00-0d23-24e732c1794f.jpeg)



## HiLetgo 回転式のエンコーダモジュール時の機材

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

|項目  |型名|備考  |
|---|---|---|
|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)   | -   |
|ロータリーエンコーダ        |[HiLetgo 回転式のエンコーダモジュール]((https://www.amazon.co.jp/gp/product/B014VUOP7S/ref=ppx_yo_dt_b_asin_title_o00_s00?ie=UTF8&psc=1))   |はんだ付け不要  |
|ブレッドボード  | 指定なし   |  |
|ジャンパーケーブル| 指定なし   |  |

## HiLetgo 回転式のエンコーダモジュール時の接続図

![SampleRe_HiLetgo_ブレッドボード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559938/4bfb52ad-57d5-ecc4-3a23-5ea09434b59a.png)

↓が実際の写真です。

![ロータリーエンコーダHiLetgo実写真_x1_4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559938/004d0948-fd7e-2fdb-a194-f71758e651d1.jpeg)

# シーケンス図

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

![. out_seq000_sw.png.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559938/ba730e14-d607-245a-93e3-64e0d8fd27f9.png)

# コードの配置場所

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

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

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

## 初期化処理

### REのインスタンスの作成

* 使用するPINの定義

```Sytem_Re.h
#define RE_ASGN_SIGNAL_A	(4)
#define RE_ASGN_SIGNAL_B	(2)
```

* インスタンスの生成

```ApiRe.cpp
#include <RotaryEncoder.h>

static RotaryEncoder s_xRotaryEncoder(RE_ASGN_SIGNAL_A, RE_ASGN_SIGNAL_B);
```


###  RE用割り込みのアタッチ

```ApiRe.cpp
	// 割り込み関連の設定
	attachInterrupt(digitalPinToInterrupt(RE_ASGN_SIGNAL_A), vIsrCallbackRe, CHANGE);
	attachInterrupt(digitalPinToInterrupt(RE_ASGN_SIGNAL_B), vIsrCallbackRe, CHANGE);
```
* vIsrCallbackRe
  * 割り込み時の処理（後述）
* CHANGE
  * A相, B相の変化点が必要のため、CHANGEを指定


### RE用タイマーの生成

```ApiRe.cpp
#define RE_CHECK_TIME			(50)		//ロータリーエンコーダチェック間隔:50msec
...

static TimerHandle_t s_xTimerRe = NULL;

...

ErType_t xInitRe(void)
{
	...
	s_xTimerRe = xTimerCreate
					(	cName,						//text name
						RE_CHECK_TIME,				//timer period
						pdTRUE,						//auto-reload
						( void * ) 0,				//number of times
						vTimerCallbackRe				//callback
					);

xTimerReset(	s_xTimerRe, RE_CHECK_TIME)

```

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


## 回転イベントの検出

### 割り込みの発生／回転数の保持

```ApiRe.cpp
/**
 * @brief ロータリーエンコーダ割り込み処理
 */
static void IRAM_ATTR vIsrCallbackRe()
{
	s_xRotaryEncoder.tick(); // just call tick() to check the state.
}
```

割り込みが発生した場合、上記の関数がコールされます。
tick()をコールし、回転数が保持されます。



### 回転数の取得／回転状態を送信

```ApiRe.cpp

/**
 * @brief ロータリーエンコーダのタイマーコールバック関数
 */
static void vTimerCallbackRe( TimerHandle_t xTimer)
{
	static int32_t pos;

	pos = s_xRotaryEncoder.getPosition();
	if( pos != s_slBakRePosition )
	{
		Serial.printf("%s - (%d), (%d)\n",__func__ ,pos ,(pos - s_slBakRePosition));
		s_slBakRePosition = pos;
	}

	return;
}
```
50msec毎に、上記の関数がソフトウェアタイマータスク内で処理されます。
前回の状態と異なる場合、Serial.printf()でユーザに通知します。

# 実行結果例

* 時計回りを実施

```
vTimerCallbackRe - (11), (1)
vTimerCallbackRe - (12), (1)
vTimerCallbackRe - (13), (1)
vTimerCallbackRe - (14), (1)
vTimerCallbackRe - (17), (3)
vTimerCallbackRe - (18), (1)
```

* 反時計回りを実施

```
vTimerCallbackRe - (25), (-1)
vTimerCallbackRe - (23), (-2)
vTimerCallbackRe - (21), (-2)
vTimerCallbackRe - (20), (-1)
```

# 参考

* [FreeRTOS Software Timers](https://www.freertos.org/FreeRTOS-Software-Timer-API-Functions.html)
* [A Library for the Arduino environment for using a rotary encoder as an input.](http://www.mathertel.de/Arduino/RotaryEncoderLibrary.aspx)
* [github - RotaryEncoder](https://github.com/mathertel/RotaryEncoder)