【コード付き】ESP32のI2C。I2CScanner と 0.56″ 7-Segmentの使い方。

ESP32-devkitのI2C Master側の動作を試しました。
本記事では下記3点を説明しています。

  • I2Cスキャナによるアドレス確認方法
  • 3.3V/5VのI2C機器との結線方法
  • 例として0.56″ 7-Segment backpackというデバイスの使い方

I2Cとは

I2C(アイ・スクエアード・シー。通称アイツーシー)は一言で言えばシリアル通信の一種です。
I2Cの主な特徴(メリット)は下記2つです。

  • 結線がクロック(SCL)とデータ(SDA)の2本で済む
  • アドレス管理をすることで複数のデバイスを1つのI2Cバス上に配置できる

Wikipedia上でのI2Cの説明は下記の通りです。

I2C(アイ・スクエアド・シー、アイ・アイ・シー)はフィリップス社で開発されたシリアルバスである。低速な周辺機器をマザーボードへ接続したり、組み込みシステム、携帯電話などで使われている。
Inter-Integrated Circuit の略で、I-squared-C(アイ・スクエアド・シー)が正式な読みとされている。ただし、一般的な文字コード環境のプレーンテキスト上では上付き文字が使えないため、I2CあるいはIICと表記されることも多く、これをもって「アイ・ツー・シー」と発声されたりカタカナ表記される[1]ことがある。

https://ja.wikipedia.org/wiki/I2C
https://ja.wikipedia.org/wiki/I2C

接続図の例は上記wikipedia参照の通りです。
VddへRp(プルアップ抵抗)をつけ忘れがちなので、注意が必要です。
(プルアップを忘れると、信号がなまってうまく通信できないことがあります:経験済み)

詳しく確認されたい場合は、NXPのサイトに「I2Cバス仕様およびユーザーマニュアル」がありますので、そちらの資料を参照ください。

https://www.nxp.com/docs/ja/user-guide/UM10204.pdf

ESP32でI2CScannerを試す

ここではESP32でI2CScannerというプログラムを使って、I2C機器のアドレスを調べる方法を共有します。

I2CScannerとは

I2C機器のアドレスを調べるプログラムのことです。
前述の通り、I2C機器はそれぞれにアドレスを割り当てて使用します。
I2CScannerは有効なアドレスを総当たりでI2Cのバスに問い合わせ、応答があったアドレスを表示します。
Slave機器のアドレスが不明な場合や、自分でアドレス変更結果を確認したい時に便利です。

ESP32用のI2CScannerサンプルコード

ESP32用のI2CScannerがEspressifから提供されています。
Arduinoのコードを改変するとしてもSerialのボーレートを変えるくらいですが、そもそもESP32用のコードがあるので素直にこちらを使いましょう。

サンプルコードの場所は下記です。
https://github.com/espressif/arduino-esp32/blob/master/libraries/Wire/examples/WireScan/WireScan.ino
(PlatformIOを利用の方はArduino.hのインクルードだけ忘れずに)

以降の項の説明で示すESP32-スレーブ機器の結線をしたのち、下記コードを実行すると、シリアルモニタ上にスレーブ機器のアドレスが表示されます。


#include <Arduino.h>
#include "Wire.h"

void setup() {
  Serial.begin(115200);
  Wire.begin();
}
void loop() {
  byte error, address;
  int nDevices = 0;

  delay(5000);

  Serial.println("Scanning for I2C devices ...");
  for(address = 0x01; address < 0x7f; address++){
    Wire.beginTransmission(address);
    error = Wire.endTransmission();
    if (error == 0){
      Serial.printf("I2C device found at address 0x%02X\n", address);
      nDevices++;
    } else if(error != 2){
      Serial.printf("Error %d at address 0x%02X\n", error, address);
    }
  }
  if (nDevices == 0){
    Serial.println("No I2C devices found");
  }
}

ESP32とSlave機器のI2C接続(SCL/SDAポート)

ESP32には2系統のI2Cが用意されています。
ESP32-devkitの場合、1系統はデフォルトで下記のI/Oに割り当てられています。

  • SDA: GPIO21
  • SCL: GPIO22

その他のESP32機器でも基本的に上記GPIO21/22にI2Cが割り当てられています(下記参照)。
ただし、利用にあたってはそれぞれのリファレンスで一応IOを確認することを推奨します。

