아두이노로 데이터를 보내면 'Serial.read()'로 데이터를 읽습니다.

그런데 문자열을 'Serial.read()'로 읽으면 한번에 한글자만 읽어집니다.

 

1. 문자열 보내기 테스트용

일단 LED 3개를 달아봅시다,

아웃풋 핀은 3,5,6를 사용합니다.

(이왕이면 빨간색, 녹색, 파란색으로 세팅합시다.)

 

1-1. 보드 구성

그냥 LED 3개가 달려있습니다.

 

 

1-2. 펌웨어 작성
LED를 데이터를 통해 끄고 켜고를 할것이기 때문에 'switch'문을 사용해야 합니다.

 

#define LED_R 6
#define LED_G 5
#define LED_B 3

String sCommand = "";

void setup()  
{                
   Serial.begin(9600);
   pinMode(LED_R, OUTPUT);
   pinMode(LED_G, OUTPUT);
   pinMode(LED_B, OUTPUT);
}
 
void loop()                    
{
   if(Serial.available())
   {
     char cTemp = Serial.read();
     //char cRGB = sData;
     int nON = 255;
     
     switch(cTemp)
     {
       case 'R':
         digitalWrite(LED_R,HIGH);
         break;
       case 'r':
         digitalWrite(LED_R,LOW);
         break;
         
       case 'G':
         digitalWrite(LED_G,HIGH);
         break;
       case 'g':
         digitalWrite(LED_G,LOW);
         break;
         
       case 'B':
         digitalWrite(LED_B,HIGH);
         break;
       case 'b':
         digitalWrite(LED_B,LOW);
         break;
     }
     
     Serial.println(cTemp);
   }
   
   delay(100);
}

 

 

코드를 보시면 아시겠지만

R : 빨간색 켜기

r : 빨간색 끄기

G : 녹색 켜기

g : 녹색 끄기

B : 파란색 켜기

b : 파란색 끄기

입니다.

 

2. 문자열 입력해 보기

이제 아두이노 프로그램에서 '시리얼 모니터'를 열어 RGB를 입력해 봅시다.

 

 

 

RGB라고 입력 했는데 R,G,B로 입력되고 있습니다.

불도 한번에 다들어왔습니다.

 

이것은 'Serial.read()'가 버퍼를 읽을때는 무조건 한바이트씩 읽기 때문입니다.

그리고 읽은 바이트는 제거 됩니다.

(참고 : arduino Reference - Serial.read())

읽은 바이트를 제거하지 않으려면 'Serial.peek()'를 사용해야 합니다.

(참고 : arduino Reference - Serial.Peek())

 

그러니 'loop()'가 돌면서 나머지 버퍼를 읽기 때문에 3개가 불이 다 들어 오는 것입니다.

 

3. 해결 방법

레퍼런스를 보니 'Serial.read()'를 반복문으로 돌리지 않으면 답이 없을듯 해서 만들려다가 그래도 뭔가 있지 않을까 해서 검색 했는데....

반복문 돌려야 한답니다 ㅎㅎㅎ;;

(참고 : stackoverflow - Convert serial.read() into a useable string using Arduino? )

 

원리는 간단합니다.

'Serial.read()'가 한바이트씩 읽는 다고 하니 반복문으로 버퍼의 모든 값을 다읽을때 까지 돌리고 그값을 'String'에 저장하면 아두이노 보드로 문자열을 보낼 수 있습니다.

 

3-1. 펌웨어 수정

일단 문자열로 컨트롤 할만한 데이터로 바꿔야 하니까 RGB를 웹에서 처럼 0~255까지 값을 이용하여 밝기를 조절해 봅시다.

입력데이터는 '255255255' 이런 형태로 RGB값을 3자리씩 총 9자리를 받아 밝기를 조절하는 것입니다.

 

#define LED_R 6
#define LED_G 5
#define LED_B 3

//한바이트 데이터를 임시 저장
char cTemp;
//완성된 명령어
String sCommand = "";

String sTemp = "";

