반응형

블루투스 4.0 BLE은 모든 작업에 UUID라는 고유 아이디를 사용하여 BLE간 통신을 하고 데이터를 전송하게 된다. 

 

블루투스 4.0 BLE를 사용하여 원격제어 하는 방법으로 버튼마다 UUID를 지정하고 그 UUID에 접속할 때 마다 코드를 실행하도록 하는 방법으로도 할 수 있다. 하지만 이 글에서는 UUID를 두개 또는 세개만 설정하여 블루투스 2.0 연결 제어방식처럼 데이터 송/수신에만 사용하고, 그 수신된 데이터에 의해 코드를 실행하도록 하겠다. 

 

블루투스 4.0 BLE 모듈 HM-10의 복제품인 AT-09와 ESP32의 내장 블루투스의 4.0 BLE 라이브러리를 사용하도록 하겠다. 

 

arduino bluetooth controller PWM 안드로이드 앱에는 HM-10 또는 그 복제품 AT-09를 사용할 수 있도록 모듈의 기본 UUID를 사용하여 프로그램 되어 있다. 

 

SERVICE_UUID              : 0000FFE0-0000-1000-8000-00805F9B34FB

CHARACTERISTIC_UUID  : 0000FFE1-0000-1000-8000-00805F9B34FB

 

상기 UUID는 AT-09에 기본 설정된 UUID 이다. AT-09에서는 CHARACTERISTIC_UUID 를 통해 데이터를 동시에 주고 받게 된다.

하지만 ESP32의 BLE 라이브러리에서는 데이터를 보내는 UUID와 받는 UUID를 따로 지정해서 사용해야 한다. 따라서 ESP 32에서 사용할 추가 UUID를 arduino bluetooth controller PWM앱에 아래와 같이 추가해 주었다.

 

CHARACTERISTIC_UUID_TX : 0000FFE2-0000-1000-8000-00805F9B34FB"

 

상기 UUID는 안드로이드 앱에 설정해 놓은 것들이다.

 

AT-09를 사용하여 아두이노에서 코딩 할 경우에는 상기 UUID에 관해 신경 쓸 필요는 없다. 왜냐하면 아두이노는 블루투스 모듈 AT-09와 시리얼 통신으로 데이터를 주고 받는다. 위에서 설명한 UUID는 AT-09가 안드로이드 앱과 데이터를 주고 받기 위한 설정일 뿐이다. 즉 모듈과 안드로이드 앱간의 문제이지 아두이노 제어에 관한 코딩과는 별개의 문제인 것이다. 

 

아두이노에서는 아두이노와 AT-09간의 시리얼 통신에 관해서만 코딩을 해주면 된다. 이 얘기는 AT-09와 안드로이드 앱간의 연결이 잘되어 통신에 문제가 없다면 블루투스 2.0을 사용한 코드를 어떤 변경없이 그대로 사용할 수 있다는 얘기가 된다. 

 

AT-09모듈과 안드로이드 앱간의 통신에 관해 말하자면,  AT-09에 설정된 UUID에 안드로이드 앱의 UUID를 맞추어 주거나, 안드로이드 앱의 UUID에 AT-09의 UUID를 맞추어 주면 기본적인 AT-09와 안드로이드 앱간의 통신 설정은 끝나게 된다. 

 

이전 글에서 안드로이드 앱을 이용한 아두이노제어에 관한 포스트의 코드를 그대로 사용하면 되고, 아두이노와 AT-09의 연결도 블루투스 2.0 모듈 HC-06과 같이 하면된다. 단지 안드로이드 앱에서 블루투스 접속시 블루투스 4.0옵션을 선택하고 연결해주어야 한다.

 

arduino bluetooth controller PWM - 아두이노 원격제어 안드로이드 앱 버전 3.5 다운로드

(*** 상기앱의 블루투스 연결은 안드로이드10 이하 버전에서만 작동합니다 ***)

 

 

arduino bluetooth controller PWM 매뉴얼

 

 

 

   

 

 

