基礎 · 読了まで14分

I2Cとは?SDA、SCL、プルアップ抵抗とArduinoのI2Cスキャナー

I2Cとは?そのしくみは?SDAとSCL、7ビットアドレス、プルアップ抵抗の計算、I2Cスキャナーのスケッチ、アドレスの衝突、Uno、ESP32、Picoのピンを解説します。

I2C(Inter-Integrated Circuit)は、マイコンがわずか2本の信号線(SDAとSCL)で何十ものセンサー、ディスプレイ、モジュールと通信できるシリアル通信プロトコルです。「アイスクエアドシー」と読み、データシートによってはIICやTWI(Two Wire Interface)とも呼ばれます。Arduinoのプロジェクトで使うI2C LCD、OLEDディスプレイ、MPU6050加速度センサー、BMP280気圧センサー、DS1307時計モジュールは、どれもこのプロトコルで通信します。すべてを同じ2本の線に並列につなぎ、それぞれ固有のアドレスで呼び出せます。

このガイドでは、I2Cが電気的にどう動くか、アドレスの意味、プルアップ抵抗が欠かせない理由とその値の計算方法を学びます。続いて、Arduino Uno、ESP32、Raspberry Pi Picoのピン表、バス上のすべてのデバイスを見つけるI2Cスキャナーのスケッチ、ライブラリを使わないレジスタの読み取り例、アドレスの衝突などのよくあるトラブルの対処法をステップごとに見ていきます。

PCF8574チップを使ったI2C LCDアダプターモジュール
I2C LCDアダプター:LCDの裏にはんだ付けされたPCF8574チップが、16ピンのLCDをたった4本の線(GND、VCC、SDA、SCL)にまとめます。アドレスはたいてい0x27か0x3Fです。

I2Cとは?そのしくみ

I2Cは1982年にPhilips(現在のNXP)が、同じ基板上のチップどうしを少ない配線で通信させるために開発しました。バスには2つの役割があります。すべての転送を開始してクロックを作るコントローラー(以前はマスターと呼ばれていました)と、それに応答するターゲット(以前のスレーブ)です。Arduinoのプロジェクトでは、コントローラーはほぼ必ずArduino、ESP32、Picoで、センサーやディスプレイがターゲットです。

  • SDA(Serial Data):データを双方向に運ぶ線です。コントローラーがアドレスとコマンドを送り、センサーは同じ線で読み取り値を返します。
  • SCL(Serial Clock):クロックの線です。クロックパルスごとにSDAの1ビットが読まれ、クロックはコントローラーが作ります。
  • GND(共通グランド):信号の基準です。電源が別々のモジュールでも、GNDは共通にしなければなりません。

ラインはオープンドレインです。どのデバイスもラインをHIGHに駆動することはなく、LOWに引き下げることしかできません。ラインを1に保っているのは、SDAとSCLを電源電圧につなぐプルアップ抵抗です。そのため、2つのデバイスが同時に話そうとしても短絡は起こりません。どれか1つのデバイスがラインをLOWにすれば、全員に0が見えます。I2Cのメッセージは次のように進みます。

  1. START:SCLがHIGHの間にコントローラーがSDAを1から0に下げると、バス上のすべてのデバイスが聞き始めます。
  2. アドレスと方向ビット:7ビットのアドレスが送られ、続いて読み出し(1)か書き込み(0)のビットが送られます。
  3. ACK:そのアドレスを持つデバイスが9番目のクロックパルスの間にSDAをLOWにして「ここにいます」と伝えます。誰も応答しなければNACKと呼ばれます。
  4. データバイト:各バイトは8ビットで送られ、受信側は1バイトごとにACKで応えます。ほとんどのセンサーでは、最初のバイトは読み書きするレジスタの番号です。
  5. STOP:SCLがHIGHの間にSDAが0から1に上がると、バスは再び空きます。読み出しでは、バスを手放さずに方向を変えられるよう、STOPの代わりにリピートSTARTを使います。

Arduinoの世界でよく使われるほかの2つのプロトコルと比べると、I2Cを選ぶべき場面がはっきりします。

項目I2CSPIUART(シリアル)
信号線2本(SDA、SCL)共通の3本(MOSI、MISO、SCK)+デバイスごとにCS 1本2本(TX、RX)
デバイスの数1本のバスに100以上のアドレス空いているCSピンの数だけふつうは2台
一般的な速度100 kHz、400 kHz、1 MHz10 MHz以上9600〜115200 baud
デバイスの選択アドレスでCSピンでなし、1対1
主な用途センサー、時計、小型ディスプレイ、EEPROMSDカード、TFTディスプレイ、RFIDGPS、Bluetooth、パソコンとの接続

