고급 예제

다양한 도구들을 가지고 마음껏 응용해보세요.

아두이노 간 통신하기 - 1:N I2C 통신

2015-04-01 17:19:56

개요

 

아두이노 간 통신하기 - UART, I2C, SPI 컨텐츠를 통해 아두이노 간의 1:1 통신을 해보았습니다.

 

3가지 통신 방법의 대한 장점, 단점과 연결하는 방법을 보았는데, 이번 컨텐츠에서는 3가지 방법중 I2C를 이용하여 오렌지 보드 간의 1:N 통신을 해보겠습니다.

 

 * I2C를 이용하여 1:N 통신을 한 이유는 UART는 1:N통신이 불가능하며, SPI 통신으로는 필요한 핀의 갯수도 많을 뿐 아니라 아두이노 SPI라이브러리에서 슬레이브 모드를 지원하지 않아서 직접 레지스터를 통해 제어를 해야하므로 복잡합니다. 그래서 1:N 통신을 지원하고, 연결 및 코딩이 쉬운 I2C를 이용하여 1:N 제어를 해보겠습니다.

 

 

동영상 미리보기

 

 

 

 

부품 목록

 

NO 부품명 수량 상세설명
1 오렌지 보드 5 Master 1개, Slave 4개
2 10kΩ 저항 2  
3 점퍼케이블 22  
4 브레드보드 1  

 

부품명 오렌지 보드 10kΩ저항 점퍼케이블 브레드보드
파트

 

 

하드웨어 Making

 

브레드보드 레이아웃

 

 

회로도

 

 

 

오렌지 보드 연결

 

오렌지 보드에 A4번(SDA)핀 끼리, A5번(SCL)핀 끼리 다 연결해 줍니다. (두번째 사진과 같이 브레드 보드 한줄에 같은 핀끼리 연결해 주시면 됩니다.)

 

5V와 GND도 같이 연결해 주시고, 5V에서 A4번핀 연결한 곳과, A5번핀 연결한 곳에 10kΩ저항을 각각 연결해 줍니다.(5V와 A4번핀 연결한 곳에 저항 하나, 5V와 A5번핀 연결한 곳에 저항 하나 연결해 줍니다.)

 

 

 

소프트웨어 Coding

 

 

Master

 

#include <Wire.h>

// 슬레이브 주소
int SLAVE[4] = {1, 2, 3, 4}; 

void setup() {
	// Wire 라이브러리 초기화
	Wire.begin(); 
	// 직렬 통신 초기화
	Serial.begin(9600); 
}

void loop() {
	if (Serial.available()) {
		char e = Serial.read();
		byte c = e - 48;
		if (c < 5) {
			// I2C 통신을 통한 전송 시작
			Wire.beginTransmission(SLAVE[c-1]);
			// 슬레이브 주소를 시리얼 창에 입력시 해당 시리얼 주소로 'o'문자와 데이터 요구를 보냅니다.
			Wire.write('o');
			Wire.write('\n');
			Wire.endTransmission(SLAVE[c-1]);
			
			delay(10);
			// 슬레이브로 데이터 요구 및 수신 데이터 처리
			i2c_communication(c-1); 
			delay(10);
		}
	}
}

void i2c_communication(int i) {
	// 12 바이트 크기의 데이터 요청
	Wire.requestFrom(SLAVE[i], 12); 
	// 12 바이트 모두 출력할 때까지 반복
	for (int j = 0 ; j < 12 ; j++) {  
		// 수신 데이터 읽기
		char c = Wire.read(); 
		// 수신 데이터 출력
		Serial.print(c); 
	}
	Serial.println();
}

 

 

Slave

(1~4 전부 코드는 같습니다. 각 Slave마다 번호와 Master에게 보내줄 문자열만 수정하시면 됩니다. 수정하실 부분은 강조 표시해 놓겠습니다.)

 

#include <Wire.h>

// 자신의 슬레이브 주소를 설정해 줍니다.(슬레이브 주소에 맞게 수정해야 합니다.)
#define SLAVE 4  

// 카운터
byte count = 0; 
char temp;

void setup() {
	// Wire 라이브러리 초기화
	// 슬레이브로 참여하기 위해서는 주소를 지정해야 한다.
	Wire.begin(SLAVE);
	Wire.onReceive(receiveFromMaster);
	// 마스터의 데이터 전송 요구가 있을 때 처리할 함수 등록
	Wire.onRequest(sendToMaster);
	pinMode(13, OUTPUT);
	Serial.begin(9600);
}

void loop () {
	// 요청이 들어오면 13번 LED를 0.5초간 켜줍니다.
	if (temp == 'o') { 
		play();
	}
}

void receiveFromMaster(int bytes) {
	char ch[2];
	for (int i = 0 ; i < bytes ; i++) {
		// 수신 버퍼 읽기
		ch[i] = Wire.read(); 
	}
	temp = ch[0];
}

void play() { 
	digitalWrite(13, HIGH);
	delay(500);
	digitalWrite(13, LOW);
	temp = 0;
}

void sendToMaster() {
	// 자신의 슬레이브 주소를 담은 메세지를 마스터에게 보냅니다. 슬레이브 주소에 맞게 수정해야 합니다.
	Wire.write("4th Arduino ");
}

 

 

소프트웨어 설명

 

