전편에서 텍스트를 이용하여 시리얼 모니터를 통해 아두이노에 LED를 켜고 LED의 상태를 블루투스 모듈을 통해 휴대폰 앱에 표시하고 블루투스 앱에서 아두이노의 LED를 켜고 시리얼 모니터에 LED 상태를 표시하는 양방향 통신 까지 진행했다. 이처럼 데이터를 양방향으로 주고 받을 수 있다는 것은 시리얼 통신을 통한 아두이노 원격제어를 완성했다고 말할 수 있다.
사실 눈으로 보이는 것은 텍스트의 표시일 뿐이지만 주고 받은 데이터를 통해 어떤 실행 명령이나 상태표시를 시각적으로 표현할 수 있도록 수신측 프로그램(시리얼 모니터 또는 안드로이드 블루투스 앱)에 코딩이 되어 있다면 들어온 데이터에 따라 작동 하고 표시하게 될 것이다.
앱 화면은 텍스트 전송, 디지털 버튼, 아날로그 입력으로 구성되어 있다. 텍스트 전송을 통한 제어는 확인해 보았으니 디지털 버튼을 이용한 제어를 살펴보겠다.
앱에서 디지털 버튼을 클릭하게 되면 "0xF0+1byte number+0xF1"의 데이터를 전송하도록 프로그램 되어 있다. 데이터는 블루투스 모듈을 통해 아두이노로 전송되게 된다.
버튼 1을 누르면 '1'을 전송하고 버튼2를 누르면 '2'를 전송하는 방식을 사용하면 직관적이고 편리할 것이라고 여길수 있다. 하지만 시리얼 통신이 완벽하다고 할 수 없어서 시리얼을 이용해 데이터 통신을 할 때 전송오류로 인한 의도치 않은 데이터 값이 수신될 가능성이 항상 존재하게 된다. 때문에 프로토콜을 규정하고 헤더와 종료문자의 검증을 통해 의도치 않는 값을 걸러내게 되는데 헤더와 종료문자의 검증이 된 데이터의 값은 오류없이 정확한 값이라고 판단하는 것이다. 이는 부정확한 데이터 값의 수신으로 인한 오동작을 방지할 뿐만아니라 시리얼 통신으로 들어온 데이터중 제어용 데이터와 일반 텍스트 데이터를 구분하게 하여 텍스트를 이용한 추가적인 코딩시 제어에 할당된 값과의 중복을 방지하게 한다.
"0xF0+1byte number+0xF1" 프로토콜(데이터 규칙)의 세부 의미는 다음과 같다.
0xF0(16진수) : 240(십진수), 11110000(이진수) - 디지털 핀에대한 데이터임을 표시하는 헤더값
0xF1(16진수) : 241(십진수), 11110001(이진수) - 데이터의 종료를 표시
디지털 버튼을 클릭하면 위와 같이 총 3byte의 데이터를 전송하게 된다.
또한, 수신시에도 같은 프로토콜을 사용하는데 아두이노에서 위의 데이터를 전송한다면 블루투스 앱은 수신한 데이터의 값에 따라 버튼의 상태를 표시하게 된다.
예를 들어 아두이노에서 LED켜고 "0xF0+11+0xF1" 라는 데이터를 btSerial.write() 함수를 통해 블루투스로 전송하게 된다면 앱은 데이터 "0xF0+11+0xF1"를 수신하고 1번 버튼을 클릭한 상태로 표시할 것이고 "0xF0+10+0xF1"을 수신한다면 클릭하지 않은 상태로 표시할 것이다.
전편에서 다루었던 양방향 텍스트 제어 코드이다. 여기에서 블루투스 시리얼 수신부 코드를 위 프로토콜에 맞게 변경할 것이다.
char text = btSerial.read(); // text value for some purpose.
Serial.write(text); // print text to Serial Monitor
}
if(pin_val == 11){
digitalWrite(ledPin, HIGH);
Serial.println("LED ON");
pin_val = "";
}
else if(pin_val == 10){
digitalWrite(ledPin, LOW);
Serial.println("LED OFF");
pin_val = "";
}
}
if(Serial.available()) {
char c = Serial.read();
if(c != '\n') {
s += c;
} else {
if(s == "on"){
digitalWrite(ledPin, HIGH);
btSerial.println("LED ON");
s = "";
}
else if(s == "off"){
digitalWrite(ledPin, LOW);
btSerial.println("LED OFF");
s = "";
}
else {
btSerial.println(s);
s = "";
}
}
}
}
상기 코드를 업로드하고 앱에서 Button1을 클릭하면 아두이노상 기본LED가 켜지고 시리얼 모니터에 "LED ON"이 표시되는 것을 확인 할 수 있다. 또한 시리얼 모니터에서 on을 입력하면 아두이노의 LED가 켜지고 블루투스 앱에 "LED ON"이 표시되는 것도 확인 할 수 있을것이다.
시리얼 모니터에서 on/off를 입력하여(전송옵션: 새줄) LED를 제어 했을때 블루투스 앱에 "LED ON/OFF" 표시 대신 버튼의 상태를 변경하고 싶다면 btSerial.println("LED ON"); 코드에 "0xF0+11+0xF1"프로토콜을 보내는 아래 코드를 추가 해주면 된다.
위의 코드를 업로드하고 시리얼 모니터상에서 on을 입력하면 아두이노 LED가 켜지고 블루투스 앱에 "LED ON"메지지와 함께 Button 1의 상태가 클릭된 것으로 표시 된것을 확인 할 수 있다. 시리얼 모니터로 제어하는것 대신 아두이노에 버튼을 달아 LED를 제어 한다면 그 제어 코드에 블루투스 앱으로 제어 상태 데이터를 보내는 코드를 추가하여 블루투스 앱에서 LED의 상태를 표시하도록 할 수 있는것이다.
블루투스 앱에서 버튼을 클릭했을때 아두이노의 LED를 켜고 시리얼 모니터로 "LED ON"을 표시하도록 코딩을 하였었다. 추가로 LED를 켜는 코드가 작동을 하게되면 다시 블루투스 앱으로 상태를 표시하도록 해보자
앞선 예제와 비슷하게 Serial.println("LED ON"); 코드에 "0xF0+11+0xF1"프로토콜을 보내는 아래 코드를 추가 해주면 된다.
char text = btSerial.read(); // text value for some purpose.
Serial.write(text); // print text to Serial Monitor
}
if(pin_val == 11){
digitalWrite(ledPin, HIGH);
btSerial.write(0xF0);
btSerial.write(11);
btSerial.write(0xF1);
Serial.println("LED ON");
pin_val = "";
}
else if(pin_val == 10){
digitalWrite(ledPin, LOW);
btSerial.write(0xF0);
btSerial.write(10);
btSerial.write(0xF1);
Serial.println("LED OFF");
pin_val = "";
}
}
이제 블루투스 앱에서 버튼을 클릭했을때 버튼의 상태가 클릭 상태로 변경되고 LED 제어용 데이터를 전송하고 아두이노에서 수신한 데이터에 따라 LED를 켜고 다시 블루투스 앱으로 LED를 켰다는 데이터를 전송해주게 된다.(echo라고도 한다.) 이렇게 해주는 이유로는 오동작의 가능성을 줄이고 문제 발생시 빠르게 해결하기 위해서 이다. 사용자가 버튼을 클릭해서 데이터를 보냈는데 정작 아두이노에서는 오류난 데이터를 수신하여 원하는 코드를 실행시키지 못하게 되었을때 사용자는 확인할 방법이 없는 것이다. 위와 같이 코드를 짜놓고 최종적으로 데이터가 수신되었을 경우 정상 적으로 실행코드가 실행되었으리라 판단 해 볼수 있다. 또한 LED를 켜는 코드를 실행 했을 경우 데이터의 송 수신이 버튼의 상태 변화로 확인된 상태에서 LED가 켜지지 않는 문제가 발생 했다면 휴대폰에서 코드를 실행시키는 부분까지는 정상 작동한 것으로 간주하고 LED 실행코드 digitalWrite(ledPin, HIGH);를 확인하거나 LED와 아두이노의 연결 또는 LED의 고장등을 살펴보면 되게되어 문제 해결에 빠르게 접근 할 수 있다.
버튼의 상태 변화는 아래와 같다.
클릭전 클릭 결과 수신
수신된 데이터에 의해 버튼의 상태가 변경되는 경우 버튼 라벨의 색깔이 갈색으로 바뀌도록하여 수신을 확인 할 수 있도록 하였다.
이상으로 디지털 핀의 제어에 대해서는 마치고 다음편에는 아날로그 데이터 전송에 대해 살펴 보겠다.