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 |