RaspberryPi Pico + ILI9341 + TFT_eSPI でタッチイベントを拾う

# はじめに

以下の記事で RaspberryPi Pico + ILI9341 (3.2インチLCD) で TFT_eSPI を用いて画面表示を行いました。

https://qiita.com/suzukiplan/items/1d063a73b41b6eb2900c

元々、ディスプレイドライバには LovyanGFX を用いる予定でしたが、LovyanGFX は RP2040 (RaspberryPi Pico) 向けのタッチスクリーンがサポートされていなかったため、TFT_eSPI を用いるようにしたという経緯があります。

ただし、TFT_eSPI + ILI9341 ではデフォルトでは正常にタッチ座標を拾うことができない問題があったので、本書ではその対処方法を記します。

# タッチイベントの拾い方

タッチイベントは（タッチパネルの配線が正常にできていれば）`TFT_eSPI::getTouch` で拾うことができます。

```c++
static TFT_eSPI gfx;

void loop() {
    uint16_t touchX;
    uint16_t touchY;
    bool touched = gfx.getTouch(&touchX, &touchY);
}
```

> タッチパネルの配線については、[こちらの記事](https://qiita.com/suzukiplan/items/1d063a73b41b6eb2900c) で示している配線で問題なく使えました。
>
> なお、本件原因を調査する過程で、「ブレッドボードだとダメかも？」と書かれた[記事](https://web.is.tokushima-u.ac.jp/wp/blog/2021/04/18/esp32-%E3%82%BF%E3%83%83%E3%83%81%E3%83%91%E3%83%8D%E3%83%AB%E4%BB%98%E3%81%8D%E6%B6%B2%E6%99%B6%E3%83%87%E3%82%A3%E3%82%B9%E3%83%97%E3%83%AC%E3%82%A4lcdili9341%EF%BC%8Csd%E3%82%AB%E3%83%BC/)がありましたが、私が確認した限り、ブレッドボードで特に問題無く使えました。

# 問題点

タッチイベントを拾った時の座標が次のようになっていました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2161417/ab3b7cbb-f6bd-114c-685c-ce5a3059769b.png)

少し余分な画面描画が入ってますが下図が実機写真です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2161417/a4132e21-582d-1577-4119-d5051d06c6ef.png)

# 原因

以下の issue に答えが書かれていました。

https://github.com/Bodmer/TFT_eSPI/issues/365

こちらの [commit](https://github.com/ImpulseAdventure/TFT_eSPI/commit/9a9654abc70adde3b83b1d8fd2fd08c9251437cb) で追加されているコメント部分です。

```c++
/***************************************************************************************
** Function name:           setRotation
** Description:             rotate the screen orientation m = 0-3 or 4-7 for BMP drawing
**                          When using the integrated touch handler, note that calibration
**                          settings must be updated whenever the orientation is changed from
**                          the orientation used during calibration.
***************************************************************************************/
void TFT_eSPI::setRotation(uint8_t m)
```

> 和訳:
> 内蔵タッチハンドラーを使用する場合は、キャリブレーション時に使用した向きから向きが変わるたびに、キャリブレーションの設定を更新する必要があることに注意してください。

# 対策

キャリブレーションを設定する API の説明が何処にもないのでカンですが `TFT_eSPI::setRotation(2)` (上下反転) した場合、`TFT_eSPI::setTouch` を以下のように実行すれば正常な座標が拾えるようになります。

```c++
gfx.setRotation(2);
uint16_t touch[] = {
      300,
      3600,
      300,
      3600,
      0b00000010
};
gfx.setTouch(touch);
```

回転状況により、`touch[4]` の bit-1, bit-2, bit-3 を調整する必要があります。

- bit-1 : rotation (landscape なら 1)
- bit-2 : X を反転
- bit-3 : Y を反転

# 余談

余談ですが、[安物のディスプレイ](https://ja.aliexpress.com/item/1005003005413104.html)ということもあって、タッチのレスポンスはかなり微妙です。

かなり昔のAndroid（version 2.3ぐらいの頃）のタッチレスポンスも微妙でしたが、それよりも酷くて、初期の任天堂DSやザウルスぐらいの頃（指ではなくタッチペンでの操作が前提だった頃）のタッチパネルぐらいの操作感です。

> デフォルトでタッチペンが付属していることからある程度は察してましたがｗ

ブレッドボードじゃなくてユニバーサル基板でスズメッキ線を極限まで短くすれば少しはマシになるのかな？_（SPIの遅さが原因ではなくて感圧の判定が微妙な感じだからあまり期待できなさそうですが）_

タッチレスポンスの良い ILI9341 が欲しいところです。

タッチパネルには、以下の2種類があり、

- 静電容量式
- 抵抗膜式

今検証で使っている ILI9341 は抵抗膜式で、抵抗膜式はある程度押し込まないと通電（タッチ反応）しないので、それがタッチレスポンスの体感的な悪さの原因かもしれません。

> スマートフォンは「静電容量式」が主流の筈です。

未確認ですが、以下が静電容量式を採用しているようなので、これを使えばタッチの体験は改善できるかも？（少しお値段は高いですが）

https://www.kickstarter.com/projects/arushi/touchsy