I2Cの速度と技術的な制約

モード最大速度最大立ち上がり時間使われる場面
スタンダードモード(Sm)100 kHz1000 ns初期設定、長い配線
ファストモード(Fm)400 kHz300 nsOLED、MPU6050、BMP280
ファストモードプラス(Fm+)1 MHz120 nsPicoや一部の新しいセンサー
ハイスピードモード(Hs)3.4 MHz80 ns(400 pF)、専用のドライバが必要電子工作ではまれ

ArduinoのWireライブラリは初期設定では100 kHzで始まります。ほとんどのセンサーは400 kHzにも対応しており、Wire.setClock(400000)で切り替えられます。速度を上げるとディスプレイの更新はなめらかになりますが、バスは配線の長さやプルアップ抵抗の値に敏感になります。規格では、バス全体の静電容量(配線とデバイスのピンの合計)は400 pFを超えてはいけません。一般的なジャンパーワイヤーやリボンケーブルは1メートルあたり50〜100 pF、デバイスのピンは1本あたり最大10 pFを加えるため、I2Cは1枚の基板上か、数十センチの配線で使うように設計されています。1メートルを超えるなら、速度を下げるか、より強いプルアップ抵抗を使うか、P82B715のようなバスエクステンダーを加える必要があります。

I2Cアドレス:7ビット、8ビット、予約アドレス

バス上のデバイスにはそれぞれ7ビットのアドレスがあり、理論上は128個のアドレスになります。0x00〜0x07と0x78〜0x7Fの範囲は特別な用途に予約されているため、使えるのは0x08〜0x77の112個です。アドレスはふつうチップのメーカーが決めていますが、多くのモジュールではA0、A1、A2、AD0と呼ばれるはんだジャンパーやピンで下位のビットを変えられます。

ここがややこしいところです。データシートやモジュールの裏の表示の中には、方向ビットを含めた8ビットでアドレスを書いているものがあります。たとえば、OLED基板の裏に「0x78」とあっても、コードでのアドレスは0x3Cです。LCDモジュールに0x4Eとあれば、コードでのアドレスは0x27です。ルールは簡単で、8ビットの値を2で割る(1ビット右にシフトする)と7ビットのアドレスになります。Arduino、ESP32、MicroPythonのライブラリはどれも7ビットのアドレスを使います。

モジュールチップ初期アドレス変更できる?
I2C LCDモジュール(16x2、20x4)PCF8574 / PCF8574A0x27 / 0x3FA0〜A2のジャンパーで0x20〜0x27または0x38〜0x3F
0.96インチOLEDSSD13060x3C裏の抵抗で0x3D
加速度・ジャイロセンサーMPU60500x68AD0 = 1で0x69
リアルタイムクロックDS1307 / DS32310x68不可
気圧と温度BMP280 / BME2800x76または0x77SDOピンによる
照度BH17500x23ADDR = 1で0x5C
16ビットADCADS11150x48ADDRピンで0x48〜0x4B
16チャンネルのサーボ・PWMドライバPCA96850x40A0〜A5のジャンパーで0x40から数十のアドレス
EEPROMメモリAT24C32 / AT24C2560x50A0〜A2で0x50〜0x57
I2CマルチプレクサTCA9548A0x70A0〜A2で0x70〜0x77
Tiny RTC(DS1307)モジュールをスキャンすると、0x68の隣にアドレス0x50が表示されます。これはエラーではなく、モジュールに載っているAT24C32 EEPROMです。DS3231を使ったZS-042モジュールでは、同じメモリが0x57で応答します。

プルアップ抵抗:なぜ必要か、値の選び方

I2Cバス上のデバイスはラインをLOWに引き下げることしかできないため、HIGHのレベルはプルアップ抵抗が作ります。抵抗が大きすぎると、バスの静電容量のせいで0から1への変化が遅くなり、信号のエッジがなまって、高速ではビットを読み間違えます。小さすぎると、ラインをLOWに引き下げるときにデバイスが吸い込む電流が増えます。規格がデバイスに求めているのは0.4 Vで3 mA以上を吸い込めることだけなので、プルアップ抵抗は電流をそれ以下に抑える必要があります。

