ESP32 BLE & RS-232 RS-485통신

2023. 3. 2. 15:41개발/Arduino

ESP32 를 사용해서

BLE 및 RS-232 RS-485 통신 테스트를 한다.

 

2023.02.28 - [기록/개발 노트] - ESP32 web server

 

ESP32 web server

새로 제작중인 PCB 보드를 테스트 한다. Wifi, BLE 신호를 RS-232 RS-485 유선신호로 변경하는 기능을 구현한다. ----- 동작확인 ----- 1. ESP32 실행한 뒤 할당받은 ip에 접속한다. RS 232 버튼을 누르면 해당 ip

iruk.tistory.com

제작한 보드의

전파인증 진행과정이다.


1. BLE 송신 소스

/*
 BLE 데이터 송신용 (RS-232)
 BLE 데이터 송신 후 유선으로 RS-232, RS-485 신호를 수신받음
 */
#include "BLEDevice.h"

unsigned long previousMillis = 0;     
const long interval = 3000; 
unsigned int counter=0;
String inString;

static BLEUUID    serviceUUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E");
static BLEUUID rxUUID("6E400002-B5A3-F393-E0A9-E50E24DCCA9E");
static BLEUUID txUUID("6E400003-B5A3-F393-E0A9-E50E24DCCA9E");

//연결 및 장치 검색 상태 변수
static boolean doConnect = false;
static boolean connected = false;
static boolean doScan = false;

static BLERemoteCharacteristic* pRxCharacteristic;
static BLERemoteCharacteristic* pTxCharacteristic;
static BLEAdvertisedDevice* myDevice;

static void notifyCallback(
  BLERemoteCharacteristic* pBLERemoteCharacteristic,
  uint8_t* pData,
  size_t length,
  bool isNotify) {
    Serial.print("Notify callback for characteristic ");
    Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str());
    Serial.print(" of data length ");
    Serial.println(length);
    Serial.print("data: ");
    Serial.println((char*)pData);  // data
}

class MyClientCallback : public BLEClientCallbacks {
  void onConnect(BLEClient* pclient) {
  }
  void onDisconnect(BLEClient* pclient) {
    connected = false;
    doScan = true;
    Serial.println("onDisconnect");
  }
};

bool connectToServer() {  
    Serial.print("Forming a connection to ");   
    
    //myDevice는 연결할 정보가 담긴 변수->여기서 정보를 넣어줌
    Serial.println(myDevice->getAddress().toString().c_str());
    
    BLEClient*  pClient  = BLEDevice::createClient();
    Serial.println(" - Created client");
    pClient->setClientCallbacks(new MyClientCallback());
    pClient->connect(myDevice);
    Serial.println(" - Connected to server");
    BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
    
    //UUID를 못가져올 시 연결해제->종료
    if (pRemoteService == nullptr) {        
      Serial.println(serviceUUID.toString().c_str());
      pClient->disconnect();
      return false;      
    }
    
    // Tx characteristic 받아오기
    pTxCharacteristic = pRemoteService->getCharacteristic(txUUID);
    if (pTxCharacteristic == nullptr) {
      Serial.print("Failed to find our characteristic UUID: ");
      Serial.println(txUUID.toString().c_str());
      pClient->disconnect();
      return false;
    }
    
    // Tx characteristic이 notify 지원하는지
    if(pTxCharacteristic->canNotify()) {    
      //데이터를 받을 콜백함수 등록
      pTxCharacteristic->registerForNotify(notifyCallback);
    }

    // Rx charateristic 받아오기
    pRxCharacteristic = pRemoteService->getCharacteristic(rxUUID);
    if (pRxCharacteristic == nullptr) {
      Serial.print("Failed to find our characteristic UUID: ");
      Serial.println(rxUUID.toString().c_str());
      pClient->disconnect();
      return false;
    } 
 
    // Rx characteristic이 write지원하는지
    if(!pRxCharacteristic->canWrite()) {
      Serial.print("Failed to find our characteristic UUID: ");
      Serial.println(rxUUID.toString().c_str());
      pClient->disconnect();
      return false;
    }
    
    // 여기까지 Client,Rx,Tx 연결완료
    // connectToServer()함수 마무리
    connected = true;
    return true;
}

// 주변 장치 검색함수 ( 서버는 수시로 advertising함 )
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
  void onResult(BLEAdvertisedDevice advertisedDevice) {
    Serial.print("BLE Advertised Device found: ");    
    Serial.print(advertisedDevice.haveServiceUUID());
    Serial.print(" , ");
    Serial.println(advertisedDevice.toString().c_str());
    // // advertising정보에 같은 같은 Service UUID가 있는지 확인
    if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) {
      Serial.print("A device to be connected has been found.");
      BLEDevice::getScan()->stop();
      //해당 장치 정보를 myDevice에 저장
      myDevice = new BLEAdvertisedDevice(advertisedDevice);
      doConnect = true;
      doScan = false;
    }
  }
};

