2021年2月14日日曜日

M5StackのLCDに表示した情報をESP32とLCDモジュールで表示するのはひと手間必要だった話


背景

M5StackとはLCDや電池を搭載し、プログラムを書き込める、電子工作に便利な装置です。
LCDが付いているのため試作機を素早く作るのに便利なので利用する機会が多いのですが、仕様が固まってくるとM5Stackに載っている機能(電池やスピーカー)を除いて最低限の装置が欲しくなります。

ピクセル数が同じ3.2インチのLCDがあるので、それをESP32の開発キットに接続してM5Stackと同様の構成にしてみました。
しかしながら、M5Stackで利用したのと同じプログラムでは、期待しない形で情報が表示されると分かりました。(タイトル画像のように、画面の縦横比が異なり、鏡文字が表示されました。)

調査と試行錯誤を重ねてESP32 + LCDモジュールでM5Stackと同じように情報を表示するプログラムの書き方を把握したので、備忘録を兼ねて記事として共有します。

使ったもの

  • M5StackやESP32にプログラムを書き込めるPC
    Arduino IDEかPlatformIOをインストールしておきます。
    Arduino IDEの場合はarduino-esp32をインストールしてください。
  • M5Stack
    比較用です。
  • ESP32 devkitC
    ESP32にプログラムを書き込める回路が載った開発ボードです。
  • 3.2インチ 320x240 LCD
    M5Stackに使われているLCDと同じILI9341というドライバが使わてているものです。
    開発元のページはこちらです。
    3.2inch SPI Module ILI9341 SKU:MSP3218
  • ブレッドボード
    ESP32開発ボードとLCDから信号線を引き出し、ジャンパワイヤで接続しやすい状態にします。
  • ジャンパワイヤ
    ブレッドボードに引き出さされた信号線を接続します。
  • USB microBケーブル
    ESP32開発ボードをPCに接続します。

配線

ESP32とLCDをこのように接続します。
M5Stackの回路図ではLEDはMSFETを通して制御する回路になっていますが、LCDモジュールはその回路が組み込まれているのでLEDをGPIO32か3V3に接続します。

LCD ESP32
VCC 3V3
GND GND
CS GPIO14
RESET GPIO33
DC GPIO27
MOSI GPIO23
SCK GPIO18
LED GPIO32 もしくは 3V3
MISO 未接続

ブレッドボードとジャンパワイヤを利用して接続するとこうなりました。


M5Stackで動かしているコード

このコードを動かすM5Stackと同じ情報をLCDモジュールに表示するのが今回の目的です。
#include <Arduino.h>
#include <M5Stack.h>

void setup() {
M5.begin();
M5.Lcd.begin();
M5.Lcd.setTextSize(4);
}

void loop() {
M5.Lcd.setCursor(0, 0);
M5.Lcd.println("Hello world");
M5.Lcd.setCursor(0, 200);
M5.Lcd.println("at " + String(millis()));
delay(100);
}

M5Stackで動かすとこうなります。


ESP32とLCDモジュールで動かすとこうなります。
画面の縦横比の不一致と鏡文字表示になる不具合が発生します。



文字だけの描画なら: TFT_eSPIで代替

今回扱うプログラムはLCDに文字だけを表示しているので、M5.Lcdを作っているM5Displayの継承元となっているTFT_eSPIを利用して、縦横比を渡しつつ初期化することで期待する動作をさせれます。
#include <Arduino.h>
#include <M5Stack.h>

#define LCD_WIDTH 320
#define LCD_HEIGHT 240
TFT_eSPI lcd(LCD_WIDTH, LCD_HEIGHT);

void setup() {
M5.begin(); // Activate gpio32 for background led
lcd.begin();
lcd.fillScreen(0);
lcd.setRotation(4);
lcd.setTextSize(4);
}

void loop() {
lcd.setCursor(0, 0);
lcd.println("Hello world");
lcd.setCursor(0, 200);
lcd.println("at " + String(millis()));
delay(100);
}

期待通りに動きました。


TFT_eSPIはM5Stack内部にコードを保持して使われていますが、元のコードはライブラリとして独立しています。

https://github.com/Bodmer/TFT_eSPI

M5Stackに関係無いプロジェクトでは上記のライブラリを利用するのも良さそうです。

M5Stackで使える関数を使い続けたいなら: 互換性のあるLovyanGFXを利用