次の式で2つの限界がわかります。最小の抵抗はR(min) = (VCC − 0.4 V) / 3 mA、最大はR(max) = t(rise) / (0.8473 × C(bus))です。5 VではR(min) = 4.6 V / 0.003 A ≈ 1.53 kΩ、3.3 Vでは≈ 0.97 kΩです。静電容量が100 pFの短いバスでは、100 kHzでR(max) ≈ 11.8 kΩ、400 kHzで≈ 3.5 kΩです。そのため実際には、100 kHzなら4.7 kΩ、400 kHzやデバイスの多いバスなら2.2 kΩがもっともよく使われる値です。抵抗の帯を読むには抵抗カラーコードツールを、抵抗と静電容量による遅れを確かめるにはRC時定数ツールを使えます。

状況推奨プルアップ抵抗備考
モジュール1つ、短い配線、100 kHz4.7 kΩ〜10 kΩほとんどのモジュールにすでに付いている
複数のモジュール、400 kHz2.2 kΩ〜4.7 kΩモジュールの抵抗は並列になる
50 cmより長い配線2.2 kΩで100 kHzSDAとSCLをより合わせず、それぞれをGNDとより合わせる
3.3 Vのバス(ESP32、Pico)2.2 kΩ〜4.7 kΩ抵抗は5 Vではなく3.3 Vにつなぐ
ほとんどのモジュールにはすでに4.7 kΩか10 kΩのプルアップ抵抗が付いていて、同じバス上のモジュールの抵抗は並列になります。3つのモジュールにそれぞれ4.7 kΩが付いていれば、合計は約1.57 kΩで、5 Vでは3 mAの上限をわずかに下回る程度です。4つや5つのモジュールを組み合わせるなら、一部のモジュールのプルアップ抵抗を外すか、はんだジャンパーを切る必要があるかもしれません。

Arduino Unoでは、Wire.begin()を呼ぶとATmega328Pの20〜50 kΩの内部プルアップ抵抗も有効になります。モジュール1つで配線が10〜20 cmなら動くことも多いのですが、信頼できるものではなく、外付けの抵抗のほうが常に確実です。プルアップ抵抗のないむき出しのセンサー基板を使うなら、SDAとSCLの両方からVCCに4.7 kΩの抵抗を入れるのを忘れないでください。

I2Cのピン:Arduino Uno、Mega、ESP32、ESP8266、Pico

ボードSDASCL備考
Arduino Uno / NanoA4A5Uno R3では、AREFの隣のSDAとSCLのピンはA4/A5と同じバス
Arduino Mega 25602021MegaではA4とA5はI2Cではない
Arduino Leonardo / MicroD2D3デジタルピンが使われる
ESP32(DevKit)GPIO 21GPIO 22Wire.begin(SDA, SCL)でほかのピンも選べる。2つ目のバスはWire1
ESP8266 NodeMCUD2(GPIO 4)D1(GPIO 5)ソフトウェアI2C、ピンは変更可
Raspberry Pi PicoGP4(I2C0)GP5(I2C0)I2C0とI2C1は多くのピンの組で使える
Arduino Unoは5 Vで、ESP32、ESP8266、Picoは3.3 Vで動作します。3.3 Vのボードにつないだモジュールを5 Vで給電し、そのプルアップ抵抗が5 Vにつながっていると、SDAとSCLのラインが5 Vまで上がり、5 Vトレラントでないピンを壊すおそれがあります。モジュールを3.3 Vで給電するか、2つの電圧の間にBSS138トランジスタを使った双方向のレベル変換モジュールを入れてください。

ステップごとに:I2Cスキャナーのスケッチでアドレスを調べる

新しいモジュールを手に入れたら、最初にやるべきことはバス上のデバイスのアドレスを調べることです。I2Cスキャナーは0x08〜0x77のすべてのアドレスに空のメッセージを送り、ACKで応答したものを一覧にします。これで、モジュールが正しく配線されていることと、コードに書くべきアドレスの両方がわかります。

  1. モジュールのGNDをArduinoのGNDに、VCCを5Vにつなぎます(3.3 Vにしか対応しないモジュールは3.3Vに)。
  2. SDAをA4に、SCLをA5につなぎます。モジュールが複数あるなら、ブレッドボード上ですべてのSDAとSCLのピンをまとめます。
  3. 次のスケッチを書き込み、シリアルモニタを115200 baudで開きます。
  4. 表示されたアドレスを書き留めます。何も表示されなければ、配線、共通のGND、プルアップ抵抗を確認してください。
  5. モジュールを1つずつ抜き差しして、どのアドレスがどのデバイスのものかを確かめます。スキャナーは5秒ごとにスキャンし直します。
