반응형

전편에서 가변저항과 블루투스 앱을 이용한 LED의 밝기를 제어함에 있어 가변저항의 제어권 획득에 관해 다루었었다. 제어권 획득에 대한 표시 및 획득을 위한 진행 사항을 시리얼 모니터에 표시하여 인지하도록 하였는데 시리얼 모니터를 보면서 가변저항을 조절하는건 한마디로 불편하다. 

 

가변저항의 제어권을 표시할 수 있는 LED를 추가하여 가변저항을 이용해 가변저항의 제어권의 획득 여부를 시각적으로 표시해보자.

 

아래와 같이 LED를 아두이노 4번핀과 연결해 주었고 아두이노 기본 LED 13번도 같이 사용하겠다. 

전편 예제에서 가변저항의 제어권 획득 과정에서 현재의 가변저항 값의 변동을 시리얼 모니터에 표시하여 목표값인 블루투스 앱에서 실행했던 PWM 값에 근접하는지 여부를 확인하여 가변저항의 조절 방향을 정할 수 있었는데, 이번에는 가변저항의 값이 블루투스 앱의 PWM 값에 근접할수록 아두이노 기본 LED 13번이 빠르게 점멸 하도록 하여 올바른 제어권 획득 진행 방향에 대해 인지 할 수 있도록 해보자. 즉, 가변저항의 값과 저장된 블루투스 앱 PWM값의 차가 작아질수록 LED 13번이 빠르게 점멸하고 그 차가 커질수록 느리게 점멸하게 된다. LED 13번이 점점 더 빠르게 점멸하다 제어권을 획득하게 되면 LED 13번은 꺼지고 LED 4번이 켜지게 되어 제어권의 획득을 인지할 수 있는 것이다. 

 

 

 

전편 예제에 아래 코드를 추가 및 변경해 주었다. 

 

#define ledPin 13                        // 가변저항 제어권 획득 진행 표시 LED 

#define get_ledPin 4                    // 가변저항 제어권 표시 LED

#define pwm_ledPin 11                // 아두이노 pwm핀(~ 핀)

 

unsigned long flicker_now = 0;     // LED 점멸에 사용할 millis() 비교용 변수선언

bool led_state = false;                 // LED 점멸 스위치 플래그

 

pinMode(ledPin, OUTPUT);           // 아두이노 기본 LED - 제어권 획득 진행 표시, 핀모드 설정

pinMode(get_ledPin, OUTPUT);     // 가변저항 제어권 획득 표시, 핀모드 설정

pinMode(pwm_ledPin, OUTPUT);   // PWM 제어 LED, 핀모드 설정

digitalWrite(get_ledPin, true);       // 기동시 제어권이 가변저항에 있으므로 led on

 

if (abs(h_pwm - bt_pwm) < 10) { 실행코드1 } else { 실행코드2 }

// 블루투스 값과 가변저항 값의 차가 10보다 작으면 - 블루투스 값에 근접했으므로 가변저항이 제어권 획득

 

 

실행코드1

bt_pwm_use = false;                            // 블루투스 제어권 없음

digitalWrite(get_ledPin, true);                // 가변저항 제어권 표시: on

digitalWrite(ledPin, false);                     // 가변저항 제어권 힉득 진행 표시: off

btSerial.print("potentiometer control");   // 블루투스 앱에 제어권 표시

Serial.print("potentiometer control");      // 시리얼 모니터에 제어권 표시

 

실행코드2: 제어권 획득하지 않은 상태라면 LED 13번 점멸코드 실행 

if (millis() - flicker_now > abs(h_pwm - bt_pwm)+10) { // 밀리초 값이 블루투스 값과 가변저항 값의 차 + 10 보다 크면

  flicker_now = millis();                    // 현재의 밀리초 값을 flicker_now에 저장하여 다음 밀리초 계산에 사용하도록 준비

  led_state = !led_state;                   // 현재의 led_state의 true 또는 false 값을 반대로 바꿔주고 led_state에 그 값을 저장

  digitalWrite(ledPin, led_state);        // 변경된 led_state 상태에 따라 on 또는 off

}

 

