本記事では、ビット・トレード・ワンで取り扱っている WeAct Studioブランド 電子ペーパーモジュール を題材に、Arduino環境から電子ペーパーを扱う方法を解説します。
今回扱うのは、1.54インチ、2.13インチ、4.2インチの3種類です。
いずれも白黒表示の電子ペーパーモジュールで、SPI接続で利用できます。
記事では、配線方法、GxEPD2ライブラリを使った基本表示と画像表示、さらに Webブラウザから画像を送る応用例 まで紹介します。
ビット・トレード・ワン(BitTradeOne)は神奈川県相模原に本社を置く、電子工作モジュール・キット、ユニークで便利なガジェット、同人ハードウェアの製品化サービス「BTOマイ・プロダクトサービス」、模型向けブランド「MODMAGIC」、各種受託を手掛ける、電子機器の製造・販売の会社です。
製品は各取扱店のほか、公式オンラインショップ BTOS やAmazonでも販売しています。
電子ペーパーとは
電子ペーパーは、高い視認性 と 低消費電力 が特長の表示デバイスです。
表示を書き換えるときに電力を使い、表示を維持している間の消費電力が小さいため、電子書籍リーダーやサイネージ、電子棚札などで広く利用されています。
近年では白黒だけでなく、赤・黄・青などを表示できる製品も増えています。
今回使用するモジュールについて
現在、ビット・トレード・ワンで取り扱っている WeAct Studio の電子ペーパーモジュールは以下の3種類で、すべて白黒パネルです。
いずれのモジュールも、マイコンとは 2x4ピンヘッダー または 専用コネクター(ケーブル付属) で接続できます。
WeAct Studio公式のGitHubリポジトリはこちらです。
https://github.com/WeActStudio/WeActStudio.EpaperModule
必要なもの
- Arduino IDE
- Arduinoで開発できるマイコン
- I/O電圧が 3.3V のものを選んでください。UNO R3やUNO R4は使えないので注意を!
- 電子ペーパーモジュール
- GxEPD2ライブラリ(Arduino IDEからインストールできます)
この記事ではマイコンとして、
- Suzuno32RV(ビット・トレード・ワンで販売中のRISC-Vマイコンボード)
- M5Stack AtomS3(ESP32-S3搭載の開発ボード)
を想定して解説します。
この記事で取り扱うモジュールは 信号レベルが3.3Vです。 Arduino UNO R3やUNO R4は使えません。
配線する
この電子ペーパーモジュールでは、ピンが8本引き出されています。電源、SPI、各種制御線です。
電源は3.3V~5Vに接続してください。
SPIはマイコンに応じて、SPIポートに接続してください。Suzuno32RVではSPIピンは固定です。AtomS3 / ESP32-S3では任意のピンが利用できます。
各種制御線はDC, RST, BUSYの3ピンで、これは任意のGPIOに接続してください。
サンプルコード
GxEPD2というライブラリを利用すると、パネルごとの差異を吸収しながら、電子ペーパーモジュールを簡単に制御できます。
- 3商品ではそれぞれ搭載しているチップが異なるため、チップ指定を切り替えられるようにしました。
- マイコン依存となるピン配置をマクロで切り替えられるようにしました。
- GxEPD2では描画を記述するさいに
display.firstPage(); do { /* 描画処理 */ } while (display.nextPage());という書き方をします。 - 以下のコードはWeAct Studio提供のサンプルプログラムを改変して作成しました。
#include <GxEPD2_BW.h>
#include <Fonts/FreeMonoBold9pt7b.h>
// ---- ピンの設定 ----
// M5AtomS3の例
#define MOSI_PIN 38
#define SCK_PIN 39
#define CS_PIN 5
#define DC_PIN 6
#define RES_PIN 7
#define BUSY_PIN 8
#define SPI_BEGIN() do { SPI.begin(SCK_PIN, -1, MOSI_PIN); } while (0)
// Suzuno32RVの例
// #define MOSI_PIN 11
// #define SCK_PIN 13
// #define CS_PIN 10
// #define DC_PIN 9
// #define RES_PIN 8
// #define BUSY_PIN 7
// #define SPI_BEGIN() do { SPI.begin(-1); } while (0)
// ---- パネル/チップの選択 ----
// 1.54インチ (GDEH0154D67 200x200, SSD1681)
//GxEPD2_BW<GxEPD2_154_D67, GxEPD2_154_D67::HEIGHT> display(GxEPD2_154_D67(CS_PIN, DC_PIN, RES_PIN, BUSY_PIN));
// 2.13インチ (DEPG0213BN 122x250, SSD1680)
GxEPD2_BW<GxEPD2_213_BN, GxEPD2_213_BN::HEIGHT> display(GxEPD2_213_BN(CS_PIN, DC_PIN, RES_PIN, BUSY_PIN));
// 4.2インチ (400x300, SSD1683)
// GxEPD2_BW<GxEPD2_420_GDEY042T81, GxEPD2_420_GDEY042T81::HEIGHT> display(GxEPD2_420_GDEY042T81(CS_PIN, DC_PIN, RES_PIN, BUSY_PIN));
void setup() {
Serial.begin(115200);
Serial.println("Initializing...");
SPI_BEGIN();
display.init(0, true, 50, false);
display.setRotation(3);
helloWorld();
Serial.println("Ready.");
}
void loop() {
delay(100);
}
const char HelloWorld[] = "Hello World!";
const char HelloWeACtStudio[] = "WeAct Studio";
const char HelloBitTradeOne[] = "BitTradeOne";
void helloWorld()
{
display.setFont(&FreeMonoBold9pt7b);
display.setTextColor(GxEPD_BLACK);
int16_t tbx, tby; uint16_t tbw, tbh;
display.getTextBounds(HelloWorld, 0, 0, &tbx, &tby, &tbw, &tbh);
// center the bounding box by transposition of the origin:
uint16_t x = ((display.width() - tbw) / 2) - tbx;
uint16_t y = ((display.height() - tbh) / 2) - tby;
int16_t tbx2, tby2;
uint16_t tbw2, tbh2;
display.getTextBounds(HelloBitTradeOne, 0, 0, &tbx2, &tby2, &tbw2, &tbh2);
uint16_t x2 = display.width() - tbw2;
uint16_t y2 = display.height() - tbh2;
display.setFullWindow();
display.firstPage();
do {
display.fillScreen(GxEPD_WHITE);
display.setCursor(x, y-tbh);
display.print(HelloWorld);
display.setTextColor(display.epd2.hasColor ? GxEPD_RED : GxEPD_BLACK);
display.getTextBounds(HelloWeACtStudio, 0, 0, &tbx, &tby, &tbw, &tbh);
x = ((display.width() - tbw) / 2) - tbx;
display.setCursor(x, y+tbh);
display.print(HelloWeACtStudio);
display.fillRoundRect(x2 - 8 - 8, y2 - 8 - 8, tbw2 + 16, tbh2 + 16, 8, GxEPD_BLACK);
display.setCursor(x2 - 8, y2);
display.setTextColor(GxEPD_WHITE);
display.print(HelloBitTradeOne);
} while (display.nextPage());
}
このコードをM5Stack AtomS3に書き込んで2.13インチ電子ペーパーモジュールを駆動すると、以下のように表示されます。
画像を表示する
今回取り上げた3製品はいずれも白黒表示なので、2値化した画像 を用意すれば表示できます。ディザリング処理をすれば、グレー階調のような見た目にすることも可能です。
画像表示を行う場合は、次の点を意識すると扱いやすくなります。
- あらかじめモジュールの解像度に合わせてリサイズする
- 白黒2値に変換する
- 必要に応じてディザリングをかける
- マイコン側ではプログラムに配列データとして埋め込み、または分割転送で扱う
なお4インチモジュールでは画像サイズが大きくなるため、マイコンのRAM容量に注意が必要です。
画像データの作成には image2cpp を使うと便利です。image2cpp では画像を白黒2値化できるだけでなく、Arduino向けの配列コードとして出力できます。 生成した配列を .h ファイルへ貼り付ければ、そのまま表示用データとして利用できます。
image2cppでは Invert image colorsにチェックを入れて コードを生成してください。image2cppは黒背景と輝点からなるOLED向けのツールなので、白と黒のビット表現が反転します。
画像データを epd_bitmap 変数に格納した場合のサンプルコードの抜粋を以下に示します。
const unsigned char epd_bitmap[] = { /* 画像データ */ };
void setup() {
Serial.begin(115200);
Serial.println("Initializing...");
SPI_BEGIN();
display.init(0, true, 50, false);
display.setFullWindow();
display.firstPage();
do {
display.fillScreen(GxEPD_WHITE);
display.drawBitmap(0, 0, epd_bitmap, display.width(), display.height(), GxEPD_BLACK);
} while (display.nextPage());
Serial.println("Ready.");
}
void loop() {
delay(100);
}
Webから画像を送信する
さらに応用的な使い方として、Suzuno32RVのUSBペリフェラルと組み合わせて、Webブラウザから電子ペーパーを描き変えるプログラムを組んでみました。
WebUSBを使って、ブラウザ上で準備した2値画像をマイコンへ転送し、電子ペーパーに描画します。400x300のモジュールではRAMが不足するため、分割して転送・描画をしています。
まとめ
ビット・トレード・ワンで取り扱っているWeAct Studio電子ペーパーモジュールの使い方を解説しました。GxEPD2ライブラリにより、チップ選択とピン配置に気をつければ簡単に描画をすることができます。
ぜひみなさまも電子ペーパー、挑戦してみてくださいね!
個人的には400x300ドットのモジュールがお気に入りです。この大きさと解像度、うまく活かしていきたいです!