C++ (Arduino IDE)
1#include <Wire.h>
2
3void printHex(byte addr) {
4  Serial.print(F("0x"));
5  if (addr < 16) Serial.print('0');    // tek haneliye sıfır ekle / pad single digit
6  Serial.print(addr, HEX);
7}
8
9void setup() {
10  Serial.begin(115200);
11  Wire.begin();                         // Uno: A4 = SDA, A5 = SCL
12#if defined(ARDUINO_ARCH_AVR)
13  Wire.setWireTimeout(3000, true);      // AVR (Uno, Nano, Mega): hat kilitlenirse 3 ms sonra kurtar / recover a stuck bus after 3 ms
14#endif
15  Serial.println(F("I2C tarayici / I2C scanner"));
16}
17
18void loop() {
19  byte found = 0;
20  for (byte addr = 0x08; addr <= 0x77; addr++) {   // geçerli 7 bit adresler / valid 7-bit addresses
21    Wire.beginTransmission(addr);
22    byte err = Wire.endTransmission();             // 0 = cihaz ACK verdi / device acknowledged
23    if (err == 0) {
24      Serial.print(F("Bulundu / found: "));
25      printHex(addr);
26      Serial.println();
27      found++;
28    } else if (err == 4 || err == 5) {
29      Serial.print(F("Hat hatasi / bus error at "));
30      printHex(addr);
31      Serial.println();
32    }
33  }
34  if (found == 0) {
35    Serial.println(F("Cihaz yok: kablolari ve pull-up'lari kontrol et / no devices: check wiring and pull-ups"));
36  } else {
37    Serial.print(found);
38    Serial.println(F(" cihaz bulundu / device(s) found"));
39  }
40  Serial.println();
41  delay(5000);                          // 5 sn sonra yeniden tara / rescan after 5 s
42}
endTransmission()の結果意味対処
0デバイスがアドレスに応答した(ACK)このアドレスをコードで使う
1データがバッファに対して長すぎるスキャナーでは出ない。Unoでは一度に32バイトを超えて書かない
2アドレスでNACKそのアドレスにデバイスがない。スキャン中は正常
3データでNACKデバイスがコマンドを拒否した。レジスタ番号を確認する
4その他のバスエラー短絡、プルアップ抵抗の欠落、ノイズ
5タイムアウトバスが固まっている。配線と電源を確認する

スケッチのsetWireTimeoutの行は重要です。SDAやSCLが短絡したり、デバイスがラインをLOWにしたまま固まったりすると、初期設定のUnoのWireライブラリはいつまでも待ち続け、プログラムが止まってしまいます。タイムアウトを有効にすると、ライブラリは3 msであきらめてI2Cのハードウェアをリセットし、スキャナーはエラーコードで問題を知らせます。この行は#ifで囲んであり、AVRのボード(Uno、Nano、Mega)でだけコンパイルされます。ESP32とESP8266のWireライブラリにはもともと独自のタイムアウト保護があるので、同じスケッチが3つのプラットフォームすべてでそのままコンパイルでき、動きます。

ライブラリなしのI2C:レジスタの読み書き

ライブラリは便利ですが、内部ではどれも同じことをしています。デバイスにレジスタの番号を送り、そのレジスタからバイトを読むか、そのレジスタにバイトを書くかです。これを一度自分でやってみれば、新しいセンサーのデータシートを読んで、ライブラリがなくても使えるようになります。次の例は、MPU6050センサーをスリープモードから起こし、識別レジスタ(WHO_AM_I)を読み、生の値から温度とZ軸の加速度を計算します。