AT-09 블루투스 4.0 BLE 모듈의 연결 및 아두이노 원격제어 시연

아래 영상에서 처음에는 블루투스 4.0 BLE의 스마트폰 연결을 기존 블루투스 2.0 연결방식을 사용하여 시도하였으며, 그렇게 하였을 경우 표시되는 오류 메세지를 볼 수 있다. 그 다음 연결은 정상적인 블루투스 4.0 BLE 연결방식을 사용하여 연결하였다. 

 

https://youtu.be/YuWDPbv5FLY

 

아두이노 IDE에서 ESP32를 사용하게 되면 ESP32가 아두이노가 되며, 또한 ESP32는 보드에 블루투스를 자체 내장하고 있어 블루투스 모듈이 되기도 한다. 아두이노처럼 ESP32에 외부 블루투스 모듈AT-09를 연결하고 시리얼 통신을 통해 데이터를 주고 받는다면 위의 코드의 analogWrite()함수 부분만을 ESP32 전용 PWM 제어 함수로 대체해 주면 큰 수정없이 ESP32의 제어가 가능하게 된다. 하지만 자체 블루투스를 사용하여 제어해 보도록 하자. 

 

 

ESP32의 자체 블루투스를 사용할 때에는 아두이노와 외부 모듈 AT-09 사용시와는 다르게 블루투스와 안드로이드 앱간의 통신 설정도 아두이노 IDE를 통해 ESP32에 코딩을 해주어야만 ESP32의 블루투스와 안드로이드 앱간의 통신이 가능하게 된다. 

 

 

 

ESP32는 라이브러리를 통해 블루투스 2.0과 블루투스 4.0 BLE의 방식을 구분하여 사용한다.

 

블루투스 2.0 라이브러리는 기존 아두이노와 모듈간의 시리얼 통신과 비슷하게 라이브러리를 제공하고 있어서 외부 블루투스 모듈을 통한 시리얼 통신으로 주고 받는 방식과 똑 같이 코딩을 하여도 되지만, 블루투스 4.0 BLE 라이브러리는 블루투스 2.0라이브러리와 사뭇 달라 라이브러리에서 제공하는 기능에 맞춰 코딩을 하여야 하며 이때에 상기의 UUID를 사용하게 된다. 

 

안드로이드 앱으로 블루투스 4.0 BLE를 통해 ESP32를 제어하기 위한 데이터를 주고 받는데 필요한 UUID는 아래와 같다. 

AT-09와 공유할 수 있도록 AT-09의 두개 UUID에 1개를 더하여 모두 3개의 UUID를 사용하겠다.

 

SERVICE_UUID                    "0000FFE0-0000-1000-8000-00805F9B34FB"

CHARACTERISTIC_UUID_RX "0000FFE1-0000-1000-8000-00805F9B34FB"

CHARACTERISTIC_UUID_TX "0000FFE2-0000-1000-8000-00805F9B34FB"

 

우선 아두이노 IDE의 툴에서 보드를 ESP32 Dev Module로 변경한 뒤 파일 -> 예제 -> ESP32 BLE Arduino -> BLE_uart 예제를 실행시킨뒤 아래와 같이 수정하였다.

 

 

4개의 BLE 관련 라이브러리가 사용됨을 알수 있다.

#include <BLEDevice.h>

#include <BLEServer.h>

#include <BLEUtils.h>

#include <BLE2902.h>

 

다른 예제들과 비교해보면 ESP32에서 데이터를 전송할 때 <BLE2902.h> 라이브러리가 사용됨을 알 수있다.

BLE_uart_example_basic_modify.zip
0.00MB

#include <BLEDevice.h>

#include <BLEServer.h>

#include <BLEUtils.h>

#include <BLE2902.h>

 

BLEServer *pServer = NULL;

BLECharacteristic * pTxCharacteristic;

bool deviceConnected = false;

 

#define SERVICE_UUID                    "0000FFE0-0000-1000-8000-00805F9B34FB" // UART service UUID

#define CHARACTERISTIC_UUID_RX "0000FFE1-0000-1000-8000-00805F9B34FB"