if (bt_pwm_use == false) {                 // 제어권이 가변저항에 있는 상태에서 

  old_h_pwm = h_pwm;

  hand_over = false;

  btSerial.print(" ");

  bt_pwm_use = true;                         // 제어권이 블루투스 앱으로 넘어가면

  digitalWrite(get_ledPin, false);           // 가변저항 제어권 표시 led off

}

 

slide_potentiometer_led_control_bt_state_echo_authority_display.zip
0.00MB

 

상기 코드를 업로드 하고 가변저항을 조절하여 최대 밝기로 조정한 뒤 블루투스 앱의 PWM 제어값 버튼을 클릭하면 0이 전송되면서 LED가 꺼지게 된다. 이때 가변저항을 조절하여 0쪽으로 이동할 수록 LED 13번이 점점 빠르게 점멸하는 것을 볼 수 있다. 반대로 255쪽으로 이동할 수록 LED의 점멸 속도가 느려지는 것을 볼 수있고 다시 가변저항을 0근처로 이동시키면 빠르게 점멸하다 제어권을 획득하게 되면 점멸하던 LED는 꺼지고 LED 4번이 껴지는 것을 볼 수 있다.

 

 

가변저항을 사용하여 LED를 제어할 때 시리얼 모니터 확인 없이도 편하게 제어권의 획득을 진행 할 수 있게 되었다.

 

* 가변저항을 0으로 조정했는데도 PWM 제어용 LED가(11번) 깜박거린다면 블루투스 잡음을 무시하는 코드의 설정값을 높여주면된다.  

  if (val <= 15) val = 0; // 15를 30 또는 상황에 맞게 변경하여 조절 

 

상기 예제 코드와 더불어 이제까지 다루었던 예제들의 코드를 합치고 길어진 메인루프 코드를 정리 해보자.

 

반복 실행될 가능성이 있는 블루투스 앱의 상태변화 표시 코드들을 배열을 이용하여 함수로 만들어, 반복 사용시 함수만 불러오면 상태변화 코드를 보낼 수 있도록 하여 전체 코드가 단순해 지도록 하였다.

 

- 디지털 버튼 상태표시 코드 

btSerial.write(0xF0);

btSerial.write(11);

 

btSerial.write(0xF1);

 

uint8_t pin_echo[] = {0xF0, 0, 0xF1}; // 디지털 버튼 상태표시 프로토콜 배열 선언

 

void send_pin_echo(uint8_t pin_val) { // 함수내에서 사용할 uint8_t pin_val 변수 선언

  pin_echo[1] = pin_val;        // 함수 인수 pin_val 값을 pin_echo 배열 인덱스 1에 저장

  for (int i = 0; i < 3; i++){   // for 루프 이용 3byte 순차 전송

    btSerial.write(pin_echo[i]);

  }

 

}

 

예) send_pin_echo(pin_val); // pin_val이 11이면 1번 버튼 상태: on

 

- PWM값에 따른 슬라이드 상태표시 코드

btSerial.write(0xF3); // 슬라이더 상태표시 용도 헤더값 

btSerial.write(1);    // 슬라이더 번호

btSerial.write(pwm1);

uint8_t temp = 0;

btSerial.write(temp);

btSerial.write(0xF1); 

 

uint8_t pwm_slide[] = {0xF4, 0, 0, 0, 0xF1};

 