The default pins may vary from board to board. On the Generic ESP32 the default I2C pins are:
・sdaPin GPIO21
・sclPin GPIO22

https://docs.espressif.com/projects/arduino-esp32/en/latest/api/i2c.html

ESP32は3.3V動作のマイコンのため、3.3Vで動作するI2C機器の場合は上記ポートにI2C機器を接続すればOKです(必要に応じて外部のプルアップ抵抗もつけます)。
5V動作のI2C機器をESP32と接続する場合は3.3V-5Vのレベルシフタを併用する必要があります。

なお、I2C機器はSCL/SDAを結線するだけで手軽に動作しますが、下記2点の注意が必要です。

  • I2C機器の動作電圧を確認する必要がある。
  • SCL/SDAともVddへプルアップしておかないと、正しく信号が送受信できないことがある

3.3Vで動くI2C(Slave)機器を扱う場合の結線

ESP32はI2Cのマスターモードで設定すると内部プルアップをしているようなので、基本的にプルアップ抵抗はなくてもI2C機器を接続できます。
ただし、内蔵のプルアップの抵抗値はかなり高いです。
波形の歪みを見るなどして確認し、必要に応じて10kΩ程度の外部プルアップを追加すると良いかと思います。

下の写真は結線の例で、SDL/SCLとVdd/GNDの4線を接続することで動作しています。(内蔵Pullupのみ使用)

ESP32 3.3VのI2C機器との結線の例

ESP32のI2Cバスに対してデフォルトでプルアップされている根拠。

ESP32のI2Cバスのプルアップの根拠は下記です。
ドライバ層以下のコードが公開されておらず追いきれませんでしたが、これでPullupされてなかったらかなりお恥ずかしいコードなので、大丈夫でしょう。

参照したコードはhttps://github.com/espressif/arduino-esp32 v2.0.3です。

以下、ソースコード抜粋です。

// Master Begin
bool TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency)

の中で、下記が呼ばれています。

esp_err_t i2cInit(uint8_t i2c_num, int8_t sda, int8_t scl, uint32_t frequency)

さらにesp_err_t i2cInit()にて下記コードが下位のドライバ層に渡されています。

    i2c_config_t conf = { };
    conf.mode = I2C_MODE_MASTER;
    conf.scl_io_num = (gpio_num_t)scl;
    conf.sda_io_num = (gpio_num_t)sda;
    conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
    conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
    conf.master.clk_speed = frequency;
    conf.clk_flags = I2C_SCLK_SRC_FLAG_FOR_NOMAL; //Any one clock source that is available for the specified frequency may be choosen

5Vで動くI2C機器とつなぐ場合の結線

5Vで動作する機器をESP32につなぐ場合、3.3V-5Vのレベルシフタが必要です。
私はAmazonで下記のようなデバイスを購入しました。:アマゾンの商品リンク:I2Cロジックレベル変換

写真の通り、4線のレベル変換が可能です。I2Cなら2系統使えます。
High側/Low側とも10kでプルアップされているので、追加のプルアップが不要な点はありがたいです。

I2C レベル変換器

上記レベルシフタを利用した結線が下記になります。

ESP32 5VのI2C機器との結線の例

左上の黒いモジュールは3.3V/5V MB102ブレッドボード電源モジュールです。:アマゾンリンク
写真では少々線が多く煩雑に見えますが、レベル変換器の上を5V、下を3.3Vとして、SDA/SCL GND/Vddをそれぞれ結線しているのみです。

ESP32で0.56″ 7-Segment Backpackを使う。

これまでの写真で登場している0.56″ 7-Segment Backpackを実際に使ってみます。
このモジュールはセカンドソース品が多数出ているようで、Amazonなどでも安価に購入できます。

使い方はこちらのadafruitのサイトに詳しいので、これを順に実施します。
ただし、adafruitのサイトはArduinoIDEでの説明となっていましたので、以下はPlatformIOでの説明を記載します。

0.56″ 7-Segment Backpackの結線。5V?3.3V?

先のサイトの記載によると、「電源は5Vがベストだけど、3Vでも動くだろうね」のように書かれています。実際に試すと5V、3.3Vどちらでも使えますのでお好みで結線してください。

Connect VCC+ to power – 5V is best but 3V also seems to work for 3V microcontrollers.

https://learn.adafruit.com/adafruit-led-backpack/0-dot-56-seven-segment-backpack-arduino-setup