MPU-6050加速度・ジャイロセンサーモジュール
MPU-6050モジュール:VCC、GND、SCL、SDAのピンでI2Cバスにつながり、AD0ピンでアドレス0x68か0x69を選びます。
C++ (Arduino IDE)
1#include <Wire.h>
2
3const byte MPU_ADDR = 0x68;             // AD0 GND'de; AD0 = 3.3V ise 0x69 / AD0 low; 0x69 if AD0 high
4
5void writeReg(byte reg, byte value) {
6  Wire.beginTransmission(MPU_ADDR);
7  Wire.write(reg);                      // önce yazmaç adresi / register address first
8  Wire.write(value);                    // sonra veri / then the data
9  Wire.endTransmission();               // STOP gönder / send STOP
10}
11
12int16_t readWord(byte reg) {
13  Wire.beginTransmission(MPU_ADDR);
14  Wire.write(reg);
15  Wire.endTransmission(false);          // STOP yok, tekrarlı START / no STOP, repeated START
16  Wire.requestFrom(MPU_ADDR, (byte)2);  // 2 bayt iste / request 2 bytes
17  uint8_t hi = Wire.read();
18  uint8_t lo = Wire.read();
19  return (int16_t)(((uint16_t)hi << 8) | lo);
20}
21
22void setup() {
23  Serial.begin(115200);
24  Wire.begin();
25  Wire.setClock(400000);                // hızlı mod 400 kHz / fast mode 400 kHz
26  writeReg(0x6B, 0x00);                 // PWR_MGMT_1: uykudan uyandır / wake up from sleep
27  Wire.beginTransmission(MPU_ADDR);
28  Wire.write(0x75);                     // WHO_AM_I yazmacı / WHO_AM_I register
29  Wire.endTransmission(false);
30  Wire.requestFrom(MPU_ADDR, (byte)1);
31  Serial.print(F("WHO_AM_I = 0x"));
32  Serial.println(Wire.read(), HEX);     // 68 beklenir / expect 68
33}
34
35void loop() {
36  int16_t raw = readWord(0x41);         // TEMP_OUT_H ve TEMP_OUT_L / TEMP_OUT_H and TEMP_OUT_L
37  float tempC = raw / 340.0 + 36.53;    // veri sayfasındaki formül / datasheet formula
38  int16_t az = readWord(0x3F);          // Z ekseni ivmesi / Z-axis acceleration
39  Serial.print(F("Sicaklik / temp: "));
40  Serial.print(tempC, 1);
41  Serial.print(F(" C   AccZ: "));
42  Serial.println(az / 16384.0, 2);      // +-2g aralığında 16384 = 1 g / 16384 = 1 g at +-2g
43  delay(500);
44}

読み取り関数のendTransmission(false)の行に注目してください。STOPの代わりにリピートSTARTで方向を変えるため、ほかのデバイスが割り込むことがなく、センサーはどのレジスタを要求されたかを忘れません。WHO_AM_Iが0x68と読めれば、通信は完璧です。安価な互換品のモジュールでは0x70など別の値になることがあります。これはチップがMPU6500のような、互換性はあるものの別のモデルであることを意味し、一部の既製ライブラリが「センサーが見つかりません」と表示する原因です。温度の式(生の値 / 340 + 36.53)はMPU6050のデータシートにあるそのままのものです。加速度は±2 gの範囲で生の値16384が1 gに相当するので、机に平らに置いたセンサーはZ軸で約1.00を示します。

1本のバスに複数のデバイスとアドレスの衝突

I2Cの最大の利点は、アドレスが異なる限り、同じ2本の線にデバイスを並列につなげることです。たとえば、LCD(0x27)、OLED(0x3C)、BMP280(0x76)、DS3231時計(0x68)は、同じA4とA5のピンを問題なく共有できます。各ライブラリは自分のアドレスと通信し、ほかのデバイスは静かに聞いています。問題が起きるのは、2つのデバイスのアドレスが同じときです。両方がACKを送り、データがぶつかり合い、たいていどちらも動きません。解決策は次のとおりです。

  • アドレスピンを変える:MPU6050のAD0ピンを3.3 Vにつなぐとアドレスが0x69になり、DS3231(0x68)との衝突がなくなります。
  • はんだジャンパーを使う:LCDモジュールのA0〜A2のジャンパーで8つ、ADS1115のADDRピンで4つのアドレスを選べます。
  • 2つ目のI2Cバスを使う:衝突するデバイスを、ESP32ならWire1、PicoならI2C1で別のピンに移します。
  • I2Cマルチプレクサを加える:TCA9548Aは1つのアドレス(0x70)から8本の独立したサブバスを作ります。同じアドレスのOLEDを8台つなぐことさえできます。
  • アドレスが固定のデバイス:DS1307やHTU21Dのように、アドレスをまったく変えられないチップもあります。その場合は、別のバスかマルチプレクサしか手がありません。

ESP32のI2C:自由なピンと2本のバス

ESP32では、I2Cのハードウェアをピンに柔軟に割り当てられます。初期設定のピンはGPIO 21(SDA)とGPIO 22(SCL)ですが、Wire.begin(SDA, SCL)でほぼ好きな2本のGPIOを選べます。従来のESP32とESP32-S3にはI2Cコントローラーが2つあるので、同じアドレスのセンサー2つを別々のバスで動かせます。ESP32-C3のコントローラーは1つだけです。GPIO 34〜39は入力専用で、I2Cには使えません。ボードの全ピン配置はピン配置ツールで、全体的な入門はESP32とは?で確認してください。

