ESP32でOLEDディスプレイ試す(I2C SSD1306 SCL SDAの設定方法)
0.96インチの有機ELディスプレイ(OLED)をAmazonで購入してみた
データシートや説明書が、ほとんどなかったが
ESP32-S3で無事に動作確認できたので、情報をまとめる
商品仕様
商品ページの説明は以下のみ
アンドロイド > Arduinoのこと?
STIM32/2 > STM32のこと?
ドライバーチップはSSD1306
色々、不安要素はあるが、ESP32向け(Arduino向け)のSSD1306ドライバがありそうなので、動くはず、ポチっ
データシート
参考となるデータシートを探す
以下の「秋月電子通商で販売しているOLEDディスプレイ」の「互換品」と考えて、以下のリンクにある参考のデータシートを閲覧する
「秋月電子通商の商品」も、販売している商品のそのものの資料ではなく、参考資料扱い
I2C通信のアドレスを探す
I2C通信するのに必要なアドレスの明記がない
自分でスキャンする必要がある
アドレスは0~127なので、全て試すのは大変ではない
スキャンした結果、購入したものは、0x3Cであった
Adafruitのライブラリのコメントには、128x32が0x3C、その他が0x3Dという記述がある
購入した商品は128x64だが、0x3C(Adafruit商品ではないので仕方ないが、文脈を理解しないと勘違いする)
アドレス変更用のスイッチ
商品の写真を見ると、抵抗の付け替えで、アドレスを変更できるような仕組みが基板にありそうだった
秋月電子通商の商品の写真にもありそう(Addressと書いてありそう)
しかし、届いたものには、写真にあるパターンが存在しなかった(残念)
チップ抵抗を持っていないので、やることもなかったかも
1つのMCUのI2CのBusで、2つのOLEDを利用したい場合は向いていない
ディスプレイ制御用のドライバ
SSD1306という記述があるので、SSD1306対応のドライバを使えば動きそう
上記のGitHubにESP32でテストして動作した(Tested Works)と記述がある
筆者は、これを見ないで購入してしまったが
ソースコード
付属のサンプルがてんこ盛りなので、
入門しやすいように、理解しやすいようにシンプルなサンプルを用意した(シンプルさを優先して、エラー処理を省いてる)
設定は以下の2行なので、理解してしまえば簡単
Adafruit_SSD1306 コンストラクタ
Adafruit_SSD1306.begin()メソッド
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS, false, false);:
以下のサンプルは、アルファベットを表示するコード(Arduino向け)
テキストを描画するメソッドtestdrawchar()を除けば、とても簡単だとわかる(4行)
コードの意味を次の項目で説明する
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
#define OLED_SCL 47
#define OLED_SDA 21
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET, 400000UL, 100000UL);
void testdrawchar(uint8_t offset = 0) {
display.clearDisplay();
display.display();
display.setTextSize(1); // Normal 1:1 pixel scale
display.setTextColor(SSD1306_WHITE); // Draw white text
display.setCursor(0, 0); // Start at top-left corner
display.cp437(true); // Use full 256 char 'Code Page 437' font
for (uint8_t i = 0; i < 170; i++)
display.write((uint8_t)((i + offset) % 26 + 65));// 65:A to 90:Z
display.display();
}
void setup()
{
Wire.begin(OLED_SDA, OLED_SCL);
display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS, false, false));
display.clearDisplay();
testdrawchar();
}
void loop()
{
}
SSD1306の設定
以下、OLEDディスプレイを動かすためのAdafruit SSD1306の設定についての情報
ディスプレイサイズの設定
Adafruit_SSD1306 のコンストラクタで指定
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
リセットピンの設定
Adafruit_SSD1306 のコンストラクタで指定
リセットピンはないので Adafruit_SSD1306 のコンストラクタの第4引数に-1を渡せばよさそう
以下、参考コード
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
周波数の設定
Adafruit_SSD1306 のコンストラクタで指定
デフォルト値の400kHzに設定(clkDuring)
ライブラリのコメントを読むと、ESP32のときは、800kHzも使われるという記述がある
SSD1306の通信後の周波数も設定できるようだが、デフォルト値のままにした(clkAfter)
他の低速なI2Cデバイスのために、SSD1306の通信後、周波数を変更すると思われる
今回は、SSD1306しか接続しないので無関係のはず
以下、参考コード、第5,6引数は何も渡さないと、デフォルト値になる
今は理解を深めるフェーズなので、あえて、明示的に指定するコードにした
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET, 400000UL, 100000UL);
Adafruit_SSD1306(uint8_t w, uint8_t h, TwoWire *twi = &Wire,
int8_t rst_pin = -1, uint32_t clkDuring = 400000UL,
uint32_t clkAfter = 100000UL);
SDAとSCLの設定
Adafruit_SSD1306 のコンストラクタで指定するが、少し設定が複雑
初心者にはハードルが高いと思う
筆者も入門したばかりだったら、自力では(解説サイトがなければ)辿りつけなかった気がする
まず、ESP32-S3の開発ボードのI2CのデフォルトのSDAとSCLのピン番号をみつけるのに苦労した
以下のページには、ESP32のデフォルトが書いてある(S3では違う可能性大)
Arduinoライブラリのソースを見てデフォルト値を探す
ボードによって異なる値になるような仕組みがある
例えば、ESP32-S3だと以下のファイルで各ピンが定義されている
variantsフォルダにピン番号が定義されたヘッダファイルが、各ボード毎にある
以下は、PlatformIOの場合のパス
Arduino IDE、IDFの場合のファイルの格納場所は、以前の記事で述べた
{install folder}\packages\framework-arduinoespressif32\variants\esp32s3\pins_arduino.h
static const uint8_t SDA = 8;
static const uint8_t SCL = 9;
「ボード名」と「フォルダ名」が完全に一致するわけではなさそうなのが厄介
「board = esp32-s3-devkitc-1」が、「esp32s3フォルダの中のヘッダファイル」が使われる、という理解でよいと思うが(確信が持てない)
PlatformIOのGo to Definition機能では、esp32s3フォルダに移動した
名前が一致しないということは、1対1の関係ではないのかもしれない
ESP32-S3(esp32s3フォルダにあるヘッダファイルが使われる環境)は、8と9ピンがデフォルトのようだ
実際に読み込まれる「pins_arduino.h」を確認する方法がわかっていない
実は、テスト時は、ここまで深く理解できておらず、デフォルト値を21と22と思っていて、自分のESP32-S3には22がない、と思ってしまった
情報を探した結果、「GPIO Matrix and Pin Mux」に行き着く
ESP32には「GPIO Matrix and Pin Mux」という仕組みがあるので、「任意のピンにI2CのSDAとSCLをアサインできる」
GPIO Matrix and Pin Mux
https://docs.espressif.com/projects/arduino-esp32/en/latest/tutorials/io_mux.html
上記のような背景があり、SDAとSCLを自分でアサインする方法の方が確実と思った
コンストラクタかdisplay.begin()メソッドの引数で設定できるのだろうと想像したのだが、設定項目がみつからない
どうやら、Wire.begin()で行う構成のようだ
そして、Wireの初期化を自分で行った旨をdisplay.begin()の引数で渡す仕組みのようだ
以下の第4引数の「periphBegin」をflaseにすると、SSD1306ライブラリ側でWireのbegin()メソッドを呼ばなくなる
periphBeginが何を意味しているのか?
periph = peripheral = ペリフェラル = 周辺機器をbeginするかどうか、のフラグ
peripheralが見る頻度が高くない単語の上に、periphと省略されていて、さらにWire.begin()のことを示す、という推測が必要だった
SPIとI2Cのことを抽象して表現しているのでわかりづらい(wireBeginならすぐピンとくるが、SPIの場合もあるので仕方ない)
以下のようなコードで、無事にI2CのSDAとSCLのピンを変更できた
ちなみに、第3引数は初期化時にリセットするかの設定(リセットピンの番号を-1にしているので無関係だが、一応、falseを渡している)
#define OLED_SCL 47
#define OLED_SDA 21
// 自分でピン番号を指定
Wire.begin(OLED_SDA, OLED_SCL);
// 第4引数の「periphBegin」で自動で、Wire.begin()が行われないようにする
display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS, false, false);
追記:
setPings()で、begin()前にピンを指定できるようだ
以下のGitHub上のIssueで、setPings()の追加について議論している
「ESP系以外のArduinoライブラリ」との関係性(互換性やマナーなど)を考える必要があるようだ
ESP32しか使っていないと、その点の視点が欠けてしまうので、なんで、こういう構成になってるだろうと、と思うことがあるかもしれないと、考えさせられた
また、「multiple invocations of Wire.begin() are supported.」(複数回、begin()を呼び出してもよい)とあるので、display.begin()の第4引数は、特に考えなくても動くかもしれない
#define OLED_SCL 47
#define OLED_SDA 21
Wire.setPins(OLED_SDA, OLED_SCL);
電源の設定
display.begin()で指定
電源の設定は、「外部電源」か「3.3Vから生成する」という2つのモードがあるようだ
サンプルにある「SSD1306_SWITCHCAPVCC」に設定した
3.3ボルトから、必要な電源を作っているという理解でよいのだろうか
display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS, false, false);
#define SSD1306_EXTERNALVCC 0x01 ///< External display voltage source
#define SSD1306_SWITCHCAPVCC 0x02 ///< Gen. display voltage from 3.3V
アドレスの設定
上記で探したアドレスを、display.begin()で指定
display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS, false, false));
初期化時にリセットするかの設定
繰り返しになるが、display.begin()の第3引数で、初期化時にリセットするかを指定する
display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS, false, false));
動作確認
設定と描画命令が正しければ、以下のような感じでOLEDディスプレイの表示される
(筆者のサンプルは訳があってアルファベットだけにしてしまったが…)
実際やるのは、苦労しなかったが
情報を整理して、まとめるのには苦労した
4ピンで配線が楽(しかもESP32なら、2ピンは任意のピンでOK)ちょっとしたデバッグ出力するのに便利そう!耐久性はまだ、わからないが、1個500円しないので試してみる価値はあると思う
試した環境
PlatformIO Core 6.1.16 / Home 3.4.4
framework-arduinoespressif32 3.20017
adafruit/Adafruit SSD1306@^2.5.13