129_I2C세대연동_1대N네트워크 🌐🕸️

도로시와 지니가 함께하는 1 Master & 2 Slaves 다중 원격 제어
- 👧 도로시: “지니야! 마스터 아두이노 하나에 연결된 버튼 2개로 슬레이브 1번과 슬레이브 2번의 LED를 각각 따로 켤 수 있을까?”
- 🐱 지니: “물론이지! I2C는 주소 기반 버스 통신망이기 때문에 마스터가
1번 주소와2번 주소를 번갈아 호출하며 데이터를 보내면 단 2가닥의 선만으로도 각각의 슬레이브를 개별 제어할 수 있단다!” -
🐶 토토: “바우! 선이 복잡하게 얽히지 않고 나뭇가지처럼 병렬로 연결된다 멍!”
- 🖥️ 강의 슬라이드: slides.md
1. 회로도 분석: 1 Master & 2 Slaves 버스 구성

🔌 병렬 버스 토폴로지(Bus Topology) 배선
세 대의 아두이노가 동일한 SDA/SCL 라인을 공유하도록 브레드보드의 공통 버스 레일에 병렬 연결합니다.
| 보드 구분 | 역할 및 핀 구성 | I2C 연결 핀 | 기능 요약 |
|---|---|---|---|
| Master | 8번 핀(버튼 1), 9번 핀(버튼 2) | A4(SDA), A5(SCL), GND | 버튼 입력을 읽어 해당 주소로 명령 전송 |
| Slave 1 | 주소 1, 7번 핀(LED 1) |
A4(SDA), A5(SCL), GND | 1번 주소로 온 데이터를 받아 LED 1 제어 |
| Slave 2 | 주소 2, 7번 핀(LED 2) |
A4(SDA), A5(SCL), GND | 2번 주소로 온 데이터를 받아 LED 2 제어 |
[!NOTE] 브레드보드의 파란색 라인을 공통 GND로 삼고, 두 개의 신호선 레일을 각각 SDA와 SCL로 지정하면 3대 이상의 아두이노도 손쉽게 연결할 수 있습니다.
2. 마스터(Master) 아두이노 스케치

👑 버튼 상태를 읽고 슬레이브 주소를 지정하여 전송
마스터는 8번과 9번 핀의 스위치 값을 읽어 각각 주소 1번과 주소 2번 슬레이브에게 1바이트 상태 값을 송출합니다.
#include <Wire.h> // 아두이노 I2C 표준 라이브러리 포함
const int button1 = 8; // 1번 슬레이브 제어용 푸시버튼 핀
const int button2 = 9; // 2번 슬레이브 제어용 푸시버튼 핀
void setup() {
Wire.begin(); // 주소 없이 호출하여 I2C '마스터' 모드로 시작
Serial.begin(115200); // 디버깅용 시리얼 통신 초기화 (고속 115200 bps)
pinMode(button1, INPUT); // 1번 버튼 핀을 입력으로 설정
pinMode(button2, INPUT); // 2번 버튼 핀을 입력으로 설정
}
void loop() {
// 1. 1번 버튼 읽기 및 슬레이브 1번(주소 1)으로 전송
int x = digitalRead(button1);
Wire.beginTransmission(1); // 주소 1번 슬레이브를 대상으로 패킷 개방
Wire.write(x); // 버튼1의 상태(HIGH/LOW)를 전송 버퍼에 적재
Wire.endTransmission(); // 실제 버스로 데이터 송출 후 종료
Serial.print("button1 = "); Serial.println(x);
delay(100); // 슬레이브 수신 안정화를 위한 짧은 지연
// 2. 2번 버튼 읽기 및 슬레이브 2번(주소 2)으로 전송
int y = digitalRead(button2);
Wire.beginTransmission(2); // 주소 2번 슬레이브를 대상으로 패킷 개방
Wire.write(y); // 버튼2의 상태(HIGH/LOW)를 전송 버퍼에 적재
Wire.endTransmission(); // 실제 버스로 데이터 송출 후 종료
Serial.print("button2 = "); Serial.println(y);
delay(100); // 통신 간격 유지
}
3. 마스터 제어 알고리즘 심층 분석

