M5Stack CoreInkの使い方 その1 公式ライブラリ概要

概要

前回はざっくりと商品を紹介しましたが、もうすこし触ってみたので使い方の説明をしていきたいと思います。

描画について

公式ライブラリは独自で作成したもので、描画周りの関数があまり実装されていません。

M5.M5Inkクラス

M5Stack CoreInkで採用している電子ペーパーを制御するクラスです。このクラスでは基本的に描画関数がなく、画面全体の画像データを転送すると描画できるクラスと思ってください。その他電子ペーパーの制御をしています。

Ink_Spriteクラス

電子ペーパーの実際の描画を担当するスプライトクラスです。

creatSprite(posX, posY, width, height, copyFromMem):スプライト作成

サイズを指定してスプライトを作成します。

// グローバル変数でクラスを宣言
Ink_Sprite InkPageSprite(&M5.M5Ink);
// Setup関数で実際に確保
InkPageSprite.creatSprite(0, 0, 200, 200, true);

上記のように利用します。引数は上記のように画面全体を作成する(0, 0, 200, 200, true)で問題ないと思います。最後の引数は画面に表示されている内容を読み出すフラグですが、起動直後は真っ白のためtrueでもfalseでも同じだと思います。

creatSprite()関数はcreateSpriteではなく、eがないので注意してください。(おそらくTypoです)

その他の関数群

// 全体を白で塗りつぶす
InkPageSprite.clear();
// 指定した座標に点を打つ(0:黒, 1:白)
InkPageSprite.drawPix(posX, posY, pixBit);
// 指定した座標を四角く塗りつぶす(0:黒, 1:白)
InkPageSprite.FillRect(posX, posY, width, height, pixBit);
// 指定した座標に画像データを転送する
InkPageSprite.drawBuff(posX, posY, width, height, imageDataptr);
// 指定した座標に文字を描画する
InkPageSprite.drawString(posX, posY, charData, &AsciiFont8x16);
// スプライトの内容を電子ペーパーに描画する
pushSprite();

主に使いそうな関数群です。描画し終わったところでpushSprite()で実際の電子ペーパーに転送して表示します。電子ペーパーの特徴として、白がデフォルトです。クリアすると白(1)になり、文字などは黒(0)を描画するイメージとなります。

画像描画

画像ファイルを変換する関数などはありませんので、実際に描画するデータを準備して流し込むことが必要になります。変換にはこのブログの上のメニューにある「TOOLS」から「画像データ変換」を開くとブラウザ上で変換するページが開くと思います。

まずは素材となる画像を準備します。電子ペーパーは200×200ですのでその大きさでまずは準備したいと思います。

とりあえずいらすとやさんから画像を拝借。200×200に縮小してから、エッジ検出を使ってその後モノクロ2色に減色しています。

上記みたいになりました。

このブログの上のメニューにある「TOOLS」から「画像データ変換」を開き、先程の画像ファイルを選択して、データ名を適当に指定し、データ形式は1Bitを選び送信をすると下のテキストエリアに変換したデータが出てきます。現在パレットデータが変換できないので、ペイントなどで開いてからPNGなどに名前をつけて保存をすると読み込める形式になると思います。

上記が変換結果です。2進数でデータを変換かけていますのでドット単位でどんなデータかがわかります。ちょっと文字数が多いのが難点ですがコンパイルすれば問題ありません。

実際の使い方のスケッチは上記にあります。

#include "M5CoreInk.h"
Ink_Sprite InkPageSprite(&M5.M5Ink);
extern const unsigned char img[5000];
unsigned char imgBuff[5000];
void setup() {
  M5.begin();
  M5.M5Ink.isInit();
  M5.M5Ink.clear();
  InkPageSprite.creatSprite(0, 0, 200, 200, false);
  // 描画テスト
  memcpy(imgBuff, img, sizeof(imgBuff));
  InkPageSprite.drawBuff(0, 0, 200, 200, imgBuff);
  InkPageSprite.pushSprite();
}

抜粋すると、上記のコードになります。creatSprite()で200×200のスプライトを作成します。その後drawBuff()で転送するのですが、drawBuff()関数の引数はconstがついていないので、そのまま渡せません、、、なので一度constがついていない変数にコピーしています。

データをフラッシュ領域ではなく、1画像5KBなのでconstを外してオンメモリ領域にいれればコピーする必要はありません。とはいえ、これは公式ライブラリの引数にconstを追加したほうが筋がいいと思います。

テキスト描画

現状英数のみのフォントが内蔵されています。

  • AsciiFont8x16
  • AsciiFont24x48

