Arduino

Serial 내부 라이브러리

기하 2022. 3. 13. 14:24

https://blog.daum.net/rockjjy99/2694

 

 

안녕하세요.

아두이노의 시리얼 통신을 처리하는 Serial 내부 라이브러리(Stream 베이스클래스)를 정리하겠습니다.

 

저 또한 개발 시 참고하고자 정리합니다.

 

아두이노 우노는 시리얼 통신을 위한 RX/수신(0번 핀)과 TX/송신(1번 핀) 물리적인 통신채널을 1개 가지고 있습니다. (아두이노 메가2560의 경우 3채널을 가지고 있습니다.)

 

참고로 아두이노의 시리얼 버퍼 크기는 64 바이트입니다.

아두이노의 시리얼 데이터는 모두 이 시리얼 버퍼에 쓰거나 읽어서 데이터를 송수신합니다.

 

또한 SoftwareSerial 라이브러리를 활용하면 일반 I/O 핀을 시리얼 통신 핀으로 확장해서 사용할 수 있으나 다음 기회에 다루겠습니다.

 

아두이노의 시리얼 통신 기능은 매우 유용합니다.

 

기본적으로 개발 시 PC와 연결되어 아두이노 스케치를 업로드하거나, 디버깅을 할 수 있습니다.

또한 외부 PC나 임베디드 시스템, 외부 시리얼 장치 등과 시리얼 통신을 하는데 사용합니다.

 

지금부터 시리얼 통신에 관한 아두이노 함수를 살펴보겠습니다.

 

 

1. Serial.begin()

첫 번째 살펴 볼 함수로 아두이노의 setup() 함수 내에 선언하여 시리얼 포트를 초기화할 때 사용합니다.

Serial.begin(speed)가 주로 사용되며,

시리얼 통신 규격을 변경할 경우 Serial.begin(speed, config)를 사용합니다.

 

함수.

Serial.begin(speed)

Serial.begin(speed, config)

 

매개변수.

speed : 시리얼 통신 속도, 보-레이트(Baud-Rate) 값 설정

config : 시리얼 통신 규격 설정

SERIAL_8N1(기본 설정 값으로 "8N1"은 8비트 데이터, 패리티 없음, STOP 비트 1)

 

샘플코드1.

void setup()                                                                                                                                       
{
  Serial.begin(9600);
  while(!Serial);
}

Serial.begin() 함수로 통신속도를 설정합니다.

while문 내의 Serial은 시리얼 포트가 준비되면 TRUE를 반환합니다.

 

샘플코드2.

void setup()                                                                                                                                       
{
  Serial.begin(9600, SERIAL_8N1);
  while(!Serial);
}

 

Serial.begin() 함수의 config 파라메타는 시리얼통신 규격을 설정합니다.

설정할 수 있는 config 값은 다음과 같습니다.

 

SERIAL_5N1, SERIAL_6N1, SERIAL_7N1, SERIAL_8N1 (the default), SERIAL_5N2, SERIAL_6N2,

SERIAL_7N2, SERIAL_8N2, SERIAL_5E1, SERIAL_6E1, SERIAL_7E1, SERIAL_8E1, SERIAL_5E2
SERIAL_6E2, SERIAL_7E2, SERIAL_8E2, SERIAL_5O1, SERIAL_6O1, SERIAL_7O1, SERIAL_8O1
SERIAL_5O2, SERIAL_6O2, SERIAL_7O2, SERIAL_8O2

 

 

2. Serial.end()

RX와 TX 핀을 시리얼 포트를 사용하지 않고 일반 I/O 핀으로 설정할 때 사용합니다.

아두이노가 동작 중에 Serial.begin()으로 사용하다가

특정 조건에서 디지털 I/O로 변경하여 사용할 경우 유용합니다.

다시 사용할 때는 적정 루틴 안에서 Serial.begin()으로 초기화합니다.

 

함수.

Serial.end();

 

매개변수.

없음.

 

샘플코드1.