#define CHARACTERISTIC_UUID_TX "0000FFE2-0000-1000-8000-00805F9B34FB"

 

class MyServerCallbacks: public BLEServerCallbacks {

  void onConnect(BLEServer* pServer) {

    deviceConnected = true;

  };

  void onDisconnect(BLEServer* pServer) {

    deviceConnected = false;

  }

};

 

class MyCallbacks: public BLECharacteristicCallbacks {

  void onWrite(BLECharacteristic *pCharacteristic) {

    std::string rxValue = pCharacteristic->getValue();

    if (rxValue.length() > 0) {

      for (int i = 0; i < rxValue.length(); i++)

        Serial.print(rxValue[i]);

    }

  }

};

 

void setup() {

  Serial.begin(115200);

  BLEDevice::init("ESP32 UART");  // Create the BLE Device

  pServer = BLEDevice::createServer();  // Create the BLE Server

  pServer->setCallbacks(new MyServerCallbacks());

  BLEService *pService = pServer->createService(SERVICE_UUID);  // Create the BLE Service

  pTxCharacteristic = pService->createCharacteristic (  // Create a BLE Characteristic

       CHARACTERISTIC_UUID_TX,

       BLECharacteristic::PROPERTY_NOTIFY

    );

  pTxCharacteristic->addDescriptor(new BLE2902());

  BLECharacteristic * pRxCharacteristic = pService->createCharacteristic (

   CHARACTERISTIC_UUID_RX,

   BLECharacteristic::PROPERTY_WRITE

        );

  pRxCharacteristic->setCallbacks(new MyCallbacks());

  pService->start();  // Start the service

  pServer->getAdvertising()->start();  // Start advertising

  Serial.println("Waiting a client connection to notify...");

}

 

void loop() {

  if (deviceConnected) {

    if (Serial.available()) {

      char temp[20] = { 0xFF, };

      Serial.readBytes(temp, 20);

      pTxCharacteristic->setValue(temp);

      pTxCharacteristic->notify();

    }

  }

}

 

 

상기 코드는 아두이노 IDE의 시리얼 모니터 그리고 ESP32, 안드로이드 앱 사이의 기본적인 데이터 송수신 코드이다. 코드를 업로드 하고 arduino bluetooth controller PWM 안드로이드 앱으로 연결 해보자.

 

블루투스 연결 화면에서 ESP32 BLE를 선택하고 Bluetooth 버튼을 클릭한 뒤 목록에 나오는 해당 블루투스를 클릭한다.

 

 

 

* 블루투스 4.0에 접속하기 위해서 앱은 스마트폰 블루투스 장치가 켜진 상태에서 블루투스 4.0모듈을 scan해야만 검색하고  연결할 수 있다. arduino bluetooth controller PWM 앱에서는 Bluetooth 2.0 기본 선택 버튼에서 Bluetooth 4.0 이나 ESP32 BLE를 선택하는 순간부터 블루투스 4.0 모듈을 scan하기 시작한다. 

 

 

※ 동일한 ESP32의 블루투스 클래식 연결을 위해 스마트폰에 블루투스 기기를 등록한 경우에는  등록된 기기를 삭제한 뒤에 BLE를 연결할 수 있습니다. 블루투스 장치의 등록은 블루투스 클래식(2.0)연결에만 사용하는 기능입니다.

스마트폰의 블루투스 기기 목록에 ESP32가 등록된 경우에는 BLE 연결을 방해하고 ESP32는 앱의 BLE 연결 요청에 응답하지 않습니다.

 

연결 후 앱에서 pwm1 슬라이드를 움직여 연속되는 데이터를 보내보고 텍스트로 hi를 보내보았다. 시리얼 모니터에 아래와 같이 표시 되는것을 볼 수 있다. 

시리얼 모니터에서도 hello를 입력해 보면 앱에 hello가 표시되는 것을 볼 수 있다. 

 

 

 

 

 

 

ESP32의 가변저항 및 LED 연결

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

 

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

analogRead() 핀으로 GPIO 34번 핀을 사용하였다. 

 

