LoginSignup
1
1

BLEによるシリアル通信の無線化

Last updated at Posted at 2024-05-25

概要

M5Stamp C3U MateのBLEにてシリアル通信の無線化を行いました。
以前Bluetooth Classicの時にSPPによるシリアル通信を使っていたのですが、
BLEでは使えなくなりました。SPPは手軽で良かったのですが、BLEではなにやら難しくて。
色々探していたところ良い情報がありやっと実現できました。

私の使い勝手から送受信データは'テキスト'、終端文字'\n'としています。
M5Stamp C3U Mate2台を使い片方をServer、もう片方をClientに設定して
相互にBLE通信を行います。ラベル無しがServer、ラベル有りがClient側です。
image.png

下記が通信の状況です。COM3:Server、COM4:Clientとなっています。
ServerからのテキストデータがClient側で受信されています。
逆も同じように受信されています。
今回はUSBSerialで行いましたがUART通信も同様に無線化が可能です。

image.png

起動直後はLEDが緑に点灯します。
image.png

7,8秒ほどで接続が完了し、LEDが青に点灯します。
image.png

接続が切れるとLEDが赤に点灯します。(Client側の電源を切っています)
image.png

環境など

 エディタ:VisualStudioCode(PlatformIO IDE)
 マイコン:M5Stamp C3U Mate 2台
 フレームワーク:Aruduino

コード(Server側) 

#include <Arduino.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#include <Adafruit_NeoPixel.h>

BLEServer *pServer = NULL;
BLECharacteristic *pTxCharacteristic;
BLECharacteristic *pRxCharacteristic;
bool deviceConnected = false;
bool oldDeviceConnected = false;
bool receivedflg=false;//受信フラグ

// See the following for generating UUIDs:
// https://www.uuidgenerator.net/

//serverとcliantでTX,RXは逆転するのに注意
#define SERVICE_UUID "e942c8e4-1a8e-11ef-9262-0242ac120002"
#define CHARACTERISTIC_UUID_RX "e942cbbe-1a8e-11ef-9262-0242ac120002"
#define CHARACTERISTIC_UUID_TX "e942cdee-1a8e-11ef-9262-0242ac120002"

#define SERVER_NAME "esp32ble"
std::string rx_Data;
String rx_Str;//受信文字列
String tx_Str;//送信文字列

//LED設定
#define LED_PIN 2
Adafruit_NeoPixel pixels(1, LED_PIN, NEO_GRB + NEO_KHZ800);

class MyServerCallbacks : public BLEServerCallbacks {
  void onConnect(BLEServer *pServer) {
    deviceConnected = true;

    // BLE接続でLED青点灯
    pixels.setPixelColor(0, pixels.Color(0, 0, 50));
    pixels.show();
  };

  void onDisconnect(BLEServer *pServer) {
    deviceConnected = false;
    // BLE接続でLED赤点灯
    pixels.setPixelColor(0, pixels.Color(50, 0, 0));
    pixels.show();
  }
};

//受信時コールバック 文字列取得
class MyCallbacks: public BLECharacteristicCallbacks {
    void onWrite(BLECharacteristic *pCharacteristic) {
      std::string rxValue = pCharacteristic->getValue();
      rx_Str=rxValue.c_str(); //受信文字列取得
      receivedflg=true;       //受信フラグセット
    }
};

void setup() {
  // 起動時LED緑点灯
  pixels.begin();//NeoPixel制御開始
  pixels.setPixelColor(0, pixels.Color(0, 50, 0));//LED設定(赤, 緑, 青)
  pixels.show();//LED点灯

  Serial.begin(115200);
  Serial.println("BLE_Server_start");

  // Create the BLE Device
  BLEDevice::init(SERVER_NAME);

  // Create the BLE Server
  BLEServer *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();
  
  // Start advertising
  pServer->getAdvertising()->start();
  Serial.println("Waiting a client connection to notify...");
}

void loop() {

  if (deviceConnected) {

    // BLEデータ受信時
    if(receivedflg==true){  
      Serial.println("rev:" + rx_Str);
      receivedflg=false;
    }

    // USBシリアルデータ送信
    if (Serial.available() > 0) {
      String serial_data = Serial.readStringUntil('\n');
      tx_Str = serial_data + "\n";//終端文字追加
      pTxCharacteristic->setValue(tx_Str.c_str());
      pTxCharacteristic->notify();
    }
  }

  // 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(10);
}

コード(Client側) 

#include <Arduino.h>
#include "BLEDevice.h"
#include <Adafruit_NeoPixel.h>

#define SERVICE_UUID "e942c8e4-1a8e-11ef-9262-0242ac120002"
#define CHARACTERISTIC_UUID_TX "e942cbbe-1a8e-11ef-9262-0242ac120002"
#define CHARACTERISTIC_UUID_RX "e942cdee-1a8e-11ef-9262-0242ac120002"
#define SERVER_NAME         "esp32ble"

static BLEUUID  serviceUUID(SERVICE_UUID);
static BLEUUID  charUUID_RX(CHARACTERISTIC_UUID_RX);
static BLEUUID  charUUID_TX(CHARACTERISTIC_UUID_TX);

static BLEAddress *pServerAddress;
static boolean doConnect = false;
static boolean connected = false;
static BLERemoteCharacteristic* pRemoteCharacteristicRX;
static BLERemoteCharacteristic* pRemoteCharacteristicTX;


bool receivedflg=false;//受信フラグ
String rx_Str;//受信文字列
String tx_Str;//送信文字列

//LED設定
#define LED_PIN 2
Adafruit_NeoPixel pixels(1, LED_PIN, NEO_GRB + NEO_KHZ800);