Master

	if (Serial.available()) {
		char e = Serial.read();
		byte c = e - 48;
		if (c < 5) {
			// I2C 통신을 통한 전송 시작
			Wire.beginTransmission(SLAVE[c-1]);
			// 슬레이브 주소를 시리얼 창에 입력시 해당 시리얼 주소로 'o'문자와 데이터 요구를 보냅니다.
			Wire.write('o');
			Wire.write('\n');
			Wire.endTransmission(SLAVE[c-1]);
			
			delay(10);
			// 슬레이브로 데이터 요구 및 수신 데이터 처리
			i2c_communication(c-1); 
			delay(10);
		}
	}

 

마스터에서 데이터를 요구할 슬레이브 주소를 시리얼 모니터를 통해 입력 받으면 해당 주소의 슬레이브에게 데이터를 요청하는 부분입니다.

요청시 우선 'o'문자를 보내고 i2c_communication 함수를 호출합니다.

 

void i2c_communication(int i) {
	// 12 바이트 크기의 데이터 요청
	Wire.requestFrom(SLAVE[i], 12); 
	// 12 바이트 모두 출력할 때까지 반복
	for (int j = 0 ; j < 12 ; j++) {  
		// 수신 데이터 읽기
		char c = Wire.read(); 
		// 수신 데이터 출력
		Serial.print(c); 
	}
	Serial.println();
}

 

i2c_communication 함수가 호출이 되면 해당 슬레이브 주소로 12바이트의 데이터를 요청한 후, 데이터를 1byte씩 받으면서 시리얼 모니터에 출력합니다.(12byte요청하였으므로, 12byte다 받을때 까지 계속 출력합니다.)

 * 슬레이브에서 마스터로 데이터를 보낼 경우 1byte 데이터들을 보내야 합니다. 2byte를 보낼 경우 마스터에서 추가 작업을 해주어야 합니다.(int 값 같은 변수들은 2byte입니다.)

 

 

Slave

 

	// Wire 라이브러리 초기화
	// 슬레이브로 참여하기 위해서는 주소를 지정해야 한다.
	Wire.begin(SLAVE);
	Wire.onReceive(receiveFromMaster);
	// 마스터의 데이터 전송 요구가 있을 때 처리할 함수 등록
	Wire.onRequest(sendToMaster);

 

setup 함수 안에 내용입니다. 자신의 슬레이브 주소를 지정하고 마스터에서 데이터 요청이나 전송이 있을 경우 해당 함수를 실행 합니다.

데이터 전송 받을 시 receiveFromMaster 함수를 데이터 전송 요청시 sendToMaster 함수를 실행 합니다.

 

void sendToMaster() {
	// 자신의 슬레이브 주소를 담은 메세지를 마스터에게 보냅니다. 슬레이브 주소에 맞게 수정해야 합니다.
	Wire.write("4th Arduino ");
}

 

마스터로 부터 데이터 요청시 자신의 슬레이브 주소를 담은 메세지를 보냅니다. 마스터에서 12byte를 요청하므로, 슬레이브에서도 12바이트를 보낼 수 있도록 합시다. 

 

위에 코드를 각각 업로드 후 실행을 하면 동영상 미리보기의 GIF파일과 같이 슬레이브 숫자를 입력하면 해당 슬레이브에게 요청을 하고 데이터를 전송 받아 모니터 창에 출력을 하게 됩니다.

슬레이브에서는 자신에게 데이터 요청이 오면 13번 LED에 불을 0.5초간 키고 끕니다. 자세한 동작은 위의 동영상 미리 보기를 참고해 주세요.

(시리얼 모니터에서 데이터를 받을 때 1byte씩 데이터를 받으므로 1234 이런 식으로 입력해도 1, 2, 3, 4번 슬레이브에게 각각 요청을 하게 됩니다.)

 

kocoafabeditor

항상 진취적이고, 새로운 것을 추구하는 코코아팹 에디터입니다!

I2C, 아두이노, 오렌지보드

제임스짱 2015-04-02 10:19:05

아두이노 1:N 통신이 항상 궁금했는데 감사합니다!

백승찬 2016-03-12 14:27:09

A4와 A5를 각각 5V에 연결하는 이유가 무엇인지 알고싶습니다. 굳이 연결하지 않고 그냥 A4는 A4끼리 A5는 A5끼리 연결해도 상관없지 않나요??

미키 2016-03-14 08:41:37

I2C통신라인인 A4, A5를 풀업 저항을 통해 5V로 연결한 것입니다. 풀업저항은 해당 라인의 레벨(High)을 한쪽으로 안정시키고자 하는 의미가 있습니다. 또한 I2C데이터 라인은 양방향 통신을 하며 하드웨어 적으로 Short가 발생하지 않도록 Open-Drain을 사용 합니다. Open Drain이란 간단히 말해서 전압이 실려있는 않은 단순한 스위치(ON/OFF)의 상태를 나타냅니다. 따라서 풀업(Pull-Up)저항이 꼭 필요합니다.(요즘은 마이컴 내부에서서 풀업저항을 달아 나온것도 있답니다. 하지만 두개 이상의 I2C 센서를 연결 할 때에는 외부에 풀업저항을 달아서 안정화 시켜주는 것이 더 좋습니다.)

정문일 2021-09-30 22:48:15

A4와 A5를 5V 전압에 연결할 때 10K옴 저항이 사용되는 이유는 뭘까요? 만약 아두이노의 개수가 늘어난다면 저항도 같이 늘어나야하나요?