void serial1Event() { // RS232 read
  if(Serial1.available()) {
    char ch1 = Serial1.read();
    Serial.write(ch1);
  }
}

void serial2Event() { // RS485 read
  if(Serial2.available()) {
    char ch2 = Serial2.read();
    Serial.write(ch2);
  }
}

void tick() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    sendData();
  }
}

void sendData(){   
    if (doConnect == true) {
    if (connectToServer()) {
      Serial.println("We are now connected to the BLE Server.");
      } else {
      Serial.println("We have failed to connect to the server; there is nothin more we will do.");
      }
      doConnect = false;
      }

  if (connected) {
    if(counter==0){
      inString = "232"; 
      counter++; 
    }
    else if(counter==1){
      inString = "485"; 
      counter=0;     
    }
     pRxCharacteristic->writeValue(inString.c_str(), inString.length()); 
     }
  else if(doScan){
    BLEDevice::getScan()->start(0);
    }
}

void setup() {
  Serial.begin(115200);
  Serial1.begin(115200, SERIAL_8N1, 26, 27); //RS-232
  Serial2.begin(115200, SERIAL_8N1, 16, 17); //RS-485

  Serial.println("Starting Arduino BLE Client application...");
  
  //BLE 클래스 초기화
  BLEDevice::init("test");
  //스캔 클래스 생성
  BLEScan* pBLEScan = BLEDevice::getScan();
  //장치 검색되면 호출할 콜백함수 등록
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
  pBLEScan->start(0);
} 

void loop(){
  if (doConnect == true) {
    if (connectToServer()) {
      Serial.println("We are now connected to the BLE Server.");
      } else {
      Serial.println("We have failed to connect to the server; there is nothin more we will do.");
      }
      doConnect = false;
  }
  tick();
  serial1Event();
  serial2Event();
}

해당 보드에서 BLE 데이터를 다른 보드에 전송하면

수신받은 보드에서 RS-232, RS-485 신호로 변환해

다시 해당 보드로 데이터를 전송하는 구조이다.


소스 분석

void setup() {
  Serial.begin(115200);
  Serial1.begin(115200, SERIAL_8N1, 26, 27); //RS-232
  Serial2.begin(115200, SERIAL_8N1, 16, 17); //RS-485

  Serial.println("Starting Arduino BLE Client application...");
  
  //BLE 클래스 초기화
  BLEDevice::init("TheOneSystem");
  //스캔 클래스 생성
  BLEScan* pBLEScan = BLEDevice::getScan();
  //장치 검색되면 호출할 콜백함수 등록
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
  pBLEScan->start(0);
}

Serial1, Serial2 포트를 열어준 뒤

BLEDevice::init() 으로 BLE 서버에 접속한다.

 

Client 장치가 식별할 수 있도록

Server 장치는 주변에 계속 advertising 한다.

setAdvertiseDeviceCallbacks() 로 인해

아래 함수가 실행된다.

class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
  void onResult(BLEAdvertisedDevice advertisedDevice) {
    Serial.print("BLE Advertised Device found: ");    
    Serial.print(advertisedDevice.haveServiceUUID());
    Serial.print(" , ");
    Serial.println(advertisedDevice.toString().c_str());
    // // advertising정보에 같은 같은 Service UUID가 있는지 확인
    if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) {
      Serial.print("A device to be connected has been found.");
      BLEDevice::getScan()->stop();
      //해당 장치 정보를 myDevice에 저장
      myDevice = new BLEAdvertisedDevice(advertisedDevice);
      doConnect = true;
      doScan = false;
    }
  }
};

주변 장치중에서 같은 UUID를 가진 장치를 발견하면

자동으로 접속한다.

myDevice에 해당 장치의 정보를 담고

환경변수 doConnect 와 doScan을 각각 true false로 저장한다.

 

연결할 장치를 찾았으니

더이상 스캔할 필요가 없기 때문이다.

void loop(){
  if (doConnect == true) {
    if (connectToServer()) {
      Serial.println("We are now connected to the BLE Server.");
      } else {
      Serial.println("We have failed to connect to the server; there is nothin more we will do.");
      }
      doConnect = false;
  }
  tick();
  serial1Event();
  serial2Event();
}

이제 loop문이 돌앙가는데

앞선 코드에서 doConnect 환경변수가 true로 저장됐으므로

if( connectToServer() ) 가 실행된다.

