중급이라고 하기에는 조금 쉬운 주제지만 많이 햇갈려하는 사람들이 많기 때문에 이번에는 아두이노 메가 즉, ATmega2560 기반의 보드에서 시리얼 통신을 많이 사용하는 방법에 대해 이야기를 해보자.
우리가 보통 아두이노를 이용해서 유선 통신을 한다라고 하면 기본적으로 3가지 방식을 떠올리게 된다.
I2C(I²C, Inter-Integrated Circuit) 통신, SPI(Serial Peripheral Interface) 통신, USART(Universal Synchronous Asynchronous Receive Transmitter) 통신이 바로 그것이다.
이 세 가지 통신은 기본적으로 마이크로컨트롤러에 포함되어 있는 대표적인 직렬(Serial) 통신 방식이다.
각각의 통신은 연결 구조와 데이터 처리 방식에 차이가 있지만, 공통적으로 데이터를 순차적으로 전송하는 면에서는 동일하다.
이러한 통신 중에서 아두이노가 가장 기본적으로 사용하고 있는 통신이 USART 통신으로 아두이노 보드에 표시된 RX TX가 이 통신을 사용하기 위한 전용 데이터 핀에 해당한다.
뭐, 조금 더 자세히 설명하자면 USRAT 통신은 동기, 비동기 방식을 모두 지원하는 통신이고 아두이노에서 사용하는 통신은 비동기 전용 통신 방식으로 UART(Universal Asynchronous Receiver Transmitter)방식에 해당한다.
여기서 동기와 비동기는 통신의 타이밍에 대한 내용인데 송신부와 수신부가 같은 클럭을 공유하는 방식을 동기, 별도의 클럭 신호 없이 약속된 속도에 맞춰 데이터를 주고 받는 방식을 비동기라고 한다.
동기 방식은 서로 클럭을 공유하기 때문에 데이터 전송 타이밍이 매우 정확하지만 클럭을 공유하기 위해 선이 추가되기 때문에 구조가 조금 더 복잡해진다.
비동기 방식은 구조는 간단하지만 데이터의 시작을 알리는 Start bit와 끝을 알리는 Stop bit를 이용해 데이터의 경계를 구분해야하기 때문에 전송 데이터에 추가적인 부가 정보(오버헤드)가 필요하다.
그래서 간단히 요약하자면 아두이노는 USART 통신의 비동기 모드만 사용하는 UART 통신을 사용한다.
그리고 이 UART 통신을 사용하기 위해 만들어진 라이브러리가 Serial 이기 때문에 다른 자료들에선 아두이노에서 직렬 통신 또는 시리얼 통신이라고 하면 UART 통신을 의미하는 경우가 많다.
다시 아두이노 보드로 돌아와서 아두이노 보드에서 사용하는 UART 통신은 기본적으로 0번과 1번 핀을 이용하여 동작한다.

