u8g2ライブラリでSSD1306ディスプレイを操作
U8g2はArduinoなどで使えるグラフィックライブラリで、U8glibというライブラリの後継です。
対応しているのはSSD1608をはじめとしたモノクロディスプレイで、ILI9341やST7789などのカラーディスプレイはサポートしていません。
U8g2にはU8x8というライブラリも含まれています。こちらは機能が制限された軽量版です。
マニュアル: https://github.com/olikraus/u8g2/wiki/u8g2reference
使い方
Arduinoではライブラリマネージャでu8g2と検索すると見つかるのでインストールします。
U8g2ではディスプレイの種類やサイズをU8g2lib.hで定義されている大量のコンストラクタの中から適切なものを選ぶという方式になっています。
コンストラクタの名前には規則があり、今回の場合はSSD1306ディスプレイ、128x64px、メーカー不詳を、フルバッファを使用して、ハードウェアI2Cで描画するのでU8G2_SSD1306_128X64_NONAME_F_HW_I2Cです。
https://github.com/olikraus/u8g2/wiki/u8g2setupcpp#constructor-name
文字列の描画スタイルには複数の種類があるようです。いずれも\nとかで改行はできないらしい。
- drawStr(x, y, 文字列): 指定の座標(左下)に文字列を描画
- drawUTF8():
drawStrのUTF-8版、ascii範囲外の文字列を扱える - print(): 位置指定は
setCursor(x,y)を使う、ascii範囲外の文字を使うときはenableUTF8Print()
公式のサンプコード: https://github.com/olikraus/u8g2/tree/master/sys/arduino
Hello World!
コンストラクタ引数のU8G2_R0は画面の回転方向を表します。
アドレスの指定はsetI2CAddress()関数で行います。
#include <U8g2lib.h>
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0);
void setup() {
u8g2.begin();
u8g2.setFont(u8g2_font_9x15_tr);
u8g2.drawStr(0,15, "Hello World!");
u8g2.sendBuffer();
}
void loop() {}

カウントアップ
#include <U8g2lib.h>
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0);
void setup() {
u8g2.begin();
u8g2.setFont(u8g2_font_9x15_tr);
}
void loop() {
static int count = 0;
static char buffer[16];
sprintf(buffer, "%d", count++);
u8g2.clearBuffer();
u8g2.setCursor(20, 40);
u8g2.print("Count: ");
u8g2.print(count);
u8g2.sendBuffer();
delay(1000);
}
フォント
U8g2では標準で豊富なフォントが用意されています。setFont関数で指定します。
デカいフォントセットを使うとその分フラッシュを消費するので注意。
ただ、特に大きい日本語フォントでも150kB程度で、2MBのフラッシュを搭載したRaspberryPi Picoでは大した問題ではないかもしれません。
u8g2.setFont(u8g2_font_9x15_tf);
フォントリスト: https://github.com/olikraus/u8g2/wiki/fntgrp
日本語フォント
https://github.com/olikraus/u8g2/wiki/fntgrpefont
#include <U8g2lib.h>
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0);
void setup() {
u8g2.begin();
u8g2.setFont(u8g2_font_f16_t_japanese1);
u8g2.drawUTF8(10,25,"秋月電子通商");
u8g2.sendBuffer();
}
void loop() {}

アイコンフォント
アイコンフォントによってはグリフが数字0123...やアルファベット@abc...のコードポイントに割り当てられているのでprint("a")のようにしてアイコンを表示できます。
しかし、種類の多いアイコンフォントではよくわからん記号領域にも割り当てられているので、下のサンプルコードのように直接、文字コードを指定するのがいいと思います。
https://github.com/olikraus/u8g2/wiki/fntgrpstreamline
#include <U8g2lib.h>
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0);
void setup() {
u8g2.begin();
u8g2.setFont(u8g2_font_streamline_weather_t);
char icon[5] = {49, 53, 55, 48, 0};
u8g2.drawUTF8(20,40, icon);
u8g2.sendBuffer();
}
void loop() {}

追記:
drawGlyphという関数で1文字をコードポイントを直接指定して描画できるようです。
u8g2.drawGlyph(20,40, 49); # x, y, CodePoint
ピン指定
RaspberryPi Picoの場合、デフォルトのI2CピンはSDA=GPIO4, SCL=GPIO5です。
コンストラクタの引数でSCL, SDAのピンを設定できるようなので試したのですが、動きませんでした。
以下のIssueによれば、この方法が使えるのは現時点でESP32のみで、それ以外はWire.hでsetSDA,setSCLを使う必要があるようです。
https://github.com/olikraus/u8g2/issues/2289
#include <U8g2lib.h>
#include <Wire.h>
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0);
void setup() {
Wire.setSDA(0);
Wire.setSCL(1);
Wire.begin();
u8g2.begin();
u8g2.setFont(u8g2_font_9x15_tf);
u8g2.drawStr(0,15,"Hello World!");
u8g2.sendBuffer();
}
void loop() {}
U8x8
U8g2の軽量版、U8x8の方も使ってみました。
バッファを持たずディスプレイに直接描画するのでRAMの少ないマイコンに向いているそうです。
U8g2との違い
- 図形の描画はできない
- 使えるフォントが少ない
- 全て等幅フォント
- サイズは8の倍数
- 現時点では日本語フォントがない
- アイコンフォントはある
フォントリスト: https://github.com/olikraus/u8g2/wiki/fntlist8x8
サンプルコード
U8g2に含まれているのでライブラリの追加インストールは不要です。
drawString()がU8g2のdrawStr()に相当するもの。座標は8x8区切りのグリッドの位置
#include <U8x8lib.h>
U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE);
void setup() {
u8x8.begin();
u8x8.setFont(u8x8_font_7x14_1x2_r);
u8x8.drawString(0,0, "Hello World!");
}
void loop() {}
Hello Worldで比較したところU8g2よりRAM使用量が1kB(割合にして10%)ほど減りました。
もっと複雑な描画ならはっきりと差が出るかもしれません。