LoginSignup
1
0

電子ペーパーモジュールを試してみた話【GxEPD2ライブラリ】

Last updated at Posted at 2024-04-21

M5atomS3で試そうとして半日潰して無理だったので、arduinoUnoR4wifiを使って動かしてみた
べ、別に逃げたわけじゃないんだからねっ! ...嘘です、逃げです。ごめんなさい:sob:

はじめに

  • 秋葉原の某店(自社ロゴのプリントTシャツを売ってる、誇り高き店)で、電子ペーパーモジュールを見つけたので買ってみた。
  • ホームページには、arduino用のサンプルスケッチ(7z版),も載っていたので、簡単に動くだろうと思って手元のM5AtomS3で動くようにpin配置だけ追加で書きこんで動かそうとしたけど無理だった...
    ※ ちなみに、(zip版)も有るけど、中身が微妙に違う。これが中華クオリティ。
  • ハードウエアspi前提のスケッチで、ソフトウエアspiで動かそうとしたのがマズかったのかもしれないけど、調べるのを諦めて、家に帰ってUnoR4wifiに繋いだら、当たり前だけど普通に動いた。
  • ついでに、液晶ライブラリのu8g2のように、電子ペーパー用のライブラリGxEPD2が有ったので使ってみた。結論、GxEPD2はいいぞ。

公式サンプルの場合

  • 現在販売されているのはv3なので、下記ファイルが公式サンプルとなる。
    image.png
  • 動かすと、一つの画像ファイルを元に、黒地、白地、白地赤色で画像が順次表示された後、無地にもどる。
    17136767866160.jpg
    image.png
    image.png
  • 2.9インチのサンプルは他にもあって、今回試した9b_V3以外に9bcと9dを試してみたけど、9bcは動かず、9dは動いたものの、白黒のみのサンプルで、赤の表示はなかった。なお、9dの場合は、電源ピンに刺していたピンを6番に刺す必要がある。サンプル中では、PWR_PINと表現されている。
    image.png
    image.png

GxEPD2の設定

  • ここから、zipで落としてarduinoIDEに取り込んだけど、ライブラリマネージャからもインストール可能みたい。
    image.png
  • スケッチ例のうち、GFX_Exampleを動かしてみた。
    image.png
  • まず、不要なヘッダーをコメントアウト。3色用のみとし、また、(old style)と(new style)については、深く考えずにnew styleを採用。
    image.png
  • 設定の大前提として、使っている制御チップをwaveshareの提供資料で調べると、UC8151Dとなっている。
    image.png
  • よって、ヘッダーファイルで、3-colorクラスのうち、この解像度のUC8151Dのコメントを外す。
    image.png
  • ありがたいことに、UnoR4wifiに対応済み。下の方に、ピン設定が書いてあるけど、DC、RSTのピン番号が、公式サンプルと逆だから、差し直す必要があることに注意。差し直さずに、コードを書き換えてもいいけど。
    image.png
  • 初期状態で_BWが選択されているのを_3Cに変更するのを忘れずに。(忘れると、エラーが出るから気づくけどね)
    image.png
  • 無駄な解像度をコメントアウト!削らないと、コンパイル時にオーバーフローする。これは気づくまで、1時間ほど無駄にした...。コンパイルの詳細を見て、「制御チップ別のcppを大量に読み込んでやがんな〜、UC8151Dしかコメントを外してしてないのにっ!くそっ犯人はどこだ!」って、無駄に探した。これを読んだ人は、忘れずに削ってね。
    image.png

GxEPD2のサンプルスケッチ(GFX_Example)の動作

  • 結構、長時間の動作なので、全てを写真や動画に収めるのは面倒だから、最初と最後以外は、適当に撮ったのを載せておく。

  • 最初と途中
    image.png
    image.png
    image.png
    image.png
    image.png
    image.png

  • 最後
    image.png

  • 電子ペーパーは電源を落としても表示が残るから、焼き付け?みたいになるのが嫌なら、 void setup() の最後の方で display.fillScreen(GxEPD_WHITE); を追加する。

  • なお、.firstPage().nextPage() で挟むのがGxEPD2の流儀みたい。
    image.png

GxEPD2で使えるコマンド

  • GxEPD2_GFX.h の中身を見て想像して欲しい。
// Display Library for SPI e-paper panels from Dalian Good Display and boards from Waveshare.
// Requires HW SPI and Adafruit_GFX. Caution: the e-paper panels require 3.3V supply AND data lines!
//
// Display Library based on Demo Example from Good Display: https://www.good-display.com/companyfile/32/
//
// Author: Jean-Marc Zingg
//
// Version: see library.properties
//
// Library: https://github.com/ZinggJM/GxEPD2

#ifndef _GxEPD2_GFX_H_
#define _GxEPD2_GFX_H_

// uncomment next line to use class GFX of library GFX_Root instead of Adafruit_GFX
//#include <GFX.h>

#if defined(_GFX_H_)
#define GxEPD2_GFX_ROOT_CLASS GFX
#else
#include <Adafruit_GFX.h>
#define GxEPD2_GFX_ROOT_CLASS Adafruit_GFX
#endif

#include <GxEPD2_EPD.h>