C++ (Arduino IDE)
1#include <Wire.h>
2
3// Klasik ESP32'de iki I2C denetleyicisi vardır: Wire ve Wire1 / the classic ESP32 has two I2C controllers: Wire and Wire1
4
5void scan(TwoWire &bus, const char *name) {
6  Serial.printf("%s:\n", name);
7  for (uint8_t addr = 0x08; addr <= 0x77; addr++) {
8    bus.beginTransmission(addr);
9    if (bus.endTransmission() == 0) {
10      Serial.printf("  bulundu / found 0x%02X\n", addr);
11    }
12  }
13}
14
15void setup() {
16  Serial.begin(115200);
17  Wire.begin(21, 22);                   // SDA, SCL: varsayılan pinler / default pins
18  Wire1.begin(32, 33, 400000);          // SDA, SCL, hız: başka iki GPIO / SDA, SCL, speed: any two other GPIOs
19  scan(Wire, "Wire  (SDA 21, SCL 22)");
20  scan(Wire1, "Wire1 (SDA 32, SCL 33)");
21}
22
23void loop() {
24}

Raspberry Pi PicoのI2C(MicroPython)

PicoのRP2040チップにはI2C0とI2C1という2つのコントローラーがあり、それぞれをいくつかのピンの組に割り当てられます。I2C0にはGP0/GP1、GP4/GP5、GP8/GP9、GP12/GP13、GP16/GP17、GP20/GP21を、I2C1にはGP2/GP3、GP6/GP7、GP10/GP11、GP14/GP15、GP18/GP19、GP26/GP27を使えます。どの組でも偶数番のピンがSDA、奇数番のピンがSCLです。MicroPythonではスキャナーは1行で済みます。i2c.scan()がACKで応答したアドレスのリストを返します。Picoは3.3 Vで動作するため、モジュールは3V3(OUT)ピンから給電するのがもっとも安全です。RP2040は1 MHzのファストモードプラスにも対応していますが、ブレッドボードでは400 kHzにとどめるほうがトラブルが少なくて済みます。Picoの最初の一歩はRaspberry Pi Picoとは?を参照してください。

MicroPython
1from machine import Pin, I2C
2
3i2c = I2C(0, sda=Pin(4), scl=Pin(5), freq=400000)   # I2C0: GP4 = SDA, GP5 = SCL
4devices = i2c.scan()                                 # ACK veren adresler / addresses that ACK
5
6if devices:
7    for addr in devices:
8        print("Bulundu / found:", hex(addr))
9else:
10    print("Cihaz yok / no devices")
11
12# MPU6050 varsa WHO_AM_I yazmacını oku / if an MPU6050 is present, read WHO_AM_I
13if 0x68 in devices:
14    i2c.writeto_mem(0x68, 0x6B, b"\x00")             # uykudan uyandır / wake up
15    who = i2c.readfrom_mem(0x68, 0x75, 1)[0]
16    print("WHO_AM_I:", hex(who))                     # 0x68 beklenir / expect 0x68

I2Cを使ったプロジェクトアイデア

  • ミニ気象観測所:BMP280で気圧と温度を測り、OLEDディスプレイに表示します。2つのモジュールが同じ2本の線を共有します。
  • デジタル時計:DS3231時計モジュールから時刻を読み、I2C LCDディスプレイに表示します。
  • 傾きインジケーター:MPU6050で角度を計算し、結果を水準器のようにOLEDに描きます。
  • Wi-Fiセンサーノード:ESP8266 NodeMCUのD1とD2のピンにつないだセンサーを、スマートフォンから監視します。
  • 16サーボのロボットアーム:PCA9685ドライバを使い、たった2本のピンで16個のサーボを制御します。
  • データロガー:1分ごとに読み取り値をAT24C256 EEPROMに書き込み、停電してもデータが残るようにします。

よくある間違いと対処法