void send_pwm_slide(uint8_t slide, uint16_t pwm_val) {  // 함수내 사용할 uint8_t slide, uint16_t pwm_val 변수선언

  pwm_slide[1] = slide;                 // 슬라이드 고유 번호 저장 

  if (pwm1 < 256) {                          // 0 ~ 255 범위내의 값 사용시

    pwm_slide[2] = pwm_val;            // 2byte number 중 LSB 값 저장

    pwm_slide[3] = 0;                       // 2byte number 중 MSB 값 저장

  }

  else {                                            // 255를 넘는 범위의 값 사용시

    pwm_slide[2] = pwm_val;            // 2byte number 중 LSB 값 저장

    pwm_slide[3] = pwm_val >> 8;    // 2byte number 중 MSB 값 저장

  }

  for (int i = 0; i < 5; i++){               // for 루프 이용 5byte 순차 전송

    btSerial.write(pwm_slide[i]);

  }

}

 

예) send_pwm_slide(1, pwm1); // 1번 슬라이드, pwm값 

 

- PWM값 echo 코드 

btSerial.write(0xF3); // 슬라이더 상태표시 용도 헤더값 

btSerial.write(1);    // 슬라이더 번호

btSerial.write(pwm1);

uint8_t temp = 0;

btSerial.write(temp);

btSerial.write(0xF1); 

 

uint8_t pwm_echo[] = {0xF3, 0, 0, 0, 0xF1};

 

void send_pwm_echo(uint8_t slide, uint16_t pwm_val) {

  pwm_echo[1] = slide;

  if (pwm1 < 256) {

    pwm_echo[2] = pwm_val;

    pwm_echo[3] = 0;

  }

  else {

    pwm_echo[2] = pwm_val;

    pwm_echo[3] = pwm_val >> 8;

  }

  for (int i = 0; i < 5; i++){

    btSerial.write(pwm_echo[i]);

  }

 

}

 

예) send_pwm_echo(1, pwm1); // 1번 슬라이드, pwm값 

 

디지털 버튼 제어시 if문을 사용하였었다. 한두개의 버튼을 제어할때는 별 문제없지만 여러개의 버튼을 사용할 때는 코드가 복잡해 질 수 있으므로 switch()문을 사용하여 pin_control()이라는 이름의 함수로 만들어주고 위에서 만들어 놓은 디지털 버튼 상태 표시 함수를 사용하여 기존 코드를 대체해 주었다.

    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 = "";

    }

 

void pin_control() {

  if (pin_val != 0) {

    switch (pin_val) {

      case 11: digitalWrite(ledPin, true); // button 1 : on

                send_pin_echo(pin_val);

                  break;

      case 10: digitalWrite(ledPin, false); // button 1 : off

                send_pin_echo(pin_val);

                  break;

      case 21: Serial.println("button 2 : on");

               send_pin_echo(pin_val); // 버튼 상태 echo 사용시

                  break;

      case 20: Serial.println("button 2 : off");

               send_pin_echo(pin_val); // 버튼 상태 echo 사용시

                  break;

      case 31: Serial.println("button 3 : on");

               send_pwm_slide(1, 30); // 버튼을 클릭하면 슬라이드 위치를 변경한다.

                  break;

      case 30: Serial.println("button 3 : off");

               send_pwm_slide(1, 150); // 버튼을 클릭하면 슬라이드 위치를 변경한다.

                  break;

      ...........

 

      case 121: Serial.println("button 12 : on");

                  break;

      case 120: Serial.println("button 12 : off");

                  break;  

    }

  pin_val = 0;

  }

}

 

 

 

위에서 디지털 핀 제어용 코드를 함수로 따로 만들준 것과 같이 PWM제어 관련 코드도 아래와 같이 함수로 만들어 쉽게 찾을수 있도록 해주었다. 

 

 

 

 

PWM 헤더값과 종료문자 검증이 완료 시점의 PWM제어 관련 코드를 pwm_control() 이라는 이름의 함수로 만들어준다. 

 

if (pwm_a[0] == 0xF3 && pwm_a[4] == 0xF1) { // PWM 헤더값과 종료문자 검증이 완료되면

  PWM제어 관련 코드 실행

}

 