TFT_eSPIの利用で文字だけの描画は解決したのですが、M5DisplayにはJpegを描画したり(drawJpg)webから画像を取ってきたり(drawPngUrl)、独自の機能があるため、それを利用したい場面があると思います。
その場合は、M5Displayと互換性を持たせつつ開発が続いているLovyanGFXを利用するのが1つの解決案です。(drawJpgdrawPngUrlもあります。)

https://github.com/lovyan03/LovyanGFX

LovyanGFXを利用して期待する情報を表示するコードはこちらです。
サンプルコード HowToUse/2_spi_setting を参考にしました。
今回利用するLCDのドライバであるILI9341用の設定クラスを宣言し、通信に必要なGPIOピンを割り当ててlcdを初期化することで動きました。

#include <Arduino.h>
#include <FS.h>
#include <SD.h>
#include <SPIFFS.h>
#include <HTTPClient.h>
#include <LovyanGFX.hpp>

lgfx::LGFX_SPI<lgfx::LGFX_Config> lcd;
lgfx::Panel_ILI9341 panel;

void setup() {
panel.spi_cs = 14;
panel.spi_dc = 27;
panel.gpio_rst = 33;
panel.gpio_bl = 32;
panel.pwm_ch_bl = 7;
lcd.setPanel(&panel);
lcd.init();

lcd.begin();
lcd.fillScreen(0);
lcd.setRotation(1);
lcd.setTextSize(4);
}

void loop() {
lcd.setCursor(0, 0);
lcd.println("Hello world");
lcd.setCursor(0, 200);
lcd.println("at " + String(millis()));
delay(100);
}


LovyanGFXはM5Stackに依存せず定義できるので、M5Stackに関係しないプロジェクトでM5Displayと同様の操作をしたい場合に重宝しそうです。
LovyanGFXの存在は、M5Displayの分割要求が却下されたgithubのissueへのコメントや、それに関するつぶやきへの返信で、開発者から教えていただきました。

非推奨: M5.Lcdを使い続けたいなら: ライブラリの幅と高さ情報を書き換える

どうしてもM5.Lcdの呼び出しを通してESP32とLCDモジュールの組み合わせで期待する表示をさせたい場合は、M5Stackのライブラリのコードを書き換えることで実現可能ではあります。
しかしながら、Arduino IDEの場合は他のプロジェクトにも影響し、PlatormIOの場合でもプロジェクトの管理外のコードを変更することになって保守が困難になるため、推奨はしません。

LIL9341_Defines.hのTFT_WIDTHとTFT_HEIGHTを書き換えます。
Arduino IDEの場合は [Arduinoのスケッチ置き場]/libraries/M5Stack/src/utility/ILI9341_Defines.h にあります。
PlatfomIOの場合は [プロジェクトのフォルダ]/.pio/libdeps/[esp32devなどのボード名]/M5Stack/src/utility/ILI9341_Defines.h にあります。
// #define TFT_WIDTH  240
// #define TFT_HEIGHT 320
#define TFT_WIDTH 320
#define TFT_HEIGHT 240

Lcd.begin()の後にsetRotationで4を設定することで、画面の回転と鏡文字表示の修正を行なえます。
M5.Lcd.begin();
M5.Lcd.setRotation(4);

上記の変更でM5.Lcdで ESP32 + LCDモジュールに期待通りの情報を表示できる状態になります。

しかしながら、ライブラリのコードを書き換えるのは開発時以外はしないのが良いと思います。
このような変更が必要な場合はM5Stackのコードをフォークして、自身のリポジトリのコードを書き換え、それを必要なライブラリとして使うのが良いと思います。

余談: M5StackのLCDは、ライブラリはILI9341のコードがあるけれど、ハードウェアはILI9342

LovyanGFXを作成した方から情報をいただきました。

調べてみるとM5Stack Basicの説明ページにILI9342を利用していると記述がありました。


ライブラリはILI9341ILI9342の記述があり、前者の方がファイル数が多いのでてっきり9341かと思っていました。

まとめ

M5Stackで表示していた情報をESP32とLCDモジュールの組み合わせで表示するには、M5Stackのライブラリをそのまま利用するのでは不十分でしたが、下記の方法があると把握しました。

いずれかの方法を採用することで、M5Stackと同じようにLCDに情報を表示するESP32とLCDモジュールを利用した装置が作れます。

変更履歴

2021.02.15
LovyyanGFXを知るきっかけとなったgithubのコメントtwitterの返信へのリンクを追加しました。
M5StackのLCDのドライバはILI9341かと思っていましたが、ILI9342だと情報をいただいたので、その情報を追加しました。

0 件のコメント :