void setup()                                                                                                                                       
{
  Serial.begin(9600);
  while(!Serial);
}
 
void loop()
{
  Serial.write("APPSKIT");
  delay(1000);
  Serial.end();
}

위 예제에서는 Serial.end()를 만나기 전 "APPSKIT" 문자열을 출력 후, Serial 기능이 해제됩니다.

 

 

3. Serial.print()/Serial.println()

시리얼 포트로 전송할 데이터를 ASCII 코드 문자(열)로 전송할 때 사용합니다.

통신 하고자 하는 대상이 ASCII 문자(열)로 전송해야 할 경우 유용하며,

아두이노 개발환경 내 시리얼 모니터의 경우가 대표적인 예가 됩니다.

 

*Serial.println()은 데이터 끝에 캐리지리턴('\r')과 뉴라인('\n')을 붙여 전송합니다.

 

함수.

Serial.print(value);

Serial.print(value, format);

Serial.println(value);

Serial.println(value, format);

 

매개변수.

value : ASCII 문자로 변환하여 전송할 값 또는 문자(열)

format : ASCII 문자 형식, BIN(2진수), OCT(8진수), DEC(10진수), HEX(16진수)

 

샘플코드1. (딱 한번 실행을 위해 Serial.end() 사용)

void setup()                                                                                                                                       
{
  Serial.begin(9600);
  while(!Serial);
}
 
void loop()
{
  int nValue = 10;
 
  Serial.print("VAL: ");
  Serial.println(nValue);
 
  Serial.print("BIN: ");
  Serial.println(nValue, BIN);
 
  Serial.print("OCT: ");
  Serial.println(nValue, OCT);
 
  Serial.print("DEC: ");
  Serial.println(nValue, DEC);
 
  Serial.print("HEX: ");
  Serial.println(nValue, HEX);
 
  Serial.end();
}

 

4. Serial.write()

시리얼 포트로 전송할 데이터를 그대로 전송할때 사용합니다.

Serial.print()는 ASCII 문자로 변환하여 전송하는 반면

Serial.write()는 변환하지 않고 그대로 전송합니다.

 

함수.

Serial.write(value);

Serial.write(string);

Serial.write(buffer, buffer_length);

 

매개변수.

value : 전송할 값, 한 바이트

string : 전송할 문자열

buffer: 전송할 값 배열(주소)

buffer_length : 전송할 배열의 데이터 수

 

 

샘플코드1. Serial.Write(value)를 통해 값 전송

void setup()                                                                                                                                       
{
  Serial.begin(9600);
  while(!Serial);
}
 
void loop()
{
  int nValue = 65; 
  Serial.write(nValue);  
  Serial.end();
}

위 예제에서는 nValue 값을 65로 설정했습니다.

아두이노 시리얼모니터는 ASCII 값으로 출력하기 때문에 'A'라는 문자가 출력됩니다.

 

샘플코드2. Serial.Write(string)를 통해 문자열 전송
void setup(){
  Serial.begin(9600);
  while(!Serial);
} 
void loop() {
  Serial.write("Hello, APPSKIT!");  
  Serial.end();
}

위 예제에서는 Serial.write(string)의 string은 " "로 인해 컴파일러가 문자열로 바꿔 전송합니다.

 

샘플코드3. Serial.Write(buffer, buffer_length)를 통해 배열 값 전달

void setup() {
  Serial.begin(9600);
  while(!Serial);
}
 
void loop(){
  char nArr[8] = { 'A', 'P', 'P', 'S', 'K', 'I', 'T', '\n' };
  Serial.write(nArr, sizeof(nArr));  
  Serial.end();
}

위 예제에서는 Serial.write() 함수를 통해 배열 값을 전달합니다.

아두이노 시리얼모니터는 ASCII 값을 표시합니다.

ASCII 코드 값을 제외한 정수 값이 전달될 경우 표시되지 않습니다.

 

 