void pwm_control() {

  uint16_t temp = pwm_a[3];

  temp = temp << 8 | pwm_a[2];

  if (pwm_a[1] == 1) {  // 슬라이드 1번

    pwm1 = temp;

    analogWrite(pwm_ledPin, pwm1); 

    send_pwm_echo(1, pwm1); // PWM 값 echo

    Serial.print("pwm1: "); Serial.println(pwm1);

    if (bt_pwm_use == false) { // 블루투스 PWM값 수신시 제어권 획득

      old_h_pwm = h_pwm;

      hand_over = false;

      btSerial.print(" ");

      bt_pwm_use = true;

      digitalWrite(get_ledPin, false); // 가변저항 제어권 표시 led

    }

    bt_pwm = pwm1;

  }

  else if (pwm_a[1] == 2) { // 슬라이드 2번

    pwm2 = temp;

    Serial.print("pwm2: "); Serial.println(pwm2);

  }

  else if (pwm_a[1] == 3) {  // 슬라이드 3번

    pwm3 = temp;

    Serial.print("pwm3: "); Serial.println(pwm3);

  }

}

 

블루투스 시리얼 관련 코드를 따로 함수로 만들어 전체 코드의 맨 아래쪽에 위치하도록 할 예정이다. 따라서 블루투스 시리얼로 들어오는 텍스트를 처리하는 코드도 함수로 만들어 텍스트 이용 코드 추가시 바로 찾을 수 있도록 함수로 만들어 주었다.

 

void general_use() { 

  char text = btSerial.read();  // text value for some purpose.

  Serial.write(text);      // print text to Serial Monitor

}

 

앞선 예제에서 시리얼 통신을 통해 들어온 디지털 핀 및 PWM 제어값을 받는 코드를 각각 작성하여 살펴보았었다. 이번에는 둘을 합치고 btSerial_read()라는 이름의 함수로 만들도록 하겠다. 

 

if (btSerial.available()) {  실행코드 } // 시리얼 버퍼에 값이 있으면 실행코드를 실행하라

 

실행코드 

if (get_pin_val != true && btSerial.peek() == 0xF0) get_pin_val = true;                // 디지털 핀값 받는 코드 진입 플래그

else if (get_pwm_val != true && btSerial.peek() == 0xF3) get_pwm_val = true;    // PWM 제어값 받는 코드 진입 플래그

if (get_pin_val == true) { 디지털 핀값 받는 코드 실행 }                                      // 디지털 핀값 플래그가 참이면

else if (get_pwm_val == true) { PWM 제어값 받는 코드 실행 }                            // PWM 제어값 플래그가 참이면

else { 텍스트로 처리 }                                      // 디지털 핀값 플래그가 참이아니고 PWM 제어값 플래그도 참이 아니면

 

플래그 선택을 통해 각각 코드로 진입하도록 해주면 되고, 앞에서 함수로 만들어준 디지털 핀 제어 함수와 PWM 제어 함수, 텍스트 처리 함수를 해당 위치에 대체해 주었다. 

 

void btSerial_read() {

  if (btSerial.available()) {         

    if (get_pin_val != true && btSerial.peek() == 0xF0) get_pin_val = true;

    else if (get_pwm_val != true && btSerial.peek() == 0xF3) get_pwm_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_control();                                                               // 디지털 핀 제어 함수

        }

        pin_val_count = 0;

        get_pin_val = false;

      }

    } 

    else if (get_pwm_val == true) { 

      if (pwm_val_count == 0) {

        pwm_a[0] = btSerial.read();

        pwm_val_count++; 

      }

      else if (pwm_val_count > 0 && pwm_val_count < 4) {

        if (pwm_val_count == 1) {

          pwm_a[1] = btSerial.read();

          pwm_val_count++;

        }

        else if (pwm_val_count == 2) {

          pwm_a[2] = btSerial.read();

          pwm_val_count++;

        } 

        else if (pwm_val_count == 3) {

          pwm_a[3] = btSerial.read();

          pwm_val_count++;

        }

      }

      else if (pwm_val_count == 4) {

        pwm_a[4] = btSerial.read();

        if (pwm_a[0] == 0xF3 && pwm_a[4] == 0xF1) { 

          pwm_control();                                                             // PWM 제어 함수

        }

        pwm_val_count = 0;

        get_pwm_val = false;

      }

    }

    else {

      general_use();                                                                  // 텍스트 처리 함수

    }

  }

 

}

 