ESP32는 총 10 개의 ADC 핀을 제공한다. 하지만 ESP32의 BLE기능 작동코드를 업로드하고 작동 시킨 뒤에는 ADC핀인 GPIO 2, 4, 15, 36, 39핀에서 가변저항을 통해 생성된 아날로그 값을 정상적으로 읽어오지 못했으며, 34번 핀에서 정상 작동하는 것을 확인했다. 나머지 핀은 확인해 보지 않았다(BLE 작동 코드없이 analogRead() 테스트에서는 정상 작동 되었었음). 

 

 

 

 

 

 

 

블루투스 4.0 BLE 모듈 AT-09를 아두이노에 연결하여 가변저항의 아날로그 값을 읽어올 때 노이즈가 발생하는것을 보았었다. ESP32도 노이즈가 발생하는지 아래 코드를 업로드 하여 테스트 하였다. 

BLE_noise_analogread_test.zip
0.00MB

#include <BLEDevice.h>

#include <BLEServer.h>

#include <BLEUtils.h>

#include <BLE2902.h>

 

BLEServer *pServer = NULL;

BLECharacteristic * pTxCharacteristic;

bool deviceConnected = false;

 

#define SERVICE_UUID           "0000FFE0-0000-1000-8000-00805F9B34FB" // UART service UUID

#define CHARACTERISTIC_UUID_RX "0000FFE1-0000-1000-8000-00805F9B34FB"

#define CHARACTERISTIC_UUID_TX "0000FFE2-0000-1000-8000-00805F9B34FB"

 

// 가변저항 관련 변수

int val; // ADC 핀에서 전압을 읽고 변환한 값을 저장하는 변수

int h_pwm = 0; // pwm 값

 

class MyServerCallbacks: public BLEServerCallbacks {

  void onConnect(BLEServer* pServer) {

    deviceConnected = true;

  };

  void onDisconnect(BLEServer* pServer) {

    deviceConnected = false;

  }

};

 

class MyCallbacks: public BLECharacteristicCallbacks {

  void onWrite(BLECharacteristic *pCharacteristic) {

    std::string rxValue = pCharacteristic->getValue();

    if (rxValue.length() > 0) {

      for (int i = 0; i < rxValue.length(); i++)

        Serial.print(rxValue[i]);

    }

  }

};

 

void setup() {

  Serial.begin(115200);

  BLEDevice::init("ESP32 UART");  // Create the BLE Device

  pServer = BLEDevice::createServer();  // Create the BLE Server

  pServer->setCallbacks(new MyServerCallbacks());

  BLEService *pService = pServer->createService(SERVICE_UUID);  // Create the BLE Service

  pTxCharacteristic = pService->createCharacteristic (  // Create a BLE Characteristic

                              CHARACTERISTIC_UUID_TX,

                              BLECharacteristic::PROPERTY_NOTIFY

                           );

  pTxCharacteristic->addDescriptor(new BLE2902());

  BLECharacteristic * pRxCharacteristic = pService->createCharacteristic (

                                                           CHARACTERISTIC_UUID_RX,

                                                           BLECharacteristic::PROPERTY_WRITE

                                                        );

  pRxCharacteristic->setCallbacks(new MyCallbacks());

  pService->start();  // Start the service

  pServer->getAdvertising()->start();  // Start advertising

  Serial.println("Waiting a client connection to notify...");

}

  

void loop() {

  val = analogRead(34); // ESP32 ADC pin

  Serial.println(val);

  h_pwm = val/16;        // ESP32 ADC 12bit 분해능

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

  delay(100);

 

}

 

 

아두이노와 외부 블루투스 모듈과의 연결과는 다르게 0인 상태에서 값 변동없이 안정적으로 0을 표시하는것을 확인하였다. 하지만  가변저항을 움직여 아날로그를 측정하기 시작했을 때부터 측정 val값이 약 15 내외에서 변동함을 확인 하였다. 

ESP32의 아날로그 분해능은 12bit 0 ~ 4095이다

 

 

 

 