5. Serial.available()

 함수로 아두이노 시리얼 버퍼에 입력된 데이터의 수를 반환합니다.

정확히 설명하면 시리얼 포트로 입력된 데이터 수(바이트)를 리턴합니다.

 

함수.

Serial.available()

 

매개변수.

없음.

 

샘플코드1. 입력 받은 시리얼 데이터를 바이트 단위로 다시 전송

void setup() {
  Serial.begin(9600);
  while(!Serial);
}
 
void loop() {
  if (Serial.available() > 0){
    Serial.write(Serial.read());
  }
}

위 예제에서는 아두이노 시리얼 버퍼에 데이터가 쌓였을 때 읽어서 그대로 보냅니다.

시리얼 버퍼에 데이터가 있을 경우 바이트 단위로 읽어서 전송합니다.

 

6. Serial.read()

시리얼 버퍼에서 한 바이트의 데이터를 읽어 올 때 사용합니다.

 

Serial.readBytes() 함수는

한번에 지정된 수의 데이터를 시리얼 버퍼에서 읽어 올 때 사용합니다.

 

Serial.readBytesUntil() 함수는

종료 문자를 기준으로 지정된 수의 데이터를 시리얼 버퍼에서 읽어 올 때 사용합니다.

 

Serial.Timeout() 함수는

Serial.readBytes() 함수, Serial.readBytesUntil() 함수

그리고 Serial.find() 함수와 Serial.findUntil() 함수와 와 같이 사용하며,

호출하지 않으면 디폴트 1초로 설정되어 있습니다.

 

*Serial.readBytes() 함수와 Serial.readBytesUntil() 함수는 수신 데이터가 초과될 경우 나눠서 저장

 

함수.

Serial.read();

Serial.Timeout(time);

Serial.readBytes(buffer, buffer_length);

Serial.readBytesUntil(character, buffer, buffer_length);

 

매개변수.

buffer: 입력받은 데이터를 저장할 배열(주소)

buffer_length : 저장할 데이터 수

character : 종료 문자, 읽어올 문자열의 가장 마지막 문자

time : 밀리 초 지연시간

 

샘플코드1. 입력받은 시리얼 데이터를 바이트 단위로 다시 전송

void setup() {
  Serial.begin(9600);
  while(!Serial);
}
 
void loop(){
  if (Serial.available() > 0) Serial.write(Serial.read()); 
}

 

샘플코드2. 일정 수 데이터를 읽고 배열에 저장 후 다시 전송

void setup() {
  Serial.begin(9600);
  while(!Serial);
  Serial.setTimeout(5);
}
 
void loop(){
  char rInput[16];
  int nLength = 0; 
  if (Serial.available() > 0)  {
    nLength = Serial.readBytes(rInput, sizeof(rInput));
    Serial.println(nLength);
  }
 
  if (nLength > 0) {
    for (int i = 0; i < nLength; i++) Serial.write(rInput[i]);
    Serial.println();
  }
}

*Serial.readBytes()와 Serial.readBytesUntil() 함수는 Serial.Timeout()함수와 같이 사용합니다.

 

위 예제에서는 시리얼 버퍼에 데이터가 있을 때,

5밀리초동안 데이터를 읽어 배열에 저장합니다.

Serial.readBytes() 함수는 읽은 데이터의 수를 반환합니다.

 

샘플코드3. 종료 문자를 기준으로 데이터를 읽고 배열에 저장 후 다시 전송

void setup() {
  Serial.begin(9600);
  while(!Serial);
  Serial.setTimeout(5);
}
 
void loop() {
  char rInput[16];
  int nLength = 0;
 
  if(Serial.available() > 0)  {
    nLength = Serial.readBytesUntil(':', rInput, sizeof(rInput));
    Serial.println(nLength);
  }
 
  if (nLength > 0)
  {
    for (int i = 0; i < nLength; i++) Serial.write(rInput[i]);
    Serial.println();
  }
}

위 예제에서는 ':'를 만날 때까지 시리얼 버퍼에서 읽어 그 수를 반환하고 배열에 저장합니다.