class GxEPD2_GFX : public GxEPD2_GFX_ROOT_CLASS
{
  public:
    GxEPD2_GFX(GxEPD2_EPD& _epd2, int16_t w, int16_t h) : GxEPD2_GFX_ROOT_CLASS(w, h), epd2(_epd2) {};
    virtual uint16_t pages() = 0;
    virtual uint16_t pageHeight() = 0;
    virtual bool mirror(bool m) = 0;
    virtual void init(uint32_t serial_diag_bitrate = 0) = 0; // serial_diag_bitrate = 0 : disabled
    // init method with additional parameters:
    // initial false for re-init after processor deep sleep wake up, if display power supply was kept
    // this can be used to avoid the repeated initial full refresh on displays with fast partial update
    // NOTE: garbage will result on fast partial update displays, if initial full update is omitted after power loss
    // pulldown_rst_mode true for alternate RST handling to avoid feeding 5V through RST pin
    virtual void init(uint32_t serial_diag_bitrate, bool initial, uint16_t reset_duration = 20, bool pulldown_rst_mode = false) = 0;
    virtual void fillScreen(uint16_t color) = 0; // 0x0 black, >0x0 white, to buffer
    // display buffer content to screen, useful for full screen buffer
    virtual void display(bool partial_update_mode = false) = 0;
    // display part of buffer content to screen, useful for full screen buffer
    // displayWindow, use parameters according to actual rotation.
    // x and w should be multiple of 8, for rotation 0 or 2,
    // y and h should be multiple of 8, for rotation 1 or 3,
    // else window is increased as needed,
    // this is an addressing limitation of the e-paper controllers
    virtual void displayWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h) = 0;
    virtual void setFullWindow() = 0;
    // setPartialWindow, use parameters according to actual rotation.
    // x and w should be multiple of 8, for rotation 0 or 2,
    // y and h should be multiple of 8, for rotation 1 or 3,
    // else window is increased as needed, 
    // this is an addressing limitation of the e-paper controllers
    virtual void setPartialWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h) = 0;
    virtual void firstPage() = 0;
    virtual bool nextPage() = 0;
    virtual void drawPaged(void (*drawCallback)(const void*), const void* pv) = 0;
    virtual void drawInvertedBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w, int16_t h, uint16_t color) = 0;
    //  Support for Bitmaps (Sprites) to Controller Buffer and to Screen
    virtual void clearScreen(uint8_t value = 0xFF) = 0; // init controller memory and screen (default white)
    virtual void writeScreenBuffer(uint8_t value = 0xFF) = 0; // init controller memory (default white)
    // write to controller memory, without screen refresh; x and w should be multiple of 8
    virtual void writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false) = 0;
    virtual void writeImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
                                int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false) = 0;
    virtual void writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm) = 0;
    virtual void writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h) = 0; // default options false
    virtual void writeImagePart(const uint8_t* black, const uint8_t* color, int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
                                int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm) = 0;
    virtual void writeImagePart(const uint8_t* black, const uint8_t* color, int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
                                int16_t x, int16_t y, int16_t w, int16_t h) = 0; // default options false
    // write sprite of native data to controller memory, without screen refresh; x and w should be multiple of 8
    virtual void writeNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false) = 0;
    // write to controller memory, with screen refresh; x and w should be multiple of 8
    virtual void drawImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false) = 0;
    virtual void drawImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
                                int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false) = 0;
    virtual void drawImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm) = 0;
    virtual void drawImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h) = 0; // default options false
    virtual void drawImagePart(const uint8_t* black, const uint8_t* color, int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
                                int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm) = 0;
    virtual void drawImagePart(const uint8_t* black, const uint8_t* color, int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
                                int16_t x, int16_t y, int16_t w, int16_t h) = 0; // default options false
    // write sprite of native data to controller memory, with screen refresh; x and w should be multiple of 8
    virtual void drawNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false) = 0;
    virtual void refresh(bool partial_update_mode = false) = 0; // screen refresh from controller memory to full screen
    virtual void refresh(int16_t x, int16_t y, int16_t w, int16_t h) = 0; // screen refresh from controller memory, partial screen
    virtual void powerOff() = 0; // turns off generation of panel driving voltages, avoids screen fading over time
    virtual void hibernate() = 0; // turns powerOff() and sets controller to deep sleep for minimum power use, ONLY if wakeable by RST (rst >= 0)
  public:
    GxEPD2_EPD& epd2;
};

#endif
  • 投げっぱなしでスマン。実のところ、まだ、何も試してないのです。
  • サンプルスケッチの場合、Adafruit_GFX と同じコマンドが使えるみたい。
    image.png
  • 実際、こんなコードがサンプルスケッチにあるし。
    image.png
    image.png
    image.png

最後に

  • GxEPD2は初めて知ったけど、結構洗練されてんじゃないかな?もともと、GxEPDが有って、それも2022年まで使われていて、バージョン3.1.3まで進んでた。
  • 簡単に電子ペーパー表示の実装ができることを確認できたから、今度は大きいサイズの電子ペーパーを買ってみようかと思う。7色の5.65インチで1万円を切っているし。
    image.png
  • 昨日は、M5atomS3で挑んで半日潰して手も足も出なかったから、今日、なんとかGxEPD2の利用までたどり着けて良かった。これで、気持ちよく月曜日を迎えられるわ。週末にストレスをためるのは洒落にならないから、本当に良かった。ありがとう、GxEPD2!
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0