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スキャナーのスケッチ、ライブラリを使わないレジスタの読み取り例、アドレスの衝突などのよくあるトラブルの対処法をステップごとに見ていきます。

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のメッセージは次のように進みます。
- START:SCLがHIGHの間にコントローラーがSDAを1から0に下げると、バス上のすべてのデバイスが聞き始めます。
- アドレスと方向ビット:7ビットのアドレスが送られ、続いて読み出し(1)か書き込み(0)のビットが送られます。
- ACK:そのアドレスを持つデバイスが9番目のクロックパルスの間にSDAをLOWにして「ここにいます」と伝えます。誰も応答しなければNACKと呼ばれます。
- データバイト:各バイトは8ビットで送られ、受信側は1バイトごとにACKで応えます。ほとんどのセンサーでは、最初のバイトは読み書きするレジスタの番号です。
- STOP:SCLがHIGHの間にSDAが0から1に上がると、バスは再び空きます。読み出しでは、バスを手放さずに方向を変えられるよう、STOPの代わりにリピートSTARTを使います。
Arduinoの世界でよく使われるほかの2つのプロトコルと比べると、I2Cを選ぶべき場面がはっきりします。
| 項目 | I2C | SPI | UART(シリアル) |
|---|---|---|---|
| 信号線 | 2本(SDA、SCL) | 共通の3本(MOSI、MISO、SCK)+デバイスごとにCS 1本 | 2本(TX、RX) |
| デバイスの数 | 1本のバスに100以上のアドレス | 空いているCSピンの数だけ | ふつうは2台 |
| 一般的な速度 | 100 kHz、400 kHz、1 MHz | 10 MHz以上 | 9600〜115200 baud |
| デバイスの選択 | アドレスで | CSピンで | なし、1対1 |
| 主な用途 | センサー、時計、小型ディスプレイ、EEPROM | SDカード、TFTディスプレイ、RFID | GPS、Bluetooth、パソコンとの接続 |
I2Cの速度と技術的な制約
| モード | 最大速度 | 最大立ち上がり時間 | 使われる場面 |
|---|---|---|---|
| スタンダードモード(Sm) | 100 kHz | 1000 ns | 初期設定、長い配線 |
| ファストモード(Fm) | 400 kHz | 300 ns | OLED、MPU6050、BMP280 |
| ファストモードプラス(Fm+) | 1 MHz | 120 ns | Picoや一部の新しいセンサー |
| ハイスピードモード(Hs) | 3.4 MHz | 80 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 / PCF8574A | 0x27 / 0x3F | A0〜A2のジャンパーで0x20〜0x27または0x38〜0x3F |
| 0.96インチOLED | SSD1306 | 0x3C | 裏の抵抗で0x3D |
| 加速度・ジャイロセンサー | MPU6050 | 0x68 | AD0 = 1で0x69 |
| リアルタイムクロック | DS1307 / DS3231 | 0x68 | 不可 |
| 気圧と温度 | BMP280 / BME280 | 0x76または0x77 | SDOピンによる |
| 照度 | BH1750 | 0x23 | ADDR = 1で0x5C |
| 16ビットADC | ADS1115 | 0x48 | ADDRピンで0x48〜0x4B |
| 16チャンネルのサーボ・PWMドライバ | PCA9685 | 0x40 | A0〜A5のジャンパーで0x40から数十のアドレス |
| EEPROMメモリ | AT24C32 / AT24C256 | 0x50 | A0〜A2で0x50〜0x57 |
| I2Cマルチプレクサ | TCA9548A | 0x70 | A0〜A2で0x70〜0x77 |
プルアップ抵抗:なぜ必要か、値の選び方
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 kHz | 4.7 kΩ〜10 kΩ | ほとんどのモジュールにすでに付いている |
| 複数のモジュール、400 kHz | 2.2 kΩ〜4.7 kΩ | モジュールの抵抗は並列になる |
| 50 cmより長い配線 | 2.2 kΩで100 kHz | SDAとSCLをより合わせず、それぞれをGNDとより合わせる |
| 3.3 Vのバス(ESP32、Pico) | 2.2 kΩ〜4.7 kΩ | 抵抗は5 Vではなく3.3 Vにつなぐ |
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
| ボード | SDA | SCL | 備考 |
|---|---|---|---|
| Arduino Uno / Nano | A4 | A5 | Uno R3では、AREFの隣のSDAとSCLのピンはA4/A5と同じバス |
| Arduino Mega 2560 | 20 | 21 | MegaではA4とA5はI2Cではない |
| Arduino Leonardo / Micro | D2 | D3 | デジタルピンが使われる |
| ESP32(DevKit) | GPIO 21 | GPIO 22 | Wire.begin(SDA, SCL)でほかのピンも選べる。2つ目のバスはWire1 |
| ESP8266 NodeMCU | D2(GPIO 4) | D1(GPIO 5) | ソフトウェアI2C、ピンは変更可 |
| Raspberry Pi Pico | GP4(I2C0) | GP5(I2C0) | I2C0とI2C1は多くのピンの組で使える |
ステップごとに:I2Cスキャナーのスケッチでアドレスを調べる
新しいモジュールを手に入れたら、最初にやるべきことはバス上のデバイスのアドレスを調べることです。I2Cスキャナーは0x08〜0x77のすべてのアドレスに空のメッセージを送り、ACKで応答したものを一覧にします。これで、モジュールが正しく配線されていることと、コードに書くべきアドレスの両方がわかります。
- モジュールのGNDをArduinoのGNDに、VCCを5Vにつなぎます(3.3 Vにしか対応しないモジュールは3.3Vに)。
- SDAをA4に、SCLをA5につなぎます。モジュールが複数あるなら、ブレッドボード上ですべてのSDAとSCLのピンをまとめます。
- 次のスケッチを書き込み、シリアルモニタを115200 baudで開きます。
- 表示されたアドレスを書き留めます。何も表示されなければ、配線、共通のGND、プルアップ抵抗を確認してください。
- モジュールを1つずつ抜き差しして、どのアドレスがどのデバイスのものかを確かめます。スキャナーは5秒ごとにスキャンし直します。
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軸の加速度を計算します。

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とは?で確認してください。
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とは?を参照してください。
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 0x68I2Cを使ったプロジェクトアイデア
- ミニ気象観測所: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は、たった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ディスプレイを使う
Arduinoにディスプレイを接続し、最初のメッセージを表示します。2行のLCDディスプレイは数本の配線だけで接続できます。まず上の行にあいさつを表示し、次に下の行にもう1つのメッセージを表示します。その後、表示を消去して最初から繰り返します。温度や時刻、スコアを表示するプロジェクトは、すべてこのステップから始まります。

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

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

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

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

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

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

ESP32温度データロガー(DHT22 + DS1307 RTC + microSD + OLED)
ESP32で日時付きの温度・湿度記録システムを作ります!DHT22で1分ごとに温度と湿度を読み取り、DS1307 RTCから取得した日時とともにmicroSDカードへCSV形式で記録します。OLEDには最新の温度を大きな文字で表示し、湿度のパーセント値、日時、総記録数も表示します。SDカードの/temp_log.csvファイルはExcel、Google Sheets、Pythonのpandasで開いて分析できます。温室の監視、家や部屋の温度管理、実験室のデータ収集、食品の保管環境や養蜂箱の監視に適しています。
ほかの記事
BlueGraysアプリ
この回路をスマホでステップごとに作る
回路図、コード、部品リストを1つの画面で確認できます。Arduino、ESP32、Picoのプロジェクトを200件以上用意しています。

