2学期開始!体験授業 受付中 →
ROBO SCIENCE

Pico 2個で会話させる ―
I2C通信

2026.10.12
R3 PE(Pre-expert) ES(Expert-soccer) ER(Expert-rescue)

I2C も、マイコンでよく使われる通信です。データを1ビットずつ送る点では、前回のシリアル通信(UART)の仲間です。ちがうのは、メインが聞いたときだけサブが答えること。今回も Pico 2個をつなぎ、1 から 100 までを受け取って確かめます。前回と同じ順番で進めるので、くらべながら読めます。

仕事を分担する ― メインが聞いて、サブが答える

役割はシリアル通信の回と同じです。

担当
サブ(Pico B)センサーを読んで計算し、結果をメインに渡す
メイン(Pico A)受け取った結果をもとに、モーターを回す

ちがうのは話し方です。シリアル通信では、サブが好きなときに送りました。I2C では、メインが聞いたときだけ、サブが答えます。サブのほうから話しかけることはできません。

資料によっては、メインを「マスター」「コントローラ」、サブを「スレーブ」「ターゲット」と書いてあります。同じ意味です。

つなぐ線は4本 ― SDA・SCL・GND・3.3V

信号の線は2本です。SDA はデータの線、SCL は時計(クロック)の線です。データは行きも帰りも SDA の1本を通ります。SCL には、メインが「いま読んで」という合図を出します。

ここに GND と電源を足して、4本にします。本数はシリアル通信のときと同じです。

つなぐときの決まりは3つです。SDA は SDA へ、SCL は SCL へつなぎます。シリアル通信のような、たすきがけにはしません。GND は共通にします。電源はメインの 3V3(OUT) からサブの VSYS へ入れ、サブに電気を送ります。

サブに書き込むときは、3.3V の線を抜きます。つないだまま USB をさすと、USB の電気がメインの 3.3V 側へ逆に流れこみ、メインが壊れることがあります。

メイン(Pico A) モーターを回す サブ(Pico B) センサーを読んで計算する GP4 (SDA) GP4 (SDA) データ(行きも帰りもこの1本) GP5 (SCL) GP5 (SCL) 時計(メインが出す) GND GND 0V の基準をそろえる 3V3 (OUT) VSYS 電源を配る SDA は SDA へ、SCL は SCL へ。シリアル通信のように、たすきがけにはしない
信号2本に、GND と電源が付いて4本。同じ名前のピンどうしをつなぐ。

SDA と SCL は、3.3V へ引き上げておく必要があります(プルアップ)。Pico 同士を短い線でつなぐなら、Pico の中の抵抗だけで動きます。線が長いとき、速くするとき、つなぐ数が増えたときは、SDA と SCL のそれぞれと 3.3V のあいだに 2.2kΩ〜4.7kΩ の抵抗を付けます。

サブを増やすときは、同じ2本の線に並べてつなぎます。プルアップ抵抗は、SDA と SCL に1個ずつあれば足ります。

メイン Pico A SDA SCL サブ 0x08 サブ 0x09 サブ 0x0A 3.3V プルアップ抵抗 2.2kΩ〜4.7kΩ 同じ2本の線に、サブを並べてつなぐ サブは番地で呼び分ける。線は増えない
SDA と SCL を、抵抗を通して 3.3V へ引き上げる。サブは同じ2本にぶら下がる。GND と電源の線は省いてあります。

実際にブレッドボードで組むと、こうなります。

配線図
ブレッドボードに Raspberry Pi Pico を2個並べ、4本のジャンパー線でつないだ配線図。上側に黒(GND)と赤(電源)、下側に青(GP4)と黄(GP5)が、同じピンどうしで渡してある。
上の黒が GND、赤が電源。下の青が GP4(SDA)どうし、黄が GP5(SCL)どうしです。
回路図
同じつなぎ方の回路図。2個の Pico の GP4 どうし、GP5 どうし、下の GND どうしがつながり、3V3 から相手の39番ピンへ線が引かれている。
同じつなぎ方を回路図で。39番ピンが VSYS です(Fritzing の記号では VSS と出ます)。
名前つながる先はじめのピン
WireI2C0GP4=SDA, GP5=SCL
Wire1I2C1GP26=SDA, GP27=SCL

プログラムでは Wire を使います。シリアル通信の Serial1 にあたるものです。

速さは合わせなくていい ― メインが時計を出す

シリアル通信では、送る側と受ける側のボーレートを同じ数字にしました。受ける側が、速さだけを頼りに数えていたからです。

I2C には時計の線(SCL)があります。メインが SCL を上げ下げし、サブはそれに合わせるだけです。速さを決めるのはメインだけで、サブ側に設定はありません。

