ILI9488 TFT ディスプレイモジュールにSDの画像ファイルを表示。こんなに簡単だったの？初心者でもできる！

![KIMG0486.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3189926/c595dd1b-6c4b-80f2-7a95-f35c84154837.jpeg)

【2025/06/14追記】現在、以下のスケッチのままではコンパイルエラーが発生し、動作しない可能性があります。その場合は次の最新の記事を参考にしてください。

https://qiita.com/Mobu_Kyoto/items/6d8a004a485f0ac7b623

----------------------------------------------------------------
　今までマイコンでの画像ファイル表示は難しいんだろうな思っていましたが、えっ？こんなに簡単なのと拍子抜けしました。いつもながら、初心者の内容で申し訳ないですが投稿します。使用したモノと環境は以下の通り。（詳しく知りたい方は、以前の投稿「迷路作成」をご覧ください）

:::note info
　・ILI9488 TFT ディスプレイモジュール 3.5" 480x320 SPI接続 SDスロット付き
　・Heltec WiFi Kit 32（ESP32）
　・Arduino IDE 2.1.1
:::
　"TFT_eSPI" さらに "LovyanGFX" 

https://github.com/lovyan03/LovyanGFX

という素晴らしいライブラリを知り、新しい事が学べました。作者の皆様には感謝してもし切れないですね。今回特に役に立ち参考にさせていただいたのは次のサイトです。

https://lang-ship.com/blog/work/lovyangfx-5-png/#toc2

　このような場合、表示したい画像を事前に「変換」して…と思い込んでいましたが、そんな必要もなく、サイズを調整したファイルを用意し、SDカードからただ読み込んで表示させるだけ。えっ？て感じですね！あまりにあっけなかったので、sprite、回転、縮小・拡大、アフィン変換もやってみました。以下がコードです。
```c:9488_SDfileDisplay.ino
#include <SD.h>
#include <sd_defines.h>
#include <sd_diskio.h>

#include "myLovyanGFX.hpp"

static LGFX lcd;
//static LGFX_Sprite sprite0(&lcd);
static LGFX_Sprite sprite1(&lcd);  // スプライトを使う場合はLGFX_Spriteのインスタンスを作成。
static LGFX_Sprite sprite2(&lcd);

#define SDCS 4

void setup() {
  //sprite0.createSprite(480, 320);// 幅480、高さ320でスプライトを作成。
  sprite1.createSprite(218, 105);  // 幅218、高さ105でスプライトを作成。
  sprite2.createSprite(145, 150);  // 幅145、高さ150でスプライトを作成。
  Serial.begin(115200);
  lcd.init();
  lcd.setRotation(1);
  lcd.clear(TFT_GREEN);
  if (!SD.begin(SDCS, SPI, 20000000)) {
    Serial.println("SD Failed.");
  } else {
    //sprite0.drawBmpFile(SD,"/img.bmp", 0, 0); // bmpファイルはsprite表示できなかった
                                                // ただし、lcdへの表示はできた。
    lcd.drawBmpFile(SD, "/img.bmp", 0, 0);      // lcd座標0,0に描画

    sprite1.drawPngFile(SD, "/compo.png", 0, 0);// スプライト座標0,0に描画
    sprite2.drawJpgFile(SD, "/yuuta.jpg", 0, 0);

    //sprite0.pushSprite(0, 0);
    sprite1.pushSprite(10, 0);              // lcdの座標10,0にスプライトを描画
    sprite2.pushSprite(40, 90, TFT_WHITE);  // lcdの座標40,90にスプライトを描画,透過色指定

                        // 表示座標,回転角,横倍率,縦倍率,透過色指定
    sprite2.pushRotateZoom(250, 180, 45.0, 0.5, 0.6, 0xFeFeFeU);

    float matrix[6] = { // アフィン変換
      1.1,    // 横1.1倍
      -0.5,   // 横傾き
      360.0,  // X座標360
      0.2,    // 縦傾き
      0.7,    // 縦0.8倍
      130.0   // Y座標130
    };
    sprite2.pushAffine(matrix, TFT_WHITE);
  }
}
void loop() {
}

```
 なお、「Include library → Manage Libraries...」後に作られる