上記の2種類です。

   /* 0x00 [   ] [ 0 ] */ 
   /* 0x01 [ ! ] [ 1 ] */ 
   /* 0x02 [ " ] [ 2 ] */ 
   /* 0x03 [ # ] [ 3 ] */ 
   /* 0x04 [ $ ] [ 4 ] */ 
   /* 0x05 [ % ] [ 5 ] */ 
   /* 0x06 [ & ] [ 6 ] */ 
   /* 0x07 [ ' ] [ 7 ] */ 
   /* 0x08 [ ( ] [ 8 ] */ 
   /* 0x09 [ ) ] [ 9 ] */ 
   /* 0x0A [ * ] [ 10 ] */ 
   /* 0x0B [ + ] [ 11 ] */ 
   /* 0x0C [ , ] [ 12 ] */ 
   /* 0x0D [ - ] [ 13 ] */ 
   /* 0x0E [ . ] [ 14 ] */ 
   /* 0x0F [ / ] [ 15 ] */ 
   /* 0x10 [ 0 ] [ 16 ] */ 
   /* 0x11 [ 1 ] [ 17 ] */ 
   /* 0x12 [ 2 ] [ 18 ] */ 
   /* 0x13 [ 3 ] [ 19 ] */ 
   /* 0x14 [ 4 ] [ 20 ] */ 
   /* 0x15 [ 5 ] [ 21 ] */ 
   /* 0x16 [ 6 ] [ 22 ] */ 
   /* 0x17 [ 7 ] [ 23 ] */ 
   /* 0x18 [ 8 ] [ 24 ] */ 
   /* 0x19 [ 9 ] [ 25 ] */ 
   /* 0x1A [ : ] [ 26 ] */ 
   /* 0x1B [ ; ] [ 27 ] */ 
   /* 0x1C [ < ] [ 28 ] */ 
   /* 0x1D [ = ] [ 29 ] */ 
   /* 0x1E [ > ] [ 30 ] */ 
   /* 0x1F [ ? ] [ 31 ] */ 
   /* 0x20 [ @ ] [ 32 ] */ 
   /* 0x21 [ A ] [ 33 ] */ 
   /* 0x22 [ B ] [ 34 ] */ 
   /* 0x23 [ C ] [ 35 ] */ 
   /* 0x24 [ D ] [ 36 ] */ 
   /* 0x25 [ E ] [ 37 ] */ 
   /* 0x26 [ F ] [ 38 ] */ 
   /* 0x27 [ G ] [ 39 ] */ 
   /* 0x28 [ H ] [ 40 ] */ 
   /* 0x29 [ I ] [ 41 ] */ 
   /* 0x2A [ J ] [ 42 ] */ 
   /* 0x2B [ K ] [ 43 ] */ 
   /* 0x2C [ L ] [ 44 ] */ 
   /* 0x2D [ M ] [ 45 ] */ 
   /* 0x2E [ N ] [ 46 ] */ 
   /* 0x2F [ O ] [ 47 ] */ 
   /* 0x30 [ P ] [ 48 ] */ 
   /* 0x31 [ Q ] [ 49 ] */ 
   /* 0x32 [ R ] [ 50 ] */ 
   /* 0x33 [ S ] [ 51 ] */ 
   /* 0x34 [ T ] [ 52 ] */ 
   /* 0x35 [ U ] [ 53 ] */ 
   /* 0x36 [ V ] [ 54 ] */ 
   /* 0x37 [ W ] [ 55 ] */ 
   /* 0x38 [ X ] [ 56 ] */ 
   /* 0x39 [ Y ] [ 57 ] */ 
   /* 0x3A [ Z ] [ 58 ] */ 
   /* 0x3B [ [ ] [ 59 ] */ 
   /* 0x3C [ \ ] [ 60 ] */ 
   /* 0x3D [ ] ] [ 61 ] */ 
   /* 0x3E [ ^ ] [ 62 ] */ 
   /* 0x3F [ _ ] [ 63 ] */ 
   /* 0x40 [ ` ] [ 64 ] */ 
   /* 0x41 [ a ] [ 65 ] */ 
   /* 0x42 [ b ] [ 66 ] */ 
   /* 0x43 [ c ] [ 67 ] */ 
   /* 0x44 [ d ] [ 68 ] */ 
   /* 0x45 [ e ] [ 69 ] */ 
   /* 0x46 [ f ] [ 70 ] */ 
   /* 0x47 [ g ] [ 71 ] */ 
   /* 0x48 [ h ] [ 72 ] */ 
   /* 0x49 [ i ] [ 73 ] */ 
   /* 0x4A [ j ] [ 74 ] */ 
   /* 0x4B [ k ] [ 75 ] */ 
   /* 0x4C [ l ] [ 76 ] */ 
   /* 0x4D [ m ] [ 77 ] */ 
   /* 0x4E [ n ] [ 78 ] */ 
   /* 0x4F [ o ] [ 79 ] */ 
   /* 0x50 [ p ] [ 80 ] */ 
   /* 0x51 [ q ] [ 81 ] */ 
   /* 0x52 [ r ] [ 82 ] */ 
   /* 0x53 [ s ] [ 83 ] */ 
   /* 0x54 [ t ] [ 84 ] */ 
   /* 0x55 [ u ] [ 85 ] */ 
   /* 0x56 [ v ] [ 86 ] */ 
   /* 0x57 [ w ] [ 87 ] */ 
   /* 0x58 [ x ] [ 88 ] */ 
   /* 0x59 [ y ] [ 89 ] */ 
   /* 0x5A [ z ] [ 90 ] */ 
   /* 0x5B [ { ] [ 91 ] */ 
   /* 0x5C [ | ] [ 92 ] */ 
   /* 0x5D [ } ] [ 93 ] */ 
   /* 0x5E [ ~ ] [ 94 ] */