0.56″ 7-Segment Backpackに必要なライブラリをインストールする

Adafruit LED Backpack と Adafruit GFX ライブラリをインストールすればOKです。
以下スクリーンショットをご参考ください。

Libraries画面で検索窓にAdafruit LED Backpackを入力し、下記のようにリストされた項目をクリックします。

続いて、Add to Project で利用するプロジェクトに追加すればOKです。

Adafruit GFXも同様に追加します。

サンプルプログラム sevenseg.ino が下記にありますので、これをmain.cppにコピーします。

下記はmain.cppにコピーした後、ESP32のPlatformIO環境で動作するようにしたコードです。

#include <Arduino.h>

/***************************************************
  This is a library for our I2C LED Backpacks

  Designed specifically to work with the Adafruit LED 7-Segment backpacks
  ----> http://www.adafruit.com/products/881
  ----> http://www.adafruit.com/products/880
  ----> http://www.adafruit.com/products/879
  ----> http://www.adafruit.com/products/878

  These displays use I2C to communicate, 2 pins are required to
  interface. There are multiple selectable I2C addresses. For backpacks
  with 2 Address Select pins: 0x70, 0x71, 0x72 or 0x73. For backpacks
  with 3 Address Select pins: 0x70 thru 0x77

  Adafruit invests time and resources providing this open source code,
  please support Adafruit and open-source hardware by purchasing
  products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries.
  BSD license, all text above must be included in any redistribution
 ****************************************************/

#include <Wire.h> // Enable this line if using Arduino Uno, Mega, etc.
#include <SPI.h>
#include <Adafruit_GFX.h>
#include "Adafruit_LEDBackpack.h"

Adafruit_7segment matrix = Adafruit_7segment();

void setup()
{
#ifndef __AVR_ATtiny85__
  Serial.begin(9600);
  Serial.println("7 Segment Backpack Test");
#endif
  matrix.begin(0x70);
}

void loop()
{
  // try to print a number thats too long
  matrix.print(10000, DEC);
  matrix.writeDisplay();
  delay(500);

  // print a hex number
  matrix.print(0xBEEF, HEX);
  matrix.writeDisplay();
  delay(500);

  // print a floating point
  matrix.print(12.34);
  matrix.writeDisplay();
  delay(500);

  // print a string message
  matrix.print("7SEG");
  matrix.writeDisplay();
  delay(10000);

  // print with print/println
  for (uint16_t counter = 0; counter < 9999; counter++)
  {
    matrix.println(counter);
    matrix.writeDisplay();
    delay(10);
  }

  // method #2 - draw each digit
  uint16_t blinkcounter = 0;
  boolean drawDots = false;
  for (uint16_t counter = 0; counter < 9999; counter++)
  {
    matrix.writeDigitNum(0, (counter / 1000), drawDots);
    matrix.writeDigitNum(1, (counter / 100) % 10, drawDots);
    matrix.drawColon(drawDots);
    matrix.writeDigitNum(3, (counter / 10) % 10, drawDots);
    matrix.writeDigitNum(4, counter % 10, drawDots);

    blinkcounter += 50;
    if (blinkcounter < 500)
    {
      drawDots = false;
    }
    else if (blinkcounter < 1000)
    {
      drawDots = true;
    }
    else
    {
      blinkcounter = 0;
    }
    matrix.writeDisplay();
    delay(10);
  }
}

改変箇所は下記3点になります。

1行目: arduino.hをインクルード

26行目:SPI.hをインクルード(使ってないはずですが・・・)

38行目:I2CScannerで確認したアドレスに変更

まとめ

以上、本記事では下記3点について解説しました。

  • I2Cスキャナによるアドレス確認方法
  • 3.3V/5VのI2C機器との結線方法
  • 例として0.56″ 7-Segment backpackというデバイスの使い方

ESP32をI2CのMaster/Slaveモードで動かす際の詳細までは本記事では触れませんでしたので、下記も続けてご参照いただければと思います。

その他のESP32でのI2Cの利用方法について、下記にまとめました。合わせてご参照ください

以上、ご参考になれば幸いです。

ぼのかば

最初にプログラムしたのは相撲ロボット用のZ80アセンブリ(Aki-80)。
以後,PICアセンブリ>PIC C と渡り歩く。

好きなICのタイプは8pinDIP。

一応、電気科卒
*ブログ内の情報はMacOSX上での操作に基づきます。

ESP32
PICを少々。