그림과 같이 아두이노 보드를 보면 0번과 1번 핀 옆에 RX, TX가 표시되어 있는데 각각 Receive data, Transmit data를 뜻한다.
데이터를 보내는 핀, 받는 핀이라는 의미로 UART 통신은 기본적으로 이 두 가지 데이터 핀을 이용해서 데이터를 주고 받는다.
그래서 수신부의 TX핀과 송신부의 RX핀, 수신부의 RX핀과 송신부의 TX핀을 연결해서 서로 데이터를 주고 받는 모양새로 선을 연결하게 되고 추가로 서로의 전원을 연결하는 방식으로 통신을 하게 된다.
이런 내용을 알고 아두이노 우노 보드를 구입해서 다른 보드와 UART 통신을 이용해서 통신을 하려고 해보면 사실 잘 안되는 경우가 많을 것이다.
그 이유는 아두이노 보드의 0번과 1번 핀은 기본적으로 컴퓨터와 통신을 하는데 사용되고 있기 때문이다.(실제론 ATmega16u2와 통신하고 있지만 편의상 컴퓨터와 하고 있다고 하자.)
컴퓨터에서 코딩을 하고 업로드 버튼을 누르면 아두이노 보드의 중앙 부분에 있는 RX TX라고 적혀 있는 LED가 불빛을 내는데 이 불빛이 지금 0번과 1번 핀으로 데이터를 주고 받고 있다는 것을 의미하는 것이다.
그래서 0번과 1번 핀에 다른 센서나 전자소자 같은 것들을 연결했다면 컴퓨터에서 업로드, 시리얼 모니터 등이 정상동작 하지 않는 것을 볼 수 있다.
이렇게 되면 ‘그럼 아두이노 보드에서 UART 통신은 사용할 수 없는 것 아니야?’라는 의문이 들 것이다.
맞는 말은 아니지만 사용하기 힘든건 사실이다.
그렇기 때문에 아두이노에서는 소프트웨어 적으로 RX, TX 역할을 할 수 있게 라이브러리를 제공하고 있다.
그게 바로 ‘SoftwareSerial’ 라이브러리다.
이 라이브러리는 기존의 RX, TX 역할을 하던 0번과 1번 핀 대신에 RX와 TX 역할을 할 데이터 핀을 지정하여 그 데이터 핀을 통해 UART 통신을 할 수 있게 해준다.
다만, 모든 데이터 핀이 RX, TX 역할을 할 수 있는 것은 아니다.
엄밀히 말하면 RX 역할인데 우노(ATmega328 기반) 보드의 경우엔 모든 데이터 핀을 RX 역할에 지정할 수 있지만 다른 보드의 경우에는 지정할 수 있는 데이터 핀이 정해져 있다.
메가(ATmega2560) 보드의 경우 10, 11, 12, 13, 14, 15, 50, 51, 52, 53, 62, 63, 64번 핀이 지정가능하고 레오나르도(ATmega32u4) 보드의 경우 8, 9, 10, 11, 14, 15, 16번 핀을 사용할 수 있다.
그래서 일명 이 소프트웨어 시리얼을 사용하려면 자기가 사용하는 보드에선 어떤 데이터 핀을 사용할 수 있는지 확인을 해야 한다.
다시 본론으로 돌아와서 소프트웨어 시리얼을 사용하면 0번과 1번 핀을 이용하지 않고도 UART 통신을 사용할 수 있다.
하지만 소프트웨어 시리얼이라도 완벽하지는 않다.
앞에서 이야기한 0번 1번과 같이 마이크로컨트롤러에서 지원해주는 하드웨어 시리얼은 데이터 처리를 마이크로컨트롤러 내부의 할당된 구역에서 정확한 타이밍에 맞춰서 처리하지만 소프트웨어 시리얼은 코딩을 통해 직접 타이밍을 계산하고 데이터가 처리되는 구조를 가지고 있다.
그래서 속도가 높을수록 안정성이 떨어지고 오차가 발생하기 쉽다.
이런 이유로 소프트웨어 시리얼 관련 자료들을 찾아보면 115200 이상으로 속도를 설정하지 않는 것을 권장한다는 말이 많다.
(특히 2010년 대 초반에 이런 말이 많이 있었다. 현재는 많이 개선 되었다고 한다. 그리고 일부 보드의 경우 속도가 제한되어 있으니 만약 특이한 보드를 사용하고 있다면 확인해야 한다. https://docs.arduino.cc/learn/built-in-libraries/software-serial/?queryID=undefined)
그리고 소프트웨어를 이용해서 구현했기 때문에 동시에 여러 개의 소프트웨어 시리얼을 만들어서 사용하기 어렵다.
물론 이것을 지원하는 함수가 있지만, 기본적으로 소프트웨어 시리얼의 동시 사용은 데이터의 충돌이나 손실이 발생하기 쉬운 구조인 것이 사실이다.
그렇기 때문에 UART를 여러 개 사용해야 하는 경우나 속도를 빠르게 해야하는 경우엔 하드웨어 시리얼을 사용하는 것을 권장한다.
그리고 이런 경우 일반적으로 사용하는 것이 ATmega2560 기반의 아두이노 메가 보드다.

아두이노 메가 보드는 우노 보드와 달리 RX TX 역할을 하는 핀이 3개 더 존재한다.
그림에 표시된 부분인데 14번 15번은 TX3, RX3 16번 17번은 TX2, RX2, 18번 19번은 TX1, RX1 라고 적혀있는 것을 볼 수 있다.
이 핀들이 0번 1번 핀과 마찬가지로 UART 통신 즉, Serial 라이브러리를 사용할 수 있는 핀이라고 할 수 있다.
그럼 어떻게 사용할까.
일반적으로 아두이노에서 Serial 라이브러리를 사용하는 것을 생각해보면 Serial.begin과 같이 사용한다.
그렇다면 이 핀들은 어떻게 사용하면 될까.
정답은 앞에 RX, TX 핀을 표시해둔 형태와 같다.
RX1, TX1을 사용해서 통신을 한다면 함수는 Serial1.begin과 같이 바뀌게 되고
RX2, TX2를 사용하면 Serial2.begin을 RX3, TX3을 사용하면 Serial3.begin이 되는 것이다.
다른 함수들도 마찬가지다.
시리얼 모니터와 같이 기존의 컴퓨터와 통신에 사용되는 것은 Serial을 다른 하드웨어 시리얼 핀을 사용했다면 Serial1, Serial2, Serial3과 같이 붙여서 사용하면 되는 것이다.
거기에 소프트웨어 시리얼과 다르게 여러 개를 동시에 사용해도 된다는 큰 장점을 가지고 있다.
간단한 예를들어 코드 1은 아두이노 메가에 블루투스(HC-06)을 연결하고 통신하는 코드다.
이때 블루투스는 Serial1에 연결했다.
만약 기존에 SoftwareSerial을 사용한 코드가 있다면 비교해보는 것도 좋을 것이다.

void setup() {
Serial.begin(9600);
Serial1.begin(9600);
}
void loop() {
if (Serial1.available()) {
char receivechar = Serial1.read();
Serial.write(receivechar);
}
if (Serial.available()) {
char transmitchar = Serial.read();
Serial1.write(transmitchar);
}
}