速さ1バイト受け取る時間使いどき
100kHz約0.2msはじめの設定。まずこれで動かす
400kHz約0.05ms競技で速くしたいとき。プルアップ抵抗を付ける

速くするときは、メインの Wire.begin() のあとに Wire.setClock(400000); と書きます。

番地で相手を選ぶ ― 0x08 のサブに聞く

I2C では、同じ2本の線にサブを何個もつなげます。だれに聞いているのかを区別するために、サブには1個ずつ番地(アドレス)を付けます。使えるのは 0x08〜0x77 です。

メイン 聞く側 サブ 番地 0x08 ① 0x08 さん、1バイトください ② 30 です サブは、聞かれたときだけ答える サブのほうから話しかけることはできない
メインが番地を呼び、呼ばれたサブだけが答える。
書き方意味
Wire.begin()メインとして始める
Wire.begin(0x08)番地 0x08 のサブとして始める
Wire.requestFrom(0x08, 1)メインが、0x08 に1バイト求める
Wire.onRequest(answer)サブが、聞かれたときに呼ぶ関数を決める
Wire.write(25)1バイト送る
Wire.read()1バイト取り出す

送れる数値は 0〜255 だけ。数値をそのまま1バイトで流すところは、Serial1.write() と同じです。

サブのプログラム ― 聞かれたら 1 から 100 を順に答える

メインがサブのデータを受け取れることを確認するために、サブは聞かれるたびに 1、2、3 … 100 と順に答え、100 まで行ったら 1 に戻ります。

// サブ(Pico B)― 答える側
#include <Wire.h>

byte value = 1;

void answer() {                       // メインに聞かれたら呼ばれる
  Wire.write(value);                  // 1バイト答える

  value++;                            // つぎの番号へ
  if (value > 100) { value = 1; }     // 100 の次は 1 に戻す
}

void setup() {
  Wire.setSDA(4);                     // GP4 をデータの線に
  Wire.setSCL(5);                     // GP5 を時計の線に
  Wire.begin(0x08);                   // 番地 0x08 のサブとして始める
  Wire.onRequest(answer);             // 聞かれたら answer を呼ぶ
}

void loop() {
}

setSDA と setSCL は、I2C0 をどのピンで使うか設定します(GP4・GP5 であればデフォルトなので省略できます)。どのピンを使っているかがコードを見て分かるように、書いておきます。どちらも begin() より前に置きます。

loop() は空です。サブは自分からは送らず、聞かれたときに answer() が呼ばれるだけだからです。

メインのプログラム ― 聞いて、シリアルモニタに出す

メインは 0x08 に1バイト求め、返ってきた値をパソコンに流します。

// メイン(Pico A)― 聞く側
#include <Wire.h>

void setup() {
  Serial.begin(115200);               // パソコン確認用(USB)

  Wire.setSDA(4);                     // GP4 をデータの線に
  Wire.setSCL(5);                     // GP5 を時計の線に
  Wire.begin();                       // 番地なし=メインとして始める
}

void loop() {
  Wire.requestFrom(0x08, 1);          // 0x08 に「1バイトください」

  if (Wire.available()) {             // 答えが届いていれば
    byte value = Wire.read();         // 1バイト取り出す
    Serial.println(value);            // シリアルモニタに出す
  }

  delay(100);                         // 目で追えるようにゆっくり
}

メインを USB でパソコンにつなぎ、Arduino IDE のシリアルモニタを 115200 で開きます。

シリアルモニタの出かた読みとれること
1, 2, 3 … 100, 1, 2 … と並ぶ配線も番地も合っている
何も出ないSDA と SCL が逆、GND がつながっていない、または番地がちがう
ときどき止まる・値がおかしい線が長い、またはプルアップ抵抗が足りない

番地は、サブの Wire.begin(0x08) とメインの Wire.requestFrom(0x08, 1) で同じ数字にします。

郵便ポストにたまらない ― 聞いたぶんだけ届く

シリアル通信では、届いたバイトが受け側のポスト(バッファ)にたまりました。取りに行くのが遅いと、あふれました。

I2C では、メインが聞いたぶんだけ届きます。聞いていないときには何も来ないので、たまることも、あふれることもありません。

そのかわり、サブに決まりが1つあります。聞かれたら、すぐ答えることです。answer() の中で重い計算をすると、メインを待たせます。answer() の中は、用意してある値を出すだけにします。

最新の値を取る ― 聞いた瞬間の値が返る

シリアル通信では、while で読み切って最後の1バイトを使いました。古い値がポストに並んでいたからです。

I2C では、その必要がありません。サブは最新の値を変数に置いておき、聞かれたらそれを出すだけです。メインが受け取るのは、いつも聞いた瞬間の値です。