만일 ':'를 만나지 않으면 시리얼 버퍼가 비워질 때까지 읽고 저장합니다.

 

7. Serial.parseInt() Serial.parseInt()

아스키 문자로 된 숫자를 정수로 변환시키는 함수입니다.

Serial.parseInt() 함수는 시리얼 버퍼에 저장된 ASCII 정수 문자를 정수 숫자로 반환합니다.

Serial.parsefloat() 함수는 시리얼 버퍼에 저장된 ASCII 소수 문자를 소수 숫자로 반환합니다.

 

함수.

Serial.parseInt()

Serial.parseFloat();

 

매개변수.

없음.

 

 

샘플코드1. 입력받은 ASCII 문자 숫자를 정수 숫자로 변환

void setup()                                                                                                                                       
{
  Serial.begin(9600);
  while(!Serial);
}
 
void loop()
{
  int nValue = 0;
 
  if(Serial.available() > 0)
  {
    nValue = Serial.parseFloat();
    Serial.print("INT: ");
    Serial.println(nValue);
  }
}

위 예제에서는 ASCII 문자 숫자로 입력답은 데이터를 정수 숫자로 변환합니다.

 

 

샘플코드2. 입력받은 ASCII 문자 숫자를 소수 숫자(소수점 두자리)로 변환

void setup()                                                                                                                                       
{
  Serial.begin(9600);
  while(!Serial);
}
 
void loop()
{
  float fValue = 0;
 
  if(Serial.available() > 0)
  {
    fValue = Serial.parseFloat();
    Serial.print("FLOAT: ");
    Serial.println(fValue);
  }
}

 

8. Serial.peek()

시리얼 버퍼를 비우지 않고 미리 확인하는 함수입니다.

Serial.peek() 함수는 시리얼 버퍼에 저장된 ASCII 문자 하나를 읽습니다.

Serial.read() 함수의 경우 읽고 나면 자동으로 시리얼 버퍼 하나가 사라지지만,

Serial.peek() 함수의 경우 시리얼 버퍼가 그대로 남아 있습니다.

 

Serial.peek() 함수의 리턴 값은 시리얼 버퍼의 맨 앞자리 값이 반환되며, 없으면 -1을 반환합니다.

 

함수.

Serial.peek()

 

매개변수.

없음.

 

 

샘플코드1. 입력받은 시리얼 버퍼의 맨 앞 바이트를 확인 후 저장

void setup()                                                                                                                                       
{
  Serial.begin(9600);
  while(!Serial);
  Serial.setTimeout(5);
}
 
void loop()
{
  char rInput;
  char rStr[16];
  int nLength;
 
  if(Serial.available() > 0)
  {
    rInput = Serial.peek();
   
    if (rInput != '*')
      while(Serial.available()) Serial.read();
    else
    {
      nLength = Serial.readBytes(rStr, sizeof(rStr));
      for (int i = 0; i < nLength; i++) Serial.print(rStr[i]);
      Serial.println();
    }
  }
}

위 예제에서는 시리얼 버퍼의 맨 앞 바이트를 확인해서 '*'가 아닌 경우 시리얼 버퍼를 비웁니다.

만일 '*'가 있다면 '*'를 포함하여 시리얼 버퍼를 모두 읽어 배열에 저장 후 출력합니다.

 

 

9. Serial.find()   Serial.findUntil()

Serial.find() 함수는 시리얼 버퍼에서 검색하고자 하는 문자 하나 또는 문자열을 찾습니다.

만일 해당 문자 또는 문자열이 찾으면, true를 반환 하여 그 뒤 데이터가 시리얼 버퍼에 남게 됩니다.

 

시리얼 버퍼에 해당 문자 또는 문자열이 없으면 false를 반환하며 시리얼 버퍼는 비워집니다.

함수.

Serial.find(target)

Serial.findUntil(target, terminal)

 

매개변수.