bool connectToServer() {  
    Serial.print("Forming a connection to ");   
    
    //myDevice는 연결할 정보가 담긴 변수->여기서 정보를 넣어줌
    Serial.println(myDevice->getAddress().toString().c_str());
    
    BLEClient*  pClient  = BLEDevice::createClient();
    Serial.println(" - Created client");
    pClient->setClientCallbacks(new MyClientCallback());
    pClient->connect(myDevice);
    Serial.println(" - Connected to server");
    BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
    
    //UUID를 못가져올 시 연결해제->종료
    if (pRemoteService == nullptr) {        
      Serial.println(serviceUUID.toString().c_str());
      pClient->disconnect();
      return false;      
    }
    
    // Tx characteristic 받아오기
    pTxCharacteristic = pRemoteService->getCharacteristic(txUUID);
    if (pTxCharacteristic == nullptr) {
      Serial.print("Failed to find our characteristic UUID: ");
      Serial.println(txUUID.toString().c_str());
      pClient->disconnect();
      return false;
    }
    
    // Tx characteristic이 notify 지원하는지
    if(pTxCharacteristic->canNotify()) {    
      //데이터를 받을 콜백함수 등록
      pTxCharacteristic->registerForNotify(notifyCallback);
    }

    // Rx charateristic 받아오기
    pRxCharacteristic = pRemoteService->getCharacteristic(rxUUID);
    if (pRxCharacteristic == nullptr) {
      Serial.print("Failed to find our characteristic UUID: ");
      Serial.println(rxUUID.toString().c_str());
      pClient->disconnect();
      return false;
    } 
 
    // Rx characteristic이 write지원하는지
    if(!pRxCharacteristic->canWrite()) {
      Serial.print("Failed to find our characteristic UUID: ");
      Serial.println(rxUUID.toString().c_str());
      pClient->disconnect();
      return false;
    }
    
    // 여기까지 Client,Rx,Tx 연결완료
    // connectToServer()함수 마무리
    connected = true;
    return true;
}
static void notifyCallback(
  BLERemoteCharacteristic* pBLERemoteCharacteristic,
  uint8_t* pData,
  size_t length,
  bool isNotify) {
    Serial.print("Notify callback for characteristic ");
    Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str());
    Serial.print(" of data length ");
    Serial.println(length);
    Serial.print("data: ");
    Serial.println((char*)pData);  // data
}

connectToServer() 에서

연결할 장치의 UUID 및 나머지 정보들을 받아온다.

 

BLE 접속 후 데이터 송/수신을 위한

class 정의를 해준다.

void tick() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    sendData();
  }
}

접속 후 tick() 함수가 실행된다.

현재 시간 millis() 를 받아와

일정시간마다 동작하는 타이머 함수이다.

const long interval = 3000;

 

 

 

 

 

interavl 값을 3000( 3초 )ms 로 설정했다.

따라서 tick() 은 loop() 내에서 무한반복되고

tick() 내의 sendData() 함수는 3초에 한번씩 실행된다.

void sendData(){   
    if (doConnect == true) {
    if (connectToServer()) {
      Serial.println("We are now connected to the BLE Server.");
      } else {
      Serial.println("We have failed to connect to the server; there is nothin more we will do.");
      }
      doConnect = false;
      }

  if (connected) {
    if(counter==0){
      inString = "232"; 
      counter++; 
    }
    else if(counter==1){
      inString = "485"; 
      counter=0;     
    }
     pRxCharacteristic->writeValue(inString.c_str(), inString.length()); 
     }
  else if(doScan){
    BLEDevice::getScan()->start(0);
    }
}

"232" "485" 문자열을 트리거 형식으로 전송한다.

번갈아가면서 BLE client에 전송한다.


2. BLE 수신 코드

/*
 BLE 데이터 수신 및 RS-232 RS-485 신호 송신용
 */
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>

BLEServer *pServer = NULL;
BLECharacteristic * pTxCharacteristic;
bool deviceConnected = false;
bool oldDeviceConnected = false;
uint8_t txValue = 0;
uint32_t value = 0;
unsigned int counter232, counter485 = 0;

#define SERVICE_UUID           "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"

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) {
        Serial.println("*********");
        Serial.print("Received BLE Value: ");
        for (int i = 0; i < rxValue.length(); i++)
          Serial.print(rxValue[i]);
          if(rxValue=="232"){
            Serial1.println("232test"+String(counter232));
            counter232++;
          }
          else if(rxValue=="485"){
            Serial2.println("485test"+String(counter485));
            counter485++;
          }
        Serial.println();
        Serial.println("*********");
      }
    }
};

