Arduino入門

2. デバイス&サンプル

5. 拡張モジュール&サンプル

アクセサリー

6. アプリケーション

PaperMono Display 画面表示

PaperMono のディスプレイに関する API とサンプルプログラムです。

サンプルプログラム

ビルド要件

  • M5Stack ボードマネージャのバージョン >= 3.3.9
  • ボードオプション = M5PaperMono
  • M5Unified ライブラリのバージョン >= 0.2.20
  • M5GFX ライブラリのバージョン >= 0.2.27

基本表示

cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
#include <Arduino.h>
#include <M5Unified.h>

M5Canvas canvas(&M5.Display);

static void drawBlackBorder()
{
    const int screenW = M5.Display.width();
    const int screenH = M5.Display.height();

    canvas.drawRect(0, 0, screenW, screenH, TFT_BLACK);
    canvas.drawRect(2, 2, screenW - 4, screenH - 4, TFT_BLACK);
    canvas.drawRect(4, 4, screenW - 8, screenH - 8, TFT_BLACK);
}

static int drawGrayscaleWordCentered(const char* word, int y, const uint16_t* colors, int colorCount)
{
    const int screenW = M5.Display.width();
    const int len     = strlen(word);
    int totalW        = 0;

    for (int i = 0; i < len; ++i) {
        totalW += canvas.textWidth(String(word[i]));
    }

    int x = (screenW - totalW) / 2;
    for (int i = 0; i < len; ++i) {
        canvas.setTextColor(colors[i % colorCount]);
        String ch(word[i]);
        canvas.drawString(ch, x, y);
        x += canvas.textWidth(ch);
    }
    return x;
}

static void drawMainDemo()
{
    const int screenW = M5.Display.width();
    const int screenH = M5.Display.height();

    static const uint16_t paperColors[1] = {TFT_BLACK};
    static const uint16_t monoColors[4] = {
        TFT_BLACK,
        TFT_RED,
        TFT_DARKGREY,        
        TFT_BLACK
    };
    static const uint16_t grayColors[4] = {
        TFT_BLACK,
        TFT_RED,        
        TFT_DARKGREY,
        TFT_WHITE
    };

    canvas.fillSprite(TFT_WHITE);
    drawBlackBorder();

    canvas.setTextDatum(top_left);
    canvas.setTextSize(1);
    canvas.setFont(&fonts::FreeSansBold24pt7b);

    drawGrayscaleWordCentered("PAPER", 168, paperColors, 1);
    drawGrayscaleWordCentered("MONO", 236, monoColors, 4);

    canvas.setFont(&fonts::FreeSansBold12pt7b);
    canvas.setTextColor(TFT_BLACK);
    canvas.setTextDatum(middle_center);
    canvas.drawString("Panel: SSD1677 + FT6336G", screenW / 2, 336);

    char resolutionBuf[32];
    snprintf(
        resolutionBuf,
        sizeof(resolutionBuf),
        "Resolution: %d x %d",
        screenW,
        screenH
    );
    canvas.drawString(resolutionBuf, screenW / 2, 376);

    canvas.drawString(
        "4-Level Grayscale E-Paper",
        screenW / 2,
        418
    );

    const int barX  = 46;
    const int barY  = 448;
    const int barW  = screenW - 92;
    const int barH  = 46;
    const int cellW = barW / 4;

    for (int i = 0; i < 4; ++i) {
        const int x = barX + i * cellW;
        const int w = (i == 3) ? (barX + barW - x) : cellW;

        canvas.fillRect(x, barY, w, barH, grayColors[i]);
        canvas.drawRect(x, barY, w, barH, TFT_BLACK);
    }

    canvas.pushSprite(0, 0);
}

void setup()
{
    auto cfg = M5.config();
    cfg.clear_display = false;
    M5.begin(cfg);

    M5.Display.setEpdMode(epd_mode_t::epd_quality);
    canvas.createSprite(M5.Display.width(), M5.Display.height());
    drawMainDemo();
}

void loop()
{
    M5.update();
    delay(100);
}

更新モード

PaperMono は、以下の 4 種類の電子ペーパー更新モードに対応しています。更新速度が速いほど、一般に表示品質と残像の抑制効果は低下します。実際の更新時間は画像の内容によっても異なります。

cpp
1 2 3 4
M5.Display.setEpdMode(epd_mode_t::epd_quality); // High quality, slowest refresh
M5.Display.setEpdMode(epd_mode_t::epd_text);    // Text mode, faster refresh, lower quality
M5.Display.setEpdMode(epd_mode_t::epd_fast);    // Fast refresh, lower quality
M5.Display.setEpdMode(epd_mode_t::epd_fastest); // Fastest refresh, lowest quality

PaperMono の更新時間の目安は次のとおりです。

更新モード 所要時間の目安
epd_quality 4.71 s
epd_text 0.45 s
epd_fast 0.34 s
epd_fastest 0.07 s

M5.Display.setEpdMode() を呼び出すと、それ以降の画面更新では選択したモードが使用されます。テキスト中心の画面には epd_text、画像や高いグレースケール品質が必要な場面には epd_quality を推奨します。高速な部分更新を連続して複数回行った後は、必要に応じて高品質な全画面更新を実行し、残像を軽減してください。

cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
#include <M5Unified.h>
#include <M5GFX.h>