target: 찾으려는 처음 문자 또는 문자열

terminal: 찾으려는 마지막 문자 또는 문자열

 

 

샘플코드1. 시리얼 버퍼를 확인 후 "++ID:" 문자열이 확인되면 그 뒤를 정수로 변환하여 출력

void setup()                                                                                                                                       
{
  Serial.begin(9600);
  while(!Serial);
  Serial.setTimeout(5);
}
 
void loop()
{
  unsigned int nId = 0;
 
  if(Serial.available() > 0)
  {
    while(Serial.find("++ID:") == true) nId = Serial.parseInt();
   
    Serial.print("ID: ");
    Serial.println(nId);
  }
}

위 예제에서는 아두이노 시리얼모니터에서 ++ID:1234 를 입력하면 1234가 출력됩니다.

만일 해당 문자(열)이 없을 경우 ID는 0으로 출력되며, 시리얼 버퍼는 비워집니다.

시리얼 버퍼에 저장된 데이터 중 입력된 문자(열)을 검색하여 찾을 때 편리합니다.

 

 

샘플코드2. 시리얼 버퍼를 확인 후 시작 "AT+" 와 끝이 "="이 확인되면 그 뒤를 정수로 변환하여 출력

void setup()                                                                                                                                       
{
  Serial.begin(9600);
  while(!Serial);
  Serial.setTimeout(5);
}
 
void loop()
{
  unsigned nRssi = 0;
 
  if(Serial.available() > 0)
  {
    while(Serial.findUntil("AT+", "=") == true) nRssi = Serial.parseInt();
   
    Serial.print("RSSI: ");
    Serial.println(nRssi);
  }
}

위 예제에서는 아두이노 시리얼모니터에서 AT+RSSI=1234 를 입력하면 1234가 출력됩니다.

"AT+"와 "=" 사이의 데이터는 무시됩니다.

만일 해당 문자(열)이 없을 경우 RSSI는 0으로 출력되며, 시리얼 버퍼는 비워집니다.

 

 

10. serialEvent()

마지막으로 살펴 볼 함수는 독특한 serialEvent() 함수입니다

serialEvent() 함수는 호출해서 사용하는 다른 Serial 함수들과 달리

이벤트에 의해 호출되는 콜백함수입니다.

마치 시리얼 포트에 데이터가 입력됐을 때, 인터렙트처럼 구동됩니다.

 

함수.

void serial.Event()

 

매개변수.

없음.

 

 

샘플코드1. 시리얼 입력 시 시작 "AT+" 와 끝이 "="이 확인되면 그 뒤를 정수로 변환하여 출력

unsigned nRssi = 0;                                                                                                                           
bool SERIAL_EVENT;
 
void setup()
{
  Serial.begin(9600);
  while(!Serial);
  Serial.setTimeout(5);
}
 
void serialEvent()
{
  if(Serial.available() > 0)
  {
    while(Serial.findUntil("AT+", "=") == true) nRssi = Serial.parseInt();
    SERIAL_EVENT = true;
  } 
}
 
void loop()
{
  if (SERIAL_EVENT == true)
  {
    Serial.print("RSSI: ");
    Serial.println(nRssi);
    SERIAL_EVENT = false;
  }
}

위 예제에서는 동작 중, 시리얼 입력이 들어오면 serialEvent() 함수가 자동으로 호출됩니다.

SERIAL_EVENT 값이 true 일 경우 RSSI를 출력합니다.

 

출력 후 SERIAL_EVENT를 false로 만들어 다음 시리얼 입력을 기다립니다.

시작 조건과 종료 조건이 맞지 않는 경우 시리얼 버퍼를 비웁니다.

 

이상으로 아두이노의 Serial 내부 라이브러리에 대해 살펴 보았습니다.

 

Serial.flush() 함수의 경우 동작 이해가 부족하여 올리지 않았습니다.

 

만일 잘못되거나 부족한 정보는 추후 업데이트 하겠습니다.

 

감사합니다.