🔄 주소 분할 다중화(Time-division Addressing)
마스터 아두이노는 하나의 I2C 버스를 시간차를 두고 번갈아 활용하여 서로 다른 슬레이브를 독립적으로 지휘합니다.
sequenceDiagram
autonumber
participant M as Master Arduino
participant Bus as Shared I2C Bus (SDA/SCL)
participant S1 as Slave 1 (Addr 0x01)
participant S2 as Slave 2 (Addr 0x02)
M->>Bus: START + Addr 0x01 (Write) + Button1_Val + STOP
Bus->>S1: 주소 일치! S1만 수신하여 LED 1 토글
Note over S2: 주소 불일치 (0x01 != 0x02), 무시함
M->>Bus: START + Addr 0x02 (Write) + Button2_Val + STOP
Bus->>S2: 주소 일치! S2만 수신하여 LED 2 토글
Note over S1: 주소 불일치 (0x02 != 0x01), 무시함
- 주소 필터링:
- 버스 상의 모든 신호는 모든 슬레이브가 듣지만, 자신의 하드웨어 주소와 일치하지 않는 패킷은 MCU 하드웨어 레벨에서 즉시 무시하므로 CPU 부하가 없습니다.
- 시분할 전송(Time-Sharing):
- 마스터가 슬레이브 1에게 보낸 뒤
delay(100)의 틈을 두고 슬레이브 2에게 순차적으로 전송하므로 버스 충돌이 원천 방지됩니다.
- 마스터가 슬레이브 1에게 보낸 뒤
4. 슬레이브 1(Slave 1) 아두이노 스케치

🏷️ 주소 1번: 1번 마스터의 명령 수신 대기
슬레이브 1은 고유 주소 1번을 부여받고, 수신 콜백 함수를 통해 LED를 즉각 점등/소등합니다.
#include <Wire.h> // I2C 라이브러리
const int pinLED = 7; // 슬레이브 1에 연결된 제어용 LED 핀
void setup() {
Wire.begin(1); // 주소 '1'번 슬레이브 모드로 I2C 버스 참여
Wire.onReceive(receivedEvent); // 마스터 패킷 수신 시 실행할 함수 등록
pinMode(pinLED, OUTPUT); // LED 핀을 출력으로 설정
}
void loop() {
delay(100); // 메인 루프는 비워두어도 하드웨어 인터럽트로 이벤트가 실행됩니다.
}
// 마스터로부터 1번 주소 패킷이 도착했을 때 자동 실행되는 함수
void receivedEvent(int howmany) {
int val = Wire.read(); // 버퍼에서 마스터가 보낸 버튼 상태 값 꺼내기
// 버튼 배선 상태에 따라 LED 출력 반영
digitalWrite(pinLED, val == HIGH ? LOW : HIGH);
}
5. 슬레이브 2(Slave 2) 아두이노 스케치

🏷️ 주소 2번: 2번 마스터의 명령 수신 대기
슬레이브 2는 슬레이브 1과 코드 구조가 99% 동일하지만, I2C 주소를 2번으로 설정하여 자신만의 명령만 선별 수신합니다.
#include <Wire.h> // I2C 라이브러리
const int pinLED = 7; // 슬레이브 2에 연결된 제어용 LED 핀
void setup() {
Wire.begin(2); // 주소 '2'번 슬레이브 모드로 I2C 버스 참여!
Wire.onReceive(receivedEvent); // 수신 이벤트 핸들러 등록
pinMode(pinLED, OUTPUT); // LED 핀을 출력으로 설정
}
void loop() {
delay(100);
}
void receivedEvent(int howmany) {
int val = Wire.read(); // 2번 주소로 전송된 데이터 바이트 수신
digitalWrite(pinLED, val == HIGH ? LOW : HIGH);
}
6. 다중 I2C 통신의 핵심 설계 원칙

💡 도로시를 위한 지니의 3대 설계 꿀팁
- 고유 주소 보장 (Unique Address):
- 버스 내 모든 디바이스는 고유한 7비트 주소를 가져야 합니다. 만약 실수로 슬레이브 둘 다
Wire.begin(1)로 설정하면 신호가 충돌하여 통신이 마비됩니다.
- 버스 내 모든 디바이스는 고유한 7비트 주소를 가져야 합니다. 만약 실수로 슬레이브 둘 다
- 이벤트 핸들러 경량화 (Lightweight ISR):
receivedEvent()내부에서는Serial.println()이나delay()같은 지연 요소를 최소화하고, 수신된 값을 전역 변수에 저장한 뒤loop()에서 무거운 작업을 처리하는 것이 정석입니다.
- 확장성의 마법:
- 슬레이브가 3대, 4대, 10대로 늘어나도 아두이노의 물리적 통신 핀은 여전히 A4(SDA)와 A5(SCL) 2개만 사용합니다!
🌟 도로시와 지니의 마무리 요약!

- 👧 도로시: “마스터가
Wire.beginTransmission(1)과Wire.beginTransmission(2)로 주소만 바꿔주면 수많은 아두이노를 각각 따로 움직일 수 있구나!” - 🐱 지니: “맞아! 이것이 바로 현대 컴퓨터 메인보드와 스마트폰 내부 센서들이 서로 소통하는 I2C 버스의 위대한 원리란다.”
- 🐶 토토: “바우! 이제 다음 단계에서는 4개의 핀으로 더 빠르게 통신하는 고속 오케스트라, SPI 통신으로 떠나보자 멍!”
서브목차