void setup()  
{                
   pinMode(LED_R, OUTPUT);
   pinMode(LED_G, OUTPUT);
   pinMode(LED_B, OUTPUT);
}
 
void loop()                    
{
   sCommand = "";
  
   while(Serial.available())
   {
     cTemp = Serial.read();
     sCommand.concat(cTemp);
   }
   
   //완성된 데이터가 있는지 확인 한다.
   if(sCommand != "" )
   {
     //완성된 데이터가 있다.
     
     char cTempData[4];
     
     sCommand.substring(0, 3).toCharArray(cTempData, 4);
     int nR = atoi(cTempData);
     
     sCommand.substring(3, 6).toCharArray(cTempData, 4);
     int nG = atoi(cTempData);
     
     sCommand.substring(6, 9).toCharArray(cTempData, 4);
     int nB = atoi(cTempData);
     
     digitalWrite(LED_R, nR);
     digitalWrite(LED_G, nG);
     digitalWrite(LED_B, nB);
   
     Serial.println(sCommand + ":" + nR + "," + nG + "," + nB ); 
   }
   
   delay(100);
}

 

 

이제 시리얼 모니터를 열어 '120250100'같이 값을 입력해 봅시다.

 

 

 

아래 코드를 통해 버퍼에 있는 값을 한번에 읽어 올 수 있습니다.

   while(Serial.available())
   {
     cTemp = Serial.read();
     sCommand.concat(cTemp);
   }

 

 

이렇게 읽어온 값은 원하는 대로 사용하시면 됩니다.

샘플 코드에서는 3자리씩 잘라서 숫자로 바꾸고 있습니다.

(참고 : [Arduino] String를 int로 변환 하기)

 

 

마무리

생각보다 버퍼값은 믿을만 하지 않습니다.

이것은 반대로 컴퓨터로 데이터를 보낼때도 같죠.

 

값이 정확하게 한줄이 다 들어올지는 모른다는 뜻입니다.

명렁어를 만들때는 보통 글자수를 설정하여 그만큼 읽어 들이거나 시작문자와 끝문자를 만들어 판단합니다.

그런데 이렇게 한줄이 정확한지 알기 힘들때는 시작문자와 끝문자를 데이터의 처음과 끝에 붙여 보내는 것이 좋습니다.

  1. 2014.04.08 23:40  Address  Edit/Delete  Reply

    비밀댓글입니다

    • Favicon of http://blog.danggun.net BlogIcon 당근천국 2014.04.09 01:00 신고  Address  Edit/Delete

      딜래이를 주지 않으면 버퍼에 글자가 차기전에 읽으면서 다른 값이 나올 수 있습니다.
      이런 현상을 방지하기 위해서는 버퍼에 데이터가 충분히 쌓이 틈을 주는 것이 가장 좋습니다.
      그럴 여유가 없다면 시작문자와 끝문자를 넣어 데이터가 모두 쌓이면 처리하는 방법도 있습니다.

  2. 2014.04.09 11:35  Address  Edit/Delete  Reply

    비밀댓글입니다

    • Favicon of http://blog.danggun.net BlogIcon 당근천국 2014.04.09 13:39 신고  Address  Edit/Delete

      시작문자와 끝문자를 임의로 지정하여 해당문자가 들어오면 그다음부터 들어오는 데이터는 전역변수에 저장해 두거나 'Serial.peek()'로 데이터를 읽어 전체 데이터를 만듭니다.
      임의의 끝문자가 들어올때까지 모든 데이터를 저장하고 끝문자가 들어오면 쌓여있는 데이터를 문자열로 처리하면 됩니다.

  3. Favicon of http://nazuna.kr BlogIcon 나즈나 2018.06.20 14:53 신고  Address  Edit/Delete  Reply

    Serial.readBytesUntil()

    이제 이 함수가 생겼더군요. 저도 반복문으로 처리하다가 알게되서 유용하게 사용중입니다.

댓글 작성

이름
패스워드
홈페이지
비밀글

티스토리 툴바