M5Canvas canvas(&M5.Display);

struct ModeOption {
    const char* name;
    epd_mode_t mode;
};

const ModeOption modeOptions[] = {
    {"epd_quality", epd_mode_t::epd_quality},
    {"epd_text", epd_mode_t::epd_text},
    {"epd_fast", epd_mode_t::epd_fast},
    {"epd_fastest", epd_mode_t::epd_fastest},
};

constexpr std::size_t MODE_COUNT = sizeof(modeOptions) / sizeof(modeOptions[0]);
constexpr int BUTTON_X = 16;
constexpr int BUTTON_Y = 130;
constexpr int BUTTON_H = 90;
constexpr int BUTTON_GAP = 34;

std::size_t selectedMode = 0;
uint32_t refreshCount = 0;
LGFX_Button modeButtons[MODE_COUNT];

int buttonWidth()
{
    return M5.Display.width() - BUTTON_X * 2;
}

void initModeButtons()
{
    const int width = buttonWidth();
    for (std::size_t i = 0; i < MODE_COUNT; ++i) {
        const int y = BUTTON_Y + static_cast<int>(i) * (BUTTON_H + BUTTON_GAP);
        modeButtons[i].initButtonUL(&M5.Lcd, BUTTON_X, y, width, BUTTON_H,
                                    TFT_BLACK, TFT_WHITE, TFT_BLACK,
                                    modeOptions[i].name, 1, 1);
    }
}

void drawScreen()
{
    const int w = M5.Display.width();
    const int h = M5.Display.height();

    canvas.fillSprite(TFT_WHITE);
    canvas.setTextDatum(middle_center);
    canvas.setFont(&fonts::FreeSansBold18pt7b);
    canvas.setTextColor(TFT_BLACK);

    char selectedText[40];
    snprintf(selectedText, sizeof(selectedText), "Selected: %s", modeOptions[selectedMode].name);
    canvas.drawString(selectedText, w / 2, 30);

    canvas.setFont(&fonts::FreeSansBold12pt7b);
    canvas.drawString("Tap a button to refresh once", w / 2, 82);

    canvas.setFont(&fonts::FreeSansBold18pt7b);

    for (std::size_t i = 0; i < MODE_COUNT; ++i) {
        const int y = BUTTON_Y + static_cast<int>(i) * (BUTTON_H + BUTTON_GAP);
        const bool selected = i == selectedMode;
        const int width = buttonWidth();

        if (selected) {
            canvas.fillRect(BUTTON_X, y, width, BUTTON_H, TFT_BLACK);
        } else {
            canvas.fillRect(BUTTON_X, y, width, BUTTON_H, TFT_WHITE);
        }
        canvas.drawRect(BUTTON_X, y, width, BUTTON_H, TFT_BLACK);
        canvas.setTextColor(selected ? TFT_WHITE : TFT_BLACK);
        canvas.drawString(modeOptions[i].name, w / 2, y + BUTTON_H / 2);
    }

    char countText[32];
    snprintf(countText, sizeof(countText), "Refresh count: %lu", (unsigned long)refreshCount);
    canvas.setFont(&fonts::FreeSansBold12pt7b);
    canvas.setTextColor(TFT_BLACK);
    canvas.drawString(countText, w / 2, h - 110);

    const int stripeY = h - 75;
    const int stripeW = (w - 48) / 4;
    const uint16_t stripeColors[] = {TFT_BLACK, TFT_DARKGREY, TFT_LIGHTGREY, TFT_WHITE};
    for (int i = 0; i < 4; ++i) {
        const int x = 24 + i * stripeW;
        canvas.fillRect(x, stripeY, stripeW, 42, stripeColors[i]);
        canvas.drawRect(x, stripeY, stripeW, 42, TFT_BLACK);
    }

    canvas.pushSprite(0, 0);
}

void selectMode(std::size_t index)
{
    selectedMode = index;
    M5.Display.setEpdMode(modeOptions[selectedMode].mode);
    ++refreshCount;
    drawScreen();
}

void setup()
{
    auto cfg = M5.config();
    cfg.clear_display = false;
    M5.begin(cfg);
    M5.Display.setRotation(0);
    M5.Display.setEpdMode(modeOptions[selectedMode].mode);

    initModeButtons();
    canvas.createSprite(M5.Display.width(), M5.Display.height());
    drawScreen();
}

void loop()
{
    M5.update();
    m5::touch_point_t touchPoint;
    const bool touched = M5.Display.getTouchRaw(&touchPoint, 1) > 0;

    for (std::size_t i = 0; i < MODE_COUNT; ++i) {
        const bool inside = touched && modeButtons[i].contains(touchPoint.x, touchPoint.y);
        modeButtons[i].press(inside);
        if (modeButtons[i].justPressed()) {
            selectMode(i);
        }
    }
    delay(5);
}

電子ペーパーは高頻度の連続更新には適していません。各モードをテストするときは、現在の更新が完了してから次のボタンを選択してください。

フロントライトの明るさ

PaperMono のフロントライトは M5PM1 によって PWM 制御されます。明るさは M5.Display.setBrightness() で設定できます。設定範囲は 0 ~ 255 で、0 に設定するとフロントライトが消灯します。

M5.Display.setBrightness(255); // 0 ~ 255
M5.Display.setBrightness(0);   // 关闭前光
Page Tools
PDF
On This Page