bool rx_received = false;      // BLE 수신시 함수 실행 플래그

 

std::string rxValue; // BLE 클래스 수신 데이터 값 전역변수 설정

 

#define get_ledPin 19 // LED, 가변저항 제어권 점멸 표시 및 제어권 획득 표시

#define pwm_ledPin 18 // ESP32 pwm 출력 핀(~ 핀)

 

#define pwm_channel 0 // ESP32 pwm 채널(0 ~ 15: 16개)

 

BLE 라이브러리 클래스에서 onWrite 함수가 실행되면 데이터 수신 플래그를 rx_received = true;로 설정하도록 변경하였다.

class MyCallbacks: public BLECharacteristicCallbacks {

  void onWrite(BLECharacteristic *pCharacteristic) {

    rxValue = pCharacteristic->getValue();

    rx_received = true;

  }

 

};

 

ESP32 보드 에서는 아두이노의 analogWrite() 함수를 사용하지 못하고 대체 함수인 ledcWrite() 함수를 사용해야 한다. 

 

ESP32에는 3개의 하드웨어 타이머가 있고 3개의 타이머를 사용하여 16개의 아날로그 출력을 할 수 있으며 그 출력은 채널 번호(0 ~ 15)를 할당하여 사용한다. 

 

아두이노와 비교하면서 살펴보자. 

 

setup() 함수 설정

아두이노 : pinMode(pwm_ledPin, OUTPUT);  // 디지털과 아날로그 핀의 입력/출력을 설정한다.

ESP32     : ledcAttachPin(pwm_ledPin, pwm_channel)  // 아날로그 핀과 사용할 아날로그 채널(0~15채널)연결

               ledcSetup(pwm_channel, freq, resolution)   // 아날로그 채널에 대한 설정

                                                                          (freq: 보통 5000, resolution : 보통 8)

 

loop() 함수에서의 사용

아두이노 : analogWrite(pwm_ledPin, h_pwm);   // 설정된 아날로그 핀에 값 대입

ESP32     : ledcWrite(pwm_channel, h_pwm);    // 설정된 아날로그 채널에 값 대입

 

아두이노는 아날로그 핀을 기준으로 아날로그 출력이 이루어지고 ESP32는 아날로그 채널을 기준으로 아날로그 출력이 됨을 알 수 있다.

 

ESP32 라이브러리의 esp32-hal-ledc.c 에서 아날로그 채널마다 할당된 타이머를 확인 할 수 있다.  

 * LEDC Chan to Group/Channel/Timer Mapping

** ledc: 0  => Group: 0, Channel: 0, Timer: 0

** ledc: 1  => Group: 0, Channel: 1, Timer: 0

** ledc: 2  => Group: 0, Channel: 2, Timer: 1

** ledc: 3  => Group: 0, Channel: 3, Timer: 1

** ledc: 4  => Group: 0, Channel: 4, Timer: 2

** ledc: 5  => Group: 0, Channel: 5, Timer: 2

** ledc: 6  => Group: 0, Channel: 6, Timer: 3

** ledc: 7  => Group: 0, Channel: 7, Timer: 3

** ledc: 8  => Group: 1, Channel: 0, Timer: 0

** ledc: 9  => Group: 1, Channel: 1, Timer: 0

** ledc: 10 => Group: 1, Channel: 2, Timer: 1

** ledc: 11 => Group: 1, Channel: 3, Timer: 1

** ledc: 12 => Group: 1, Channel: 4, Timer: 2

** ledc: 13 => Group: 1, Channel: 5, Timer: 2

** ledc: 14 => Group: 1, Channel: 6, Timer: 3

 

** ledc: 15 => Group: 1, Channel: 7, Timer: 3

 

위를 기준으로 하여 아래 코드를 추가 및 변경해 주었다.

ledcAttachPin(pwm_ledPin, pwm_channel);     // Esp32 analogWrite 

ledcSetup(pwm_channel, 5000, 8);                // Esp32 analogWrite

ledcWrite(pwm_channel, h_pwm);                 // analogWrite() 대체함수 - 가변저항 LED 제어

 