上記の94文字が収録されています。

日本語の描画はこのままではできません。次回以降にefontやLovyanGFXを利用した拡張描画方法を紹介したいと思います。

未調整の先出しになりますが、上記のようにLovyanGFXのスプライトを作成して、描画したあとに転送するのが楽だと思います。LovyanGFX自体で直接描画することもできますが、その場合には公式ライブラリとの整合性が問題になります。

リアルタイムクロック(RTC)について

M5StickC、M5Stack Core2などと同じBM8563が搭載されています。使い方はM5StickCなどと同じなのですが、、、クラス名が違っています(涙)

M5.rtcクラス

他のボードはM5.RtcなのでTypoな気がします。そのうちdefineでRtcに変更してくれないかな、、、

特徴として、M5StickCのときにも機能的にはあった通知が大幅に強化されています。RTCで特定の時間になったら通知イベントを発生するのはM5StickCでも可能だったのですが、あまり有効利用できませんでした。M5Stack Core2から通知が電源ボタンに接続されており、ディープスリープではなく、電源OFF状態から指定時間に起動できるようになっています。

とはいえ、現在リリースされている0.0.1バージョンのライブラリでは利用することができません。GitHubの最新バージョンを使うことで試すことができます。こちらも新バージョンがリリースされてから解説をしたいと思っています。

ボタンについて

M5Stack CoreInkはこれまでで一番ボタンを搭載しています。

ボタンクラスGPIO
多機能ボタン上BtnUP37
多機能ボタン押し込みBtnMID38
多機能ボタン下BtnDOWN39
上ボタンBtnEXT5
電源ボタンBtnPWR27
リセットボタン(裏側)

特徴的なのが上下に移動ができる多機能ボタンですね。GPIO的にはバネが入っていて元に戻る3ボタンの動作になります。

気をつけないといけないのが電源ボタンです。電源ボタンと命名されていますが、電源ONのトリガーボタンになります。電源OFFの状態から押すことによって電源ONになりますが、起動したあとは普通のボタンとして利用できます。電源OFFをしたい場合などについては、このボタンを長押しても、プログラムで設定しない限り電源OFFになりません。

電源について

ちょっと特殊なので、かんたんに解説したいと思います。

  • USBの電源接続
  • 電源ボタンを押す
  • RTCのタイマー通知
  • GPIO12がHIGH

上記の4つの条件のどれかがONになると電源ONになります。そして、この4条件がすべてOFFになった場合に電源OFFになります。

つまりUSBに接続されている状態では絶対に電源OFFになりません。これわかりにくいので注意してください。また、電源ONになったところで、公式ライブラリはGPIO12をHIGHに設定しています。つまりGPIO12をLOWに設定しない限り電源OFFになりません。GPIO12を明示的にLOWに設定するか、リセットボタンを押してESP32を再起動することでLOWになり電源OFFになります。

M5Stack CoreInkのスリープ動作は、USBに接続されていない状態でRTCにタイマー設定をしてから、GPIO12をLOWにすることで電源OFFになります。そしてRTCのタイマー通知を使って起動する仕組みになっています。スリープではなく電源OFFからのタイマー起動なので消費電力を非常に抑えることが可能です。

単に電源OFFにしたい場合には、USB接続を抜いてからリセットボタンを押すか、電源ボタンなどにプログラムでGPIO12をLOWにするコードを書いておく必要があります。

ブザーについて

M5Stack CoreInkにはブザーがついています。しかしながらDACではなく普通のGPIOなのでPWMでのブザーにしか利用できません。

M5.Speakerクラス

内容的にはM5StickC PlusのM5.Beepクラスのはずですが、M5Stack系のSpeakerとなっています。この辺はどう統一していくのだろう。M5StickC Plusが特殊だった気もします。。。

使い方はこれまでと同じです。

    M5.Speaker.tone(2700);
    delay(100);
    M5.Speaker.mute();

単発はtone()関数で音を指定して鳴らして、指定秒数後にmute()で止めます。

// 音の高さと長さを指定
M5.Speaker.tone(2700, 200);
// loop()の中で適度に呼び出すと指定の長さでmute()をしてくれる
M5.Beep.update();

自動的に止めるためには、loop()の中でM5.Beep.update()を定期的に呼び出します。そうすると音がなっている場合には、指定秒数経過していたらmute()をしてくれる処理があります。

とはいえ、loop()が回ってきたタイミングで止めるので時間精度は結構適当です。

まとめ

現状の公式ライブラリ0.0.1はタイマー起動周りが使えないので、M5Stack CoreInkの性能を活かすことができません。なるべくならばGitHubから最新版を落として入れ替えたほうがいいのですが、まずはリリースバージョンでの解説をしたいと思います。

ライブラリはまだ荒削りですが、ハードウエアは非常に成熟していると思います。ちょっと値段が高いのですが、電子ペーパーの価格がありますので仕方ないですね。

続編

コメント