:::note
\libraries\LovyanGFX\examples\HowToUse\2_user_setting\2_user_setting.ino
:::
に従って、自分用の hppファイル（私の場合は"myLovyanGFX.hpp"）を編集用意し、"new tab"でinoファイルと同じフォルダーに加えておく必要があります。
```myLovyanGFX.hpp
#pragma once

#define LGFX_USE_V1

#include <LovyanGFX.hpp>

// ESP32でLovyanGFXを独自設定で利用する場合の設定例

/*
このファイルを複製し、新しい名前を付けて、環境に合わせて設定内容を変更してください。
作成したファイルをユーザープログラムからincludeすることで利用可能になります。

複製したファイルはライブラリのlgfx_userフォルダに置いて利用しても構いませんが、
その場合はライブラリのアップデート時に削除される可能性があるのでご注意ください。

安全に運用したい場合はバックアップを作成しておくか、ユーザープロジェクトのフォルダに置いてください。
//*/


/// 独自の設定を行うクラスを、LGFX_Deviceから派生して作成します。
class LGFX : public lgfx::LGFX_Device
{
/*
 クラス名は"LGFX"から別の名前に変更しても構いません。
 AUTODETECTと併用する場合は"LGFX"は使用されているため、LGFX以外の名前に変更してください。
 また、複数枚のパネルを同時使用する場合もそれぞれに異なる名前を付けてください。
 ※ クラス名を変更する場合はコンストラクタの名前も併せて同じ名前に変更が必要です。

 名前の付け方は自由に決めて構いませんが、設定が増えた場合を想定し、
 例えばESP32 DevKit-CでSPI接続のILI9341の設定を行った場合、
  LGFX_DevKitC_SPI_ILI9341
 のような名前にし、ファイル名とクラス名を一致させておくことで、利用時に迷いにくくなります。
//*/

lgfx::Panel_ILI9488   _panel_instance;
lgfx::Bus_SPI         _bus_instance;   // SPIバスのインスタンス
lgfx::Touch_XPT2046   _touch_instance;

public:
  // コンストラクタを作成し、ここで各種設定を行います。
  // クラス名を変更した場合はコンストラクタも同じ名前を指定してください。
  LGFX(void)
  {
    { // バス制御の設定を行います。
      auto cfg = _bus_instance.config();    // バス設定用の構造体を取得します。

// SPIバスの設定
      cfg.spi_host = VSPI_HOST;     // 使用するSPIを選択  ESP32-S2,C3 : SPI2_HOST or SPI3_HOST / ESP32 : VSPI_HOST or HSPI_HOST
      // ※ ESP-IDFバージョンアップに伴い、VSPI_HOST , HSPI_HOSTの記述は非推奨になるため、エラーが出る場合は代わりにSPI2_HOST , SPI3_HOSTを使用してください。
      cfg.spi_mode = 0;             // SPI通信モードを設定 (0 ~ 3)
      cfg.freq_write = 40000000;    // 送信時のSPIクロック (最大80MHz, 80MHzを整数で割った値に丸められます)
      cfg.freq_read  = 16000000;    // 受信時のSPIクロック
      cfg.spi_3wire  = true;        // 受信をMOSIピンで行う場合はtrueを設定
      cfg.use_lock   = true;        // トランザクションロックを使用する場合はtrueを設定
      cfg.dma_channel = SPI_DMA_CH_AUTO; // 使用するDMAチャンネルを設定 (0=DMA不使用 / 1=1ch / 2=ch / SPI_DMA_CH_AUTO=自動設定)
      // ※ ESP-IDFバージョンアップに伴い、DMAチャンネルはSPI_DMA_CH_AUTO(自動設定)が推奨になりました。1ch,2chの指定は非推奨になります。
      cfg.pin_sclk = 18;            // SPIのSCLKピン番号を設定
      cfg.pin_mosi = 23;            // SPIのMOSIピン番号を設定
      cfg.pin_miso = 19;            // SPIのMISOピン番号を設定 (-1 = disable)
      cfg.pin_dc   = 27;            // SPIのD/Cピン番号を設定  (-1 = disable)
     // SDカードと共通のSPIバスを使う場合、MISOは省略せず必ず設定してください。
      _bus_instance.config(cfg);    // 設定値をバスに反映します。
      _panel_instance.setBus(&_bus_instance);      // バスをパネルにセットします。
    }

    { // 表示パネル制御の設定を行います。
      auto cfg = _panel_instance.config();    // 表示パネル設定用の構造体を取得します。

      cfg.pin_cs           =     5;  // CSが接続されているピン番号   (-1 = disable)
      cfg.pin_rst          =    32;  // RSTが接続されているピン番号  (-1 = disable)
      cfg.pin_busy         =    -1;  // BUSYが接続されているピン番号 (-1 = disable)

      // ※ 以下の設定値はパネル毎に一般的な初期値が設定されていますので、不明な項目はコメントアウトして試してみてください。

      cfg.panel_width      =   320;  // 実際に表示可能な幅
      cfg.panel_height     =   480;  // 実際に表示可能な高さ
      cfg.offset_x         =     0;  // パネルのX方向オフセット量
      cfg.offset_y         =     0;  // パネルのY方向オフセット量
      cfg.offset_rotation  =     0;  // 回転方向の値のオフセット 0~7 (4~7は上下反転)
      cfg.dummy_read_pixel =     8;  // ピクセル読出し前のダミーリードのビット数
      cfg.dummy_read_bits  =     1;  // ピクセル以外のデータ読出し前のダミーリードのビット数
      cfg.readable         =  true;  // データ読出しが可能な場合 trueに設定
      cfg.invert           = false;  // パネルの明暗が反転してしまう場合 trueに設定
      cfg.rgb_order        = false;  // パネルの赤と青が入れ替わってしまう場合 trueに設定
      cfg.dlen_16bit       = false;  // 16bitパラレルやSPIでデータ長を16bit単位で送信するパネルの場合 trueに設定
      cfg.bus_shared       =  true;  // SDカードとバスを共有している場合 trueに設定(drawJpgFile等でバス制御を行います)

// 以下はST7735やILI9163のようにピクセル数が可変のドライバで表示がずれる場合にのみ設定してください。
//    cfg.memory_width     =   240;  // ドライバICがサポートしている最大の幅
//    cfg.memory_height    =   320;  // ドライバICがサポートしている最大の高さ

      _panel_instance.config(cfg);
    }

/*
    { // タッチスクリーン制御の設定を行います。（必要なければ削除）
      auto cfg = _touch_instance.config();

      cfg.x_min      = 0;    // タッチスクリーンから得られる最小のX値(生の値)
      cfg.x_max      = 319;  // タッチスクリーンから得られる最大のX値(生の値)
      cfg.y_min      = 0;    // タッチスクリーンから得られる最小のY値(生の値)
      cfg.y_max      = 479;  // タッチスクリーンから得られる最大のY値(生の値)
      cfg.pin_int    = -1;   // INTが接続されているピン番号
      cfg.bus_shared = true; // 画面と共通のバスを使用している場合 trueを設定
      cfg.offset_rotation = 0;// 表示とタッチの向きのが一致しない場合の調整 0~7の値で設定

// SPI接続の場合
      cfg.spi_host = VSPI_HOST;// 使用するSPIを選択 (HSPI_HOST or VSPI_HOST)
      cfg.freq = 1000000;     // SPIクロックを設定
      cfg.pin_sclk = 18;     // SCLKが接続されているピン番号
      cfg.pin_mosi = 23;     // MOSIが接続されているピン番号
      cfg.pin_miso = 19;     // MISOが接続されているピン番号
      cfg.pin_cs   =  4;     //   CSが接続されているピン番号

    }
*/

    setPanel(&_panel_instance); // 使用するパネルをセットします。
  }
};
```
　私には理由が解りませんが、BMPファイルだけsprite表示できませんでした。
　お気づきの点がございましたら、お教えいただけると幸いです。