전편에서 텍스트를 이용하여 시리얼 모니터를 통해 아두이노에 LED를 켜고 LED의 상태를 블루투스 모듈을 통해 휴대폰 앱에 표시하고 블루투스 앱에서 아두이노의 LED를 켜고 시리얼 모니터에 LED 상태를 표시하는 양방향 통신 까지 진행했다. 이처럼 데이터를 양방향으로 주고 받을 수 있다는 것은 시리얼 통신을 통한 아두이노 원격제어를 완성했다고 말할 수 있다.
사실 눈으로 보이는 것은 텍스트의 표시일 뿐이지만 주고 받은 데이터를 통해 어떤 실행 명령이나 상태표시를 시각적으로 표현할 수 있도록 수신측 프로그램(시리얼 모니터 또는 안드로이드 블루투스 앱)에 코딩이 되어 있다면 들어온 데이터에 따라 작동 하고 표시하게 될 것이다.
앱 화면은 텍스트 전송, 디지털 버튼, 아날로그 입력으로 구성되어 있다. 텍스트 전송을 통한 제어는 확인해 보았으니 디지털 버튼을 이용한 제어를 살펴보겠다.
arduino bluetooth controller PWM - 아두이노 원격제어 안드로이드 앱 버전 3.5 다운로드
arduino bluetooth controller PWM 매뉴얼
(*** 상기앱의 블루투스 연결은 안드로이드10 이하 버전에서만 작동합니다 ***)
앱에서 디지털 버튼을 클릭하게 되면 "0xF0+1byte number+0xF1"의 데이터를 전송하도록 프로그램 되어 있다. 데이터는 블루투스 모듈을 통해 아두이노로 전송되게 된다.
버튼 1을 누르면 '1'을 전송하고 버튼2를 누르면 '2'를 전송하는 방식을 사용하면 직관적이고 편리할 것이라고 여길수 있다. 하지만 시리얼 통신이 완벽하다고 할 수 없어서 시리얼을 이용해 데이터 통신을 할 때 전송오류로 인한 의도치 않은 데이터 값이 수신될 가능성이 항상 존재하게 된다. 때문에 프로토콜을 규정하고 헤더와 종료문자의 검증을 통해 의도치 않는 값을 걸러내게 되는데 헤더와 종료문자의 검증이 된 데이터의 값은 오류없이 정확한 값이라고 판단하는 것이다. 이는 부정확한 데이터 값의 수신으로 인한 오동작을 방지할 뿐만아니라 시리얼 통신으로 들어온 데이터중 제어용 데이터와 일반 텍스트 데이터를 구분하게 하여 텍스트를 이용한 추가적인 코딩시 제어에 할당된 값과의 중복을 방지하게 한다.
"0xF0+1byte number+0xF1" 프로토콜(데이터 규칙)의 세부 의미는 다음과 같다.
0xF0(16진수) : 240(십진수), 11110000(이진수) - 디지털 핀에대한 데이터임을 표시하는 헤더값
1byte number : 핀값 - button1 = 10(off), 11(on), button2 = 20(off), 21(on),,,,,,,, button12 = 120(off), 121(on)
0xF1(16진수) : 241(십진수), 11110001(이진수) - 데이터의 종료를 표시
디지털 버튼을 클릭하면 위와 같이 총 3byte의 데이터를 전송하게 된다.
또한, 수신시에도 같은 프로토콜을 사용하는데 아두이노에서 위의 데이터를 전송한다면 블루투스 앱은 수신한 데이터의 값에 따라 버튼의 상태를 표시하게 된다.
예를 들어 아두이노에서 LED켜고 "0xF0+11+0xF1" 라는 데이터를 btSerial.write() 함수를 통해 블루투스로 전송하게 된다면 앱은 데이터 "0xF0+11+0xF1"를 수신하고 1번 버튼을 클릭한 상태로 표시할 것이고 "0xF0+10+0xF1"을 수신한다면 클릭하지 않은 상태로 표시할 것이다.
전편에서 다루었던 양방향 텍스트 제어 코드이다. 여기에서 블루투스 시리얼 수신부 코드를 위 프로토콜에 맞게 변경할 것이다.
#include <SoftwareSerial.h>
#define rxPin 3
#define txPin 2
SoftwareSerial btSerial(txPin, rxPin); // SoftwareSerial NAME(TX, RX);
#define ledPin 13
String s;
void setup() {
Serial.begin(9600); //시리얼모니터
btSerial.begin(9600); //블루투스 시리얼
pinMode(ledPin, OUTPUT);
}
void loop() {
if(btSerial.available()) {
char c = btSerial.read();
if(c != '\n') {
s += c;
} else {
if(s == "on"){
digitalWrite(ledPin, HIGH);
s = "";
}
else if(s == "off"){
digitalWrite(ledPin, LOW);
s = "";
}
else {
Serial.println(s);
s = "";
}
}
}
if(Serial.available()) {
char c = Serial.read();
if(c != '\n') {
s += c;
} else {
if(s == "on"){
digitalWrite(ledPin, HIGH);
s = "";
}
else if(s == "off"){
digitalWrite(ledPin, LOW);
s = "";
}
else {
btSerial.println(s);
s = "";
}
}
}
}
아래와 같이 코드를 추가해 주었다.
bool get_pin_val = false; // 들어온 데이터에 디지털 핀 헤더값이 있을경우 핀값을 확인하는 코드 진입용 플래그
uint8_t pin_val_count = 0; // 들어온 3byte 데이터를 1byte씩 구분하여 받기 위한 카운터
uint8_t pin_a[3] = {0, }; // 3byte 데이터를 저장하기 위한 배열
uint8_t pin_val; // 핀 상태값을 저장하기 위한 변수
if (get_pin_val != true && btSerial.peek() == 0xF0) get_pin_val = true;
// 디핀값을 확인하는 코드에 진입하지 않은 상태에서 시리얼 버퍼에 디지털핀 헤더값이 있다면 핀값을 확인하는 코드로 진입해라.
if (get_pin_val == true) { 실행코드1 } else { 실행코드2 } // 코드 진입 플래그가 참이면 실행코드1, 아니면 실행코드2
실행코드1(메인루프가 1회전 할때마다 카운트를 1씩 증가시켜 3byte를 순차적으로 받는 코드)
if (pin_val_count == 0) {pin_a[0] = btSerial.read(); pin_val_count++; }
// 카운터값이 0이면(0일 때에만) 핀값 배열 인덱스 0에 1byte값 저장하고 카운트 1증가
else if (pin_val_count == 1) { pin_a[1] = btSerial.read(); pin_val_count++; }
// 그렇지 않고 카운터값이 1이면(1일 때에만) 핀값 배열 인덱스 1에 1byte값 저장하고 카운트 1증가
else if (pin_val_count == 2) { // 그렇지 않고 카운터값이 2이면(2일 때에만)
pin_a[2] = btSerial.read(); // 핀값 배열 인덱스 2에 1byte값 저장
if (pin_a[0] == 0xF0 && pin_a[2] == 0xF1) { pin_val = pin_a[1]; }
// 핀헤더와 데이터 종료값이 프로토콜과 맞는지 검증하고 맞으면 핀값 변수 pin_val 에 핀값배열 인덱스 1번값을 저장하라
pin_val_count = 0; // 3byte를 모두 받았으므로 다음 데이터를 받을 수 있도록 카운트 초기화
get_pin_val = false; // 핀값 확인 작업이 완료 되었으므로 다음 데이터를 받을 수 있도록 코드 진입 플래그 초기화
}
실행코드2 : 들어온 데이타를 그대로 시리얼 모니터에 출력
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 = ""; }
// 만약 핀값이 11이면 LED를 켜고 시리얼 모니터에 "LED ON"을 출력하고 핀값 초기화
else if(pin_val == 10) { digitalWrite(ledPin, LOW); Serial.println("LED OFF"); pin_val = ""; }
// 그렇지 않고 핀값이 10이면 LED를 끄고 시리얼 모니터에 "LED OFF"을 출력하고 핀값 초기화
시리얼 모니터에서 입력했을경우 LED를 켜는 코드에 블루투스 시리얼로 "LED ON/OFF"를 표시하도록 코드 추가
btSerial.println("LED ON");
btSerial.println("LED OFF");
#include <SoftwareSerial.h>
#define BT_rxPin 3 // Bluetooth RX -> 3(arduino TX)
#define BT_txPin 2 // Bluetooth TX -> 2(arduino RX)
SoftwareSerial btSerial(BT_txPin, BT_rxPin); // (arduino RX, arduino TX)
#define ledPin 13
String s ="";
bool get_pin_val = false;
uint8_t pin_val_count = 0;
uint8_t pin_a[3] = {0, };
uint8_t pin_val;
void setup() {
Serial.begin(9600); // hardware Serial
btSerial.begin(9600); // software bluetooth Serial
pinMode(ledPin, OUTPUT);
}
void loop() {
if (btSerial.available()) {
if (get_pin_val != true && btSerial.peek() == 0xF0) get_pin_val = true;
if (get_pin_val == true) {
if (pin_val_count == 0) {
pin_a[0] = btSerial.read();
pin_val_count++;
}
else if (pin_val_count == 1) {
pin_a[1] = btSerial.read();
pin_val_count++;
}
else if (pin_val_count == 2) {
pin_a[2] = btSerial.read();
if (pin_a[0] == 0xF0 && pin_a[2] == 0xF1) {
pin_val = pin_a[1];
}
pin_val_count = 0;
get_pin_val = false;
}
}
else {
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"프로토콜을 보내는 아래 코드를 추가 해주면 된다.
btSerial.write(0xF0);
btSerial.write(11);
btSerial.write(0xF1);
if(Serial.available()) {
char c = Serial.read();
if(c != '\n') {
s += c;
} else {
if(s == "on"){
digitalWrite(ledPin, HIGH);
btSerial.write(0xF0);
btSerial.write(11);
btSerial.write(0xF1);
btSerial.print("LED ON");
s = "";
}
else if(s == "off"){
digitalWrite(ledPin, LOW);
btSerial.write(0xF0);
btSerial.write(10);
btSerial.write(0xF1);
btSerial.print("LED OFF");
s = "";
}
else {
btSerial.println(s);
s = "";
}
}
}
}
위의 코드를 업로드하고 시리얼 모니터상에서 on을 입력하면 아두이노 LED가 켜지고 블루투스 앱에 "LED ON"메지지와 함께 Button 1의 상태가 클릭된 것으로 표시 된것을 확인 할 수 있다. 시리얼 모니터로 제어하는것 대신 아두이노에 버튼을 달아 LED를 제어 한다면 그 제어 코드에 블루투스 앱으로 제어 상태 데이터를 보내는 코드를 추가하여 블루투스 앱에서 LED의 상태를 표시하도록 할 수 있는것이다.
블루투스 앱에서 버튼을 클릭했을때 아두이노의 LED를 켜고 시리얼 모니터로 "LED ON"을 표시하도록 코딩을 하였었다. 추가로 LED를 켜는 코드가 작동을 하게되면 다시 블루투스 앱으로 상태를 표시하도록 해보자
앞선 예제와 비슷하게 Serial.println("LED ON"); 코드에 "0xF0+11+0xF1"프로토콜을 보내는 아래 코드를 추가 해주면 된다.
btSerial.write(0xF0);
btSerial.write(11);
btSerial.write(0xF1);
if (btSerial.available()) {
if (get_pin_val != true && btSerial.peek() == 0xF0) get_pin_val = true;
if (get_pin_val == true) {
if (pin_val_count == 0) {
pin_a[0] = btSerial.read();
pin_val_count++;
}
else if (pin_val_count == 1) {
pin_a[1] = btSerial.read();
pin_val_count++;
}
else if (pin_val_count == 2) {
pin_a[2] = btSerial.read();
if (pin_a[0] == 0xF0 && pin_a[2] == 0xF1) {
pin_val = pin_a[1];
}
pin_val_count = 0;
get_pin_val = false;
}
}
else {
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의 고장등을 살펴보면 되게되어 문제 해결에 빠르게 접근 할 수 있다.
버튼의 상태 변화는 아래와 같다.
클릭전 클릭 결과 수신
수신된 데이터에 의해 버튼의 상태가 변경되는 경우 버튼 라벨의 색깔이 갈색으로 바뀌도록하여 수신을 확인 할 수 있도록 하였다.
이상으로 디지털 핀의 제어에 대해서는 마치고 다음편에는 아날로그 데이터 전송에 대해 살펴 보겠다.
최신 업데이트 유료앱(모든 안드로이드 버전 블루투스 연결 지원)
https://postpop.tistory.com/175
관련 글
[arduino] - 아두이노 - 안드로이드를 이용한 무선 원격제어 그리고 시리얼 통신 - 1편
[arduino] - 아두이노 - 안드로이드를 이용한 무선 원격제어 그리고 시리얼 통신 - 2편
[arduino] - 아두이노 - 안드로이드를 이용한 무선 원격제어 그리고 시리얼 통신 - 3편
[arduino] - 아두이노 - 안드로이드를 이용한 무선 원격제어 그리고 시리얼 통신 - 4편
[arduino] - 아두이노 - 안드로이드를 이용한 무선 원격제어 그리고 시리얼 통신 - 5편
[arduino] - 블루투스 4.0 BLE 이용 아두이노 및 ESP32 원격제어
[arduino] - 아두이노 - 시리얼통신 주요함수와 예제, String class
[arduino] - 아두이노 - ESP01 wifi 모듈 무선 원격제어 그리고 시리얼 통신 - 6편
[arduino] - ESP8266 - NodeMcu 1.0 와이파이 이용 원격제어(soft AP, wifi)
[arduino] - ESP32 - Dev Module 와이파이 이용 원격제어(soft AP, wifi)
arduino bluetooth controller PWM - 아두이노 원격제어 안드로이드 앱 버전 3.5 다운로드
arduino bluetooth controller PWM 매뉴얼
'Arduino' 카테고리의 다른 글
아두이노 IDE에 ESP8266 사용 환경 설치하기 (0) | 2019.05.12 |
---|---|
블루투스 4.0 BLE, 스마트폰 연결과 페어링(pairing), AT-09 (0) | 2019.05.11 |
아두이노 - 안드로이드를 이용한 무선 원격제어 그리고 시리얼 통신 - 5편 (2) | 2019.05.07 |
아두이노 - 안드로이드를 이용한 무선 원격제어 그리고 시리얼 통신 - 4편 (0) | 2019.05.05 |
아두이노 - 안드로이드를 이용한 무선 원격제어 그리고 시리얼 통신 - 2편 (0) | 2019.05.02 |
아두이노 - 안드로이드를 이용한 무선 원격제어 그리고 시리얼 통신 - 1편 (45) | 2019.04.29 |
아두이노 - 블루투스 4.0 BLE 모듈에 대한 생각, 제품명 확인 프로그램, AT-09 (0) | 2019.04.14 |
아두이노 - 블루투스모듈, 블루투스 클래식과 블루투스 4.0 BLE의 차이 (0) | 2019.04.14 |