void setup() {
  Serial.begin(115200);
  Serial1.begin(115200, SERIAL_8N1, 26, 27); //RS-232
  Serial2.begin(115200, SERIAL_8N1, 16, 17); //RS-485


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

  // Create the BLE Server
  pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());

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

  // Create a BLE Characteristic
  pTxCharacteristic = pService->createCharacteristic(
										CHARACTERISTIC_UUID_TX,
										BLECharacteristic::PROPERTY_NOTIFY
									);
                      
  pTxCharacteristic->addDescriptor(new BLE2902());

  BLECharacteristic * pRxCharacteristic = pService->createCharacteristic(
											 CHARACTERISTIC_UUID_RX,
											BLECharacteristic::PROPERTY_WRITE
										);

  pRxCharacteristic->setCallbacks(new MyCallbacks());

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

  pServer->getAdvertising()->addServiceUUID(SERVICE_UUID);

  // Start advertising
  pServer->getAdvertising()->start();
  Serial.println("Waiting a client connection to notify...");
}

void loop() {
//    if (deviceConnected) {
//        String inputString = "test";
//        //pTxCharacteristic->setValue(&txValue, 1);
//        pTxCharacteristic->setValue(inputString.c_str());
//        pTxCharacteristic->notify();
//        //txValue++;
//		delay(10); // bluetooth stack will go into congestion, if too many packets are sent	 
//	}

    // disconnecting
    if (!deviceConnected && oldDeviceConnected) {
        delay(500); // give the bluetooth stack the chance to get things ready
        pServer->startAdvertising(); // restart advertising
        Serial.println("start advertising");
        oldDeviceConnected = deviceConnected;
    }
    // connecting
    if (deviceConnected && !oldDeviceConnected) {
		// do stuff here on connecting
        oldDeviceConnected = deviceConnected;
    }
    delay(1000);
}

BLE 데이터를 수신받아

RS-232 RS-485 신호로 변환 후 재전송한다.


소스 분석

void setup() {
  Serial.begin(115200);
  Serial1.begin(115200, SERIAL_8N1, 26, 27); //RS-232
  Serial2.begin(115200, SERIAL_8N1, 16, 17); //RS-485


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

  // Create the BLE Server
  pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());

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

  // Create a BLE Characteristic
  pTxCharacteristic = pService->createCharacteristic(
										CHARACTERISTIC_UUID_TX,
										BLECharacteristic::PROPERTY_NOTIFY
									);
                      
  pTxCharacteristic->addDescriptor(new BLE2902());

  BLECharacteristic * pRxCharacteristic = pService->createCharacteristic(
											 CHARACTERISTIC_UUID_RX,
											BLECharacteristic::PROPERTY_WRITE
										);

  pRxCharacteristic->setCallbacks(new MyCallbacks());

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

  pServer->getAdvertising()->addServiceUUID(SERVICE_UUID);

  // Start advertising
  pServer->getAdvertising()->start();
  Serial.println("Waiting a client connection to notify...");
}

 

pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());
class MyServerCallbacks: public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) {
      deviceConnected = true;
    };

    void onDisconnect(BLEServer* pServer) {
      deviceConnected = false;
    }
};

서버생성 및 서버 연결상태를 다룰 callback 함수를 생성한다.

#define SERVICE_UUID           "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"

  pTxCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_TX,
BLECharacteristic::PROPERTY_NOTIFY
);
                      
  pTxCharacteristic->addDescriptor(new BLE2902());

  BLECharacteristic * pRxCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_RX,
BLECharacteristic::PROPERTY_WRITE
);

장치의 UUID 에 맞게 TX, RX UUID를 지정해준다.

Notify, Write 속성을 갖는지 확인 후 지정한다.

pRxCharacteristic->setCallbacks(new MyCallbacks());

RX UUID는 데이터를 읽을 수 있으므로

callback함수를 지정해준다.

class MyCallbacks: public BLECharacteristicCallbacks {
    void onWrite(BLECharacteristic *pCharacteristic) {
      std::string rxValue = pCharacteristic->getValue();

      if (rxValue.length() > 0) {
        Serial.println("*********");
        Serial.print("Received BLE Value: ");
        for (int i = 0; i < rxValue.length(); i++)
          Serial.print(rxValue[i]);
          if(rxValue=="232"){
            Serial1.println("232test"+String(counter232));
            counter232++;
          }
          else if(rxValue=="485"){
            Serial2.println("485test"+String(counter485));
            counter485++;
          }
        Serial.println();
        Serial.println("*********");
      }
    }
};

callback 함수에서는

"232" 를 수신했을 때와

"485" 를 수신했을 때를 구분해서

각각 Serial1, Serial2 로 유선신호를 내보낸다.

 

그렇게되면 처음에 설명했던 보드에서

그 유선신호를 받아서 시리얼 모니터에 출력한다.


github : https://github.com/choiprin/Arduino/tree/main/WiFi/Receive

'개발 > Arduino' 카테고리의 다른 글

Xiaomi Flower care + ESP32 / BLE통신  (0) 2023.03.13
Lora 및 485 통신  (0) 2023.03.08
ESP32 web server  (0) 2023.02.28
성능지표 테스트  (0) 2023.01.10
성능지표 및 Arduino 함수 트리거 처리  (2) 2023.01.04