症状考えられる原因対処法
スキャナーがデバイスを見つけないSDAとSCLが逆、共通のGNDがない、モジュールに電源が来ていない配線を確認する。UnoではA4 = SDA、A5 = SCL
スキャナーが止まる、またはすべてのアドレスでエラーを出すバスの短絡、またはプルアップ抵抗がないsetWireTimeoutを加える。待機中のSDAとSCLで電源電圧を測る
スケッチはコンパイルできるがディスプレイが真っ暗コードのアドレスが違う(0x3Fではなく0x27)スキャナーで見つけたアドレスを使う
基板の裏に0x78とあり、コードが動かない8ビットのアドレスを使った2で割る:0x78 → 0x3C
2つのセンサーが一緒に動かないアドレスの衝突アドレスピンを変える、2つ目のバスかTCA9548Aを使う
ランダムに止まる、モーターが動くと誤動作する長い配線、ノイズ、または弱い電源配線を短くし、100 kHzに下げ、モジュールに100 nFのコンデンサを付ける
ESP32やPicoのピンが壊れた5 Vにつながったプルアップ抵抗を3.3 Vのピンにつないだモジュールを3.3 Vで給電するか、レベル変換モジュールを使う
I2Cのトラブルでいちばん頼りになるのはテスターです。電源が入っていて通信していないとき、SDAとSCLの両方で電源電圧(5 Vか3.3 V)が測れるはずです。0 Vなら、どれかのデバイスがラインを短絡させているか、固まっています。ボードの電源を切った状態で、SDAとVCCの間の抵抗を測れば、バス全体のプルアップ抵抗のおおよその値もわかります。

I2Cは、たった2本の線でディスプレイ、時計、センサー、メモリをプロジェクトに加える、もっとも実用的な方法です。アドレス、プルアップ抵抗、スキャナーのスケッチを理解すれば、ほとんどのトラブルは数分で解決できます。BlueGraysアプリには、I2C LCDディスプレイ、OLEDディスプレイでのポンゲーム、DS1307を使ったデジタル時計、ESP32の天気表示など、I2Cを使うプロジェクトが、ステップごとの回路図とコード付きでそろっています。

よくある質問

I2CとSPIの違いは?

I2Cはアドレス指定によって同じ2本の線に100台以上のデバイスをつなげますが、速度はふつう100〜400 kHzに限られます。SPIは共通の3本の線に加えて、デバイスごとに個別のチップセレクト(CS)ピンが必要ですが、その代わり10 MHz以上に達します。センサーや小型ディスプレイにはI2Cが、SDカードやカラーTFTディスプレイにはSPIが選ばれます。

I2Cバスにはいくつのデバイスをつなげますか?

7ビットのアドレス指定では112個のアドレスが使えます。実際の限界はアドレスの数よりも、バスの静電容量(最大400 pF)と並列になるプルアップ抵抗で決まります。ブレッドボードでは5〜10台なら問題なく動きます。それ以上なら、TCA9548Aマルチプレクサか2つ目のI2Cバスを使ってください。

I2Cのプルアップ抵抗はどの値にすべきですか?

100 kHzで短い配線なら4.7 kΩ、400 kHzやデバイスの多いバスなら2.2 kΩがもっともよく使われる値です。5 Vのバスでは1.5 kΩ、3.3 Vのバスでは1 kΩを下回らないようにしてください。モジュールに付いている抵抗が並列になることも忘れずに。

Arduino UnoのI2Cはどのピンですか?

Arduino UnoとNanoでは、SDAがA4ピン、SCLがA5ピンです。Uno R3にはAREFの隣にSDAとSCLと書かれたピンがもう2本ありますが、基板上でA4とA5につながっていて、別のバスではありません。MegaではI2Cのピンは20(SDA)と21(SCL)です。

I2Cのケーブルはどこまで長くできますか?

I2Cは基板上の短い距離のために設計されたもので、100 kHzで適切なプルアップ抵抗を使えば、ふつう約1メートルまで動きます。それより長い距離では、速度を下げるか、ツイストペアケーブルを使うか、P82B715のようなバスドライバに切り替える必要があります。数メートルになるなら、RS-485か無線のほうが適しています。

I2Cスキャナーがデバイスを見つけられないときはどうすればよいですか?

まず、SDAとSCLが正しいピンにつながっているか、共通のGNDがつながっているか、モジュールに電源が来ているかを確認してください。次に、待機中のSDAとSCLで電源電圧を測ってプルアップ抵抗を確かめます。それでも見つからなければ、モジュールを単体で短い配線にして100 kHzで試し、ESP32ならWire.beginに正しいピンを渡しているか確認してください。

全プロジェクトを見る
I2CでLCDディスプレイを使う
#32初級
プレミアム

I2CでLCDディスプレイを使う

Arduinoにディスプレイを接続し、最初のメッセージを表示します。2行のLCDディスプレイは数本の配線だけで接続できます。まず上の行にあいさつを表示し、次に下の行にもう1つのメッセージを表示します。その後、表示を消去して最初から繰り返します。温度や時刻、スコアを表示するプロジェクトは、すべてこのステップから始まります。