// サブ ― いつ聞かれても、最新の値を答える
#include <Wire.h>

volatile byte latest = 0;             // answer と loop の両方で使う

void answer() {
  Wire.write(latest);                 // 置いてある値を出すだけ
}

void setup() {
  Wire.setSDA(4);
  Wire.setSCL(5);
  Wire.begin(0x08);
  Wire.onRequest(answer);
}

void loop() {
  latest = calcValue();               // 重い計算はここでやる
}

calcValue() のところは、自分のセンサー処理に置き換えてください。出てくるのが 0〜255 の数値であれば、なんでもかまいません。

もう1つ、シリアル通信ではできなかったことができます。届かなかったことに気づけます。Wire.requestFrom() は受け取ったバイト数を返すので、0 ならサブが答えていません。

// メイン ― 答えがないときに気づく
if (Wire.requestFrom(0x08, 1) == 0) {
  // 0 バイト=サブが答えていない
}

1バイトは9ビット ― 8ビットと返事

シリアル通信では、受け側はスタートビットが落ちた瞬間から時間を数えてビットを区切りました。

I2C は時間を数えません。受け取る側は、SCL が上がるたびに SDA を1ビット読みます。時計の線があるので、区切りは時計が教えてくれます。

サブ(0x08)から 25 を受け取るとき、線の上を流れるもの スタート 番地 7ビット 読む 返事 データ 8ビット 返事 ストップ 0 D7 0 D6 0 D5 1 D4 1 D3 0 D2 0 D1 1 D0 1 返事 SCL 時計 SDA データ
上の帯が全体の順番。下はデータ8ビットと返事を広げたところ。SCL が上がっているあいだに、SDA を1ビット読む。●が読む場所。
順番出す側中身
スタートメインこれから始める、の合図
番地 7ビットメインだれに話すか
読む/書く 1ビットメイン聞くのか、送るのか
返事 1ビットサブ0 なら「います」
データ 8ビットサブ上位のビットから順に
返事 1ビットメイン0 なら「続けて」、1 なら「ここまで」
ストップメイン終わり、の合図

8ビットごとに、受け取った側が返事を1ビット返します。これを ACK(アック)といいます。データ8ビットと返事1ビットで、1バイトは9ビットです。

番地を呼んでも返事がなければ、その番地にサブはいません。届かなかったことに気づけるのは、この返事のビットがあるからです。

ビットを送る順番は、シリアル通信と逆です。シリアル通信は下位のビットから、I2C は上位のビットから送ります。図の 25 は、0・0・0・1・1・0・0・1 の順に流れています。

シリアル通信とくらべる ― 同じところ、ちがうところ

シリアル通信(UART)I2C
線の本数4本4本
信号の線TX・RX(たすきがけ)SDA・SCL(同じ名前どうし)
つなげる数1対1同じ2本に何個も
相手の選び方線で決まる番地で選ぶ
話しはじめる側どちらからでもメインだけ
速さ両方で同じ数字にするメインが時計を出す
1バイト10ビット(スタート・8・ストップ)9ビット(8・返事)
1バイトの時間約0.09ms(115200)約0.2ms(100kHz)/約0.05ms(400kHz)
受け側にたまるかたまる。あふれるたまらない
届かないとき気づけない返事がないので分かる

サブが1個で、値を流しつづけるなら、シリアル通信が向いています。サブを何個もつなぐとき、メインが聞きたいときだけ値がほしいときは、I2C が向いています。

動かないときに見るところ ― 症状から原因を引く

症状原因対処
何も返ってこないSDA と SCL が逆GP4 どうし、GP5 どうしになっているか見る
何も返ってこない番地がちがうサブの Wire.begin() とメインの Wire.requestFrom() の番地をそろえる
動いたり動かなかったりするGND がつながっていないGND同士を必ず結ぶ。線の半抜けも疑う
ときどき値がおかしいプルアップ抵抗が足りないSDA・SCL と 3.3V のあいだに抵抗を付ける。線を短くする
値が古いまま変わらないサブの loop() が止まっているサブ側で LED を点滅させて、回っているか見る
サブが起動しない3.3V か GND が届いていないサブの VSYS と GND に電圧が来ているか、テスターで見る
Wire でエラーが出るPico用のボードが入っていないArduino IDE のボードマネージャで Raspberry Pi Pico/RP2040 を入れる
ロボサイエンスについて

ロボカップジュニア2027 に向けて、レスキュー(災害救助)とサッカーの2種目に取り組みます。プログラミングと電子工作の経験は問いません。

前回:Pico 2個で会話させる ― シリアル通信 →
ロボサイエンスの記録一覧 →
体験授業・お問い合わせ →

← ロボサイエンスの記録一覧へ戻る