이제 메인루프에 가변저항 제어 코드와 시리얼 모니터 이용한 LED 텍스트 제어코드를 합치고 함수로 만들어 놓은 코드 들은 함수로 대체한다.  

 

void loop() {

  if (millis() - read_now > 100) { // 가변저항 값 읽기

    read_now = millis();

    val = analogRead(A0);

    if (val <= 15) val = 0;

    h_pwm = val/4;

  }

  if (bt_pwm_use == false) {

    if (old_h_pwm != h_pwm) { // 가변저항이 조정되면

      analogWrite(pwm_ledPin, h_pwm); // 가변저항 LED 제어

      old_h_pwm = h_pwm;

      send_pwm_slide(1, h_pwm);                                                         // 가변저항 조종값 블루투스 앱 슬라이바 표시

      Serial.print("h_pwm: "); Serial.println(h_pwm);

    }

  } else {

    if (abs(old_h_pwm - h_pwm) > 10) { // 가변저항 일정범위 이상 움직이면 제어권 획득 진행 

      hand_over = true;

    }

    if (hand_over == true) {

      Serial.println(h_pwm);

      if (abs(h_pwm - bt_pwm) < 10) { // 블루투스 값 근처 이동시 제어권 획득

        bt_pwm_use = false;

        digitalWrite(get_ledPin, true); // 가변저항 제어권 표시 led

        digitalWrite(ledPin, false); // 가변저항 제어권 힉득 진행 표시 led

        btSerial.print("potentiometer control"); 

        Serial.print("potentiometer control"); 

      } else {

        if (millis() - flicker_now > abs(h_pwm - bt_pwm)+10) {

          flicker_now = millis();

          led_state = !led_state;

          digitalWrite(ledPin, led_state); // 가변저항 제어권 힉득 진행 표시 led

        }

      }

    }

  }

  btSerial_read();                                                                                 // 블루투스 시리얼 읽기 함수

  if(Serial.available()) { // 시리얼 버퍼에 데이터 있으면

    char c = Serial.read();  

    if(c != '\n') {

      s += c;

    } else {

      if(s == "on"){ // 시리얼 모니터 제어 led on

        digitalWrite(ledPin, HIGH);

        send_pin_echo(11);                                                                     // 블루투스 앱 버튼 상태: on

        btSerial.print("LED ON");

        s = "";  

      }

      else if(s == "off"){ // 시리얼 모니터 제어 led off 

        digitalWrite(ledPin, LOW);

        send_pin_echo(10);                                                                     // 블루투스 앱 버튼 상태: off

        btSerial.print("LED OFF");  

        s = "";

      }

      else {

        btSerial.println(s); 

        s = "";

      }

    }

  }

 

}

 

안드로이드를 이용한 아두이노 무선 원격제어 FULL 코드

android_remote_control_full.zip
0.00MB

 

위의 코드를 업로드 하고 가변저항을 조정하여 LED의 밝기를 조절해보고 블루투스 앱으로도 조정해 보자. 또한 버튼 1번을 클릭하여 아두이노 기본 LED를 켜고 꺼보고 시리얼 모니터에 on/off 를 입력하여 아두이노 기본 LED도 컨트롤 해보자.

나머지 버튼들도 클릭해보면 시리얼 모니터에 그 버튼에 대한 코드 작동 여부가 표시 된다.

 

https://youtu.be/MfciYu0FSb4

 

관련 글

[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 매뉴얼

 

+ Recent posts