static void notifyCallback(
  BLERemoteCharacteristic* pBLERemoteCharacteristic,
  uint8_t* pData,
  size_t length,
  bool isNotify) {
    rx_Str = (char*)pData;// BLEデータ取得
    receivedflg = true;//受信フラグセット
}

class MyClientCallback : public BLEClientCallbacks {
  void onConnect(BLEClient* pclient) {
    // BLE接続でLED青点灯
    pixels.setPixelColor(0, pixels.Color(0, 0, 50));
    pixels.show();
  }

  void onDisconnect(BLEClient* pclient) {
    connected = false;
    Serial.println("onDisconnect");

    // BLE接続でLED赤点灯
    pixels.setPixelColor(0, pixels.Color(50, 0, 0));
    pixels.show();
  }
};

bool connectToServer(BLEAddress pAddress) {
    Serial.print("Forming a connection to ");
    Serial.println(pAddress.toString().c_str());
    BLEClient*  pClient  = BLEDevice::createClient();
    Serial.println(" - Created client");

    pClient->setClientCallbacks(new MyClientCallback());
    
    // Connect to the remove BLE Server.            
    pClient->connect(pAddress);
    Serial.println(" - Connected to server");
    pClient->setMTU(517); //set client to request maximum MTU from server (default is 23 otherwise)

    BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
    Serial.print(" - Connected to server :");
    Serial.println(serviceUUID.toString().c_str());
    if (pRemoteService == nullptr) {
      Serial.print("Failed to find our service UUID: ");
      Serial.println(serviceUUID.toString().c_str());
      pClient->disconnect();
      return false;
    }
    
    pRemoteCharacteristicRX = pRemoteService->getCharacteristic(charUUID_RX);
    if (pRemoteCharacteristicRX == nullptr) {
      Serial.print("Failed to find characteristic RX ID: ");
      Serial.println(charUUID_RX.toString().c_str());
      pClient->disconnect();
      return false;
    }
    Serial.print(" - Found characteristic RX ID: ");
    Serial.println(charUUID_RX.toString().c_str());

    // Send用キャラクタリスティックの参照を取得する
    pRemoteCharacteristicTX = pRemoteService->getCharacteristic(charUUID_TX);
    if (pRemoteCharacteristicTX == nullptr) {
      Serial.print("Failed to find characteristic TX ID ");
      Serial.println(charUUID_TX.toString().c_str());
      pClient->disconnect();
      return false;
    }
    Serial.print(" - Found characteristic TX ID: ");
    Serial.println(charUUID_TX.toString().c_str());
    
    // Read the value of the characteristic.
    if(pRemoteCharacteristicRX->canRead()) {
      std::string value = pRemoteCharacteristicRX->readValue();
      Serial.print("The characteristic value was: ");
      Serial.println(value.c_str());
    }

    if(pRemoteCharacteristicRX->canNotify())    
    pRemoteCharacteristicRX->registerForNotify(notifyCallback);
    Serial.println("End of notify");
    
    return true;
}


class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
  void onResult(BLEAdvertisedDevice advertisedDevice) {
    Serial.print("BLE Advertised Device found: ");
    Serial.println(advertisedDevice.toString().c_str());
    Serial.println(advertisedDevice.getName().c_str());
    
    if(advertisedDevice.getName()==SERVER_NAME){
      Serial.println(advertisedDevice.getAddress().toString().c_str());
      advertisedDevice.getScan()->stop();
      pServerAddress = new BLEAddress(advertisedDevice.getAddress());
      doConnect = true;
    }
  }
};

void get_scan(){
  BLEScan* pBLEScan = BLEDevice::getScan();
  Serial.println("getScan");
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
  Serial.println("setAdvertisedDeviceCallbacks");
  pBLEScan->setActiveScan(true);
  pBLEScan->start(10); 
  Serial.println("");
  Serial.println("End of scan");
}


void setup() {
  // 起動時LED緑点灯
  pixels.begin();//NeoPixel制御開始
  pixels.setPixelColor(0, pixels.Color(0, 50, 0));//LED設定(赤, 緑, 青)
  pixels.show();//LED点灯

  Serial.begin(115200);
  delay(10000);

  Serial.println("BLE_Cliant_start");
  BLEDevice::init("");
  get_scan();
  Serial.println("End of setup");
}


void loop() {
  if (doConnect == true) {
    if (connectToServer(*pServerAddress)) {
      Serial.println("We are now connected to the BLE Server.");
      connected = true;
      

    } else {
      Serial.println("We have failed to connect to the server; there is nothin more we will do.");
      connected = false;
    }
    doConnect = false;
  }
  if (connected) {

    // BLEデータ受信時
    if(receivedflg==true){
      Serial.println("rev:"+rx_Str);
      receivedflg = false;
    }

    // USBシリアルデータ送信
    if (Serial.available() > 0) {
      String serial_data = Serial.readStringUntil('\n');
      tx_Str = serial_data + "\n";//終端文字追加
      pRemoteCharacteristicTX->writeValue(tx_Str.c_str());
    }

  } else{
    Serial.println("Not connected");
  //conectできないときにsscanループさせておくため
    doConnect = false;
    get_scan();
  }
  delay(10);
}

plathomeio.ini設定

[env:esp32-c3-devkitm-1]
platform = espressif32
board = esp32-c3-devkitm-1
framework = arduino
monitor_speed = 115200
build_flags =
-DARDUINO_USB_CDC_ON_BOOT
-DARDUINO_USB_MODE
board_build.partitions = no_ota.csv
lib_deps =
m5stack/M5Unified@^0.1.14
adafruit/Adafruit NeoPixel@^1.12.2

参考サイト

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1