4.8(53)
70
ESP32 I2C LCDディスプレイ(16x2)
#119中級
プレミアム

ESP32 I2C LCDディスプレイ(16x2)

このプロジェクトでは、16x2文字のLCDをI2CでESP32に接続して文字を表示します。I2Cならデータ用の2本のピン(SDAとSCL)だけで制御でき、従来のパラレル接続のように6本以上のピンを使わずに済みます。コードでは1行目に固定メッセージ、2行目に毎秒増えるカウンターを表示します。また、起動時にI2Cスキャナーを実行し、ディスプレイのアドレス(通常は0x27または0x3F)をシリアルモニタに表示します。

4.9(23)
40
ESP32とDS1307 RTCのデジタル時計(I2C + LCD)
#120中級
プレミアム

ESP32とDS1307 RTCのデジタル時計(I2C + LCD)

このプロジェクトでは、DS1307リアルタイムクロック(RTC)モジュールを使って16x2 I2C LCDに時刻と日付を表示します。DS1307は電池でバックアップされるRTCで、ESP32の電源を切っても時刻を保持します。両方のモジュールが同じI2Cバス(SDA/SCL)を共有するため、2本の信号線だけで複数の機器を制御する方法を学べます。I2C通信プロトコルやRTCモジュールの動作原理、LCDの表示データを随時更新する方法を実践的に学ぶプロジェクトです。

4.6(26)
40
Arduino I2C計算ツール
#94中級
プレミアム

Arduino I2C計算ツール

4x4のメンブレンキーパッドと16x2のI2C LCDディスプレイを使い、足し算、引き算、掛け算、割り算を行うArduinoベースの計算ツールのプロジェクトです。I2Cにより、ディスプレイを2本のケーブルだけで接続できるため、配線を整理しやすく、ほかの用途に使えるピンも多く残せます。小数の入力と負の数にも対応しています。

5.0(31)
43
パドルボールゲーム(SSD1306)
#86中級
プレミアム

パドルボールゲーム(SSD1306)

ボール1個とパドル2本で遊ぶ、ビデオゲームの原点の1つである卓球ゲームを、小さなOLEDディスプレイで楽しめます。2つのボタンで右のパドルを上下に動かします。左のパドルはコンピューターがボールを追いながら操作し、ボールを打つたびにブザーが短く鳴ります。先に9点を取ると勝ちです。その後、新しいゲームが自動で始まります。

4.5(47)
65
ESP32 HTTPClient+天気API(SSD1306 OLED付き)
#134上級
プレミアム

ESP32 HTTPClient+天気API(SSD1306 OLED付き)

このプロジェクトでは、ESP32でHTTPClient.hを使い、インターネット経由で天気API(wttr.in)をREST APIとして呼び出します。前のI2C LCD版とは異なり、SSD1306 OLED(128×64)ディスプレイを使うため、大きな文字やベクターアイコンで、1つの画面に豊かな表現ができます。天気を表す7種類のベクターアイコン(晴れ、晴れ時々曇り、曇り、雨、雪、嵐、霧)を実行時に描画するので、ビットマップファイルは不要です。気温は中央に3倍の大きさの文字で表示します。

4.3(10)
19
ドットマトリクス時計とカレンダー(MAX7219、DS1307)
#84中級
プレミアム

ドットマトリクス時計とカレンダー(MAX7219、DS1307)

机や壁に置き、時刻と日付をスクロール表示する光るパネルです。8x32のLEDディスプレイには大きな数字の時計が表示され、コロンの2つの点が毎秒点滅します。毎分の初めに曜日と日付がスクロールし、その後、時計表示に戻ります。時計モジュールには専用の電池があるため、電源を抜いても時刻を保持します。

5.0(37)
59
ESP32温度データロガー(DHT22 + DS1307 RTC + microSD + OLED)
#144上級
プレミアム

ESP32温度データロガー(DHT22 + DS1307 RTC + microSD + OLED)

ESP32で日時付きの温度・湿度記録システムを作ります!DHT22で1分ごとに温度と湿度を読み取り、DS1307 RTCから取得した日時とともにmicroSDカードへCSV形式で記録します。OLEDには最新の温度を大きな文字で表示し、湿度のパーセント値、日時、総記録数も表示します。SDカードの/temp_log.csvファイルはExcel、Google Sheets、Pythonのpandasで開いて分析できます。温室の監視、家や部屋の温度管理、実験室のデータ収集、食品の保管環境や養蜂箱の監視に適しています。

4.7(7)
12