ESP32의 ADC는 12bit 분해능 이므로 0 ~ 255값 환산위해 16으로 나누어 주었다.

h_pwm = val/16; // ESP32 ADC 12bit 분해능

 

아두이노에서 시리얼통신(btSerial.print();)으로 출력하던 것을 아래와 같이 변경하였다.  

스트링 직접 전송 :

pTxCharacteristic->setValue("P_meter control");

pTxCharacteristic->notify(); // 전송명령

 

스트링 타입 변수값 전송 :

char temp [21];  // 버퍼 선언

s.toCharArray(temp, s.length()+1); // string s의 문자열을  s.length()+1만큼 문자배열로 복사하고 버퍼 temp에 저장(char)

pTxCharacteristic->setValue(temp); 

pTxCharacteristic->notify();

 

배열값 전송 :

pTxCharacteristic->setValue((uint8_t*)&pwm_slide, 5); // (배열명, 바이트수)

pTxCharacteristic->notify();

 

블루투스 수신부

BLE 라이브러리에서 String으로 정의된 변수 rxValue에 1byte 값을 들어온 순서대로 저장하게 된다. 하지만 rxValue가 순수한 String 변수는 아닌것 같으며, String class 코드들이 적용되지 않은것을 확인했다. 

 

 

BLE는 한번에 최대 20byte 까지 송 수신하게 된다. 따라서 rxValue를 크기가 20byte인 배열로서 취급해야 코드 적용이 된다. 

 

작동 영상

https://youtu.be/M2A8UbB5n6Q

 

esp32_ble_uart_full_poten.zip
0.00MB

 

아래코드는 가변저항 관련 코드를 삭제한 코드이다. 

esp32_ble_uart_full.zip
0.00MB

 

최신 업데이트 유료앱

https://postpop.tistory.com/175

 

ADUCON - Arduino wireless remote control application

Arduino remote control app using a wireless module available in Arduino. Bluetooth 2.0 Classic / 4.0 BLE : HC-05, HC-06, HM-10, AT-09, BT05, ESP32, etc. Wi-Fi : ESP01, ESP8266 NodeMcu, ESP32, etc. https://play.google.com/store/apps/details?id=com.tistory.p

postpop.tistory.com

 

https://postpop.tistory.com/176

 

ADUPAD - Arduino wireless remote control PAD application

Arduino remote control PAD app using a wireless module available in Arduino. Bluetooth 2.0 Classic / 4.0 BLE : HC-05, HC-06, HM-10, AT-09, BT05, ESP32, etc. Wi-Fi : ESP01, ESP8266 NodeMcu, ESP32, etc. https://play.google.com/store/apps/details?id=io.kodula

postpop.tistory.com

 

 

관련 글

[arduino] - 아두이노 - 안드로이드를 이용한 무선 원격제어 그리고 시리얼 통신 - 1편

 

[arduino] - 아두이노 - 안드로이드를 이용한 무선 원격제어 그리고 시리얼 통신 - 2편

 

[arduino] - 아두이노 - 안드로이드를 이용한 무선 원격제어 그리고 시리얼 통신 - 3편

 

[arduino] - 아두이노 - 안드로이드를 이용한 무선 원격제어 그리고 시리얼 통신 - 4편

 

[arduino] - 아두이노 - 안드로이드를 이용한 무선 원격제어 그리고 시리얼 통신 - 5편

 

[arduino] - 블루투스 4.0 BLE, 스마트폰 연결과 페어링(pairing), AT-09

 

[arduino] - 블루투스 4.0 BLE 기초 용어

 

[arduino] - 블루투스 4.0 BLE 이용 아두이노 및 ESP32 원격제어

 

[arduino] - ESP32 블루투스 4.0 BLE, 비밀번호(pin code) 이용 페어링 연결

 

arduino bluetooth controller PWM - 아두이노 원격제어 안드로이드 앱 버전 3.5 다운로드

 

arduino bluetooth controller PWM 매뉴얼

 

 

+ Recent posts