Connecting two seeeds via BLE

Hi,
I’m trying to connect two Sededs XIAO ESP32 C3 together via BLE. I’ve been using ChatGPT for the past 3 nights to try and trouble shoot it and feel i’m close, but just can’t quite crack the last issue.

I am able to set up the Server and Client and send simple Chars back and forth. Great!
However, I want to be able to send Data packs based on what function is running on the Server back and forth. It seems I am able to Send Data from the Server and Receive it on the Client. Unfortuntately I can’t get it to work the other way round. I am pretty sure the Client is sending it, but the Server either is not receiving it, or can’t interpret the incoming data.

Here is the Code for the Server:

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

// Define UUIDs and Variables
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID_TX "beb5483e-36e1-4688-b7f5-ea07361b26a8"
#define CHARACTERISTIC_UUID_RX "e66e361a-e7c8-44ea-9cf5-d60b151b3f93" // Additional characteristic for receiving data

int Pong_P1_Score;

struct PongData {
  int var1;
  int var2;
  int var3;
  int var4;
};

struct JumpData {
  int var1;
  int var2;
  float var3;
};

union DataPacket {
  PongData pong;
  JumpData jump;
};

struct CombinedDataPacket {
  uint8_t type; // 0 for PongData, 1 for JumpData
  DataPacket data;
};

CombinedDataPacket dataToSend;
CombinedDataPacket dataReceived;

BLECharacteristic *txCharacteristic;
BLECharacteristic *rxCharacteristic;

bool deviceConnected = false;

// BLE Server Callbacks
class MyServerCallbacks: public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) {
      deviceConnected = true;
      Serial.println("Client connected");
    };

    void onDisconnect(BLEServer* pServer) {
      deviceConnected = false;
      Serial.println("Client disconnected");
    }
};

// BLE Characteristic Callbacks
class MyCallbacks: public BLECharacteristicCallbacks {
    void onWrite(BLECharacteristic *pCharacteristic) {
        // Get data from the characteristic as a String
        String value = pCharacteristic->getValue().c_str();
        size_t length = value.length();
        
        Serial.print("Received data length: ");
        Serial.println(length);

        // Debugging: Print the received string data
        Serial.print("Received data: ");
        Serial.println(value);

        // Check length and ensure data is not empty
        if (length == sizeof(CombinedDataPacket)) {
            // Convert the String data to a byte array
            byte* byteArray = (byte*) value.c_str();

            // Copy received data to our struct
            memcpy(&dataReceived, byteArray, sizeof(CombinedDataPacket));

            // Handle the received data based on its type
            if (dataReceived.type == 0) {
                Serial.println("Received Pong Data:");
                Serial.print("Pong var1: ");
                Serial.println(dataReceived.data.pong.var1);
                Serial.print("Pong var2: ");
                Serial.println(dataReceived.data.pong.var2);
                Serial.print("Pong var3: ");
                Serial.println(dataReceived.data.pong.var3);
                Serial.print("Pong var4: ");
                Serial.println(dataReceived.data.pong.var4);
            } else if (dataReceived.type == 1) {
                Serial.println("Received Jump Data:");
                Serial.print("Jump var1: ");
                Serial.println(dataReceived.data.jump.var1);
                Serial.print("Jump var2: ");
                Serial.println(dataReceived.data.jump.var2);
                Serial.print("Jump var3: ");
                Serial.println(dataReceived.data.jump.var3);
            }
        } else {
            Serial.println("Received data length mismatch");
        }
    }
};



void sendData() {
  if (deviceConnected) {
    // Sending data to client
    txCharacteristic->setValue((uint8_t*)&dataToSend, sizeof(dataToSend));
    txCharacteristic->notify();
    Serial.println("Data sent to client");
  }
}

void updatePongData() {
  // Update the Pong data based on game logic
  dataToSend.type = 0; // 0 for PongData
  dataToSend.data.pong.var1 = Pong_P1_Score;
  dataToSend.data.pong.var2 = 20;
  dataToSend.data.pong.var3 = 30;
  dataToSend.data.pong.var4 = 40;

  // Send the updated data
  sendData();
}

void updateJumpData() {
  // Update the Jump data based on game logic
  dataToSend.type = 1; // 1 for JumpData
  dataToSend.data.jump.var1 = 10;
  dataToSend.data.jump.var2 = 20;
  dataToSend.data.jump.var3 = 30.5;

  // Send the updated data
  sendData();
}

void setup() {
  Serial.begin(115200);

  // Initialize BLE
  BLEDevice::init("ESP32_Server");
  BLEServer *pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());

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

  // Create BLE characteristics
  txCharacteristic = pService->createCharacteristic(
                      CHARACTERISTIC_UUID_TX,
                      BLECharacteristic::PROPERTY_NOTIFY
                    );
  txCharacteristic->addDescriptor(new BLE2902());

  rxCharacteristic = pService->createCharacteristic(
                      CHARACTERISTIC_UUID_RX,
                      BLECharacteristic::PROPERTY_WRITE
                    );
  rxCharacteristic->setCallbacks(new MyCallbacks());

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

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

void loop() {
  Pong_P1_Score += 1;
  
  // Call the function to update Pong data and send it
  updatePongData();

  delay(1000); // Adjust the delay as needed
}

Here is the Client Code:

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

// Define UUIDs and Variables
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID_TX "beb5483e-36e1-4688-b7f5-ea07361b26a8"
#define CHARACTERISTIC_UUID_RX "e66e361a-e7c8-44ea-9cf5-d60b151b3f93" // Additional characteristic for receiving data

int Pong_P1_Score;

struct PongData {
  int var1;
  int var2;
  int var3;
  int var4;
};

struct JumpData {
  int var1;
  int var2;
  float var3;
};

union DataPacket {
  PongData pong;
  JumpData jump;
};

struct CombinedDataPacket {
  uint8_t type; // 0 for PongData, 1 for JumpData
  DataPacket data;
};

CombinedDataPacket dataToSend;
CombinedDataPacket dataReceived;

BLECharacteristic *txCharacteristic;
BLECharacteristic *rxCharacteristic;

bool deviceConnected = false;

// BLE Server Callbacks
class MyServerCallbacks: public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) {
      deviceConnected = true;
      Serial.println("Client connected");
    };

    void onDisconnect(BLEServer* pServer) {
      deviceConnected = false;
      Serial.println("Client disconnected");
    }
};

// BLE Characteristic Callbacks
class MyCallbacks: public BLECharacteristicCallbacks {
    void onWrite(BLECharacteristic *pCharacteristic) {
        // Get data from the characteristic as a String
        String value = pCharacteristic->getValue().c_str();
        size_t length = value.length();
        
        Serial.print("Received data length: ");
        Serial.println(length);

        // Debugging: Print the received string data
        Serial.print("Received data: ");
        Serial.println(value);

        // Check length and ensure data is not empty
        if (length == sizeof(CombinedDataPacket)) {
            // Convert the String data to a byte array
            byte* byteArray = (byte*) value.c_str();

            // Copy received data to our struct
            memcpy(&dataReceived, byteArray, sizeof(CombinedDataPacket));

            // Handle the received data based on its type
            if (dataReceived.type == 0) {
                Serial.println("Received Pong Data:");
                Serial.print("Pong var1: ");
                Serial.println(dataReceived.data.pong.var1);
                Serial.print("Pong var2: ");
                Serial.println(dataReceived.data.pong.var2);
                Serial.print("Pong var3: ");
                Serial.println(dataReceived.data.pong.var3);
                Serial.print("Pong var4: ");
                Serial.println(dataReceived.data.pong.var4);
            } else if (dataReceived.type == 1) {
                Serial.println("Received Jump Data:");
                Serial.print("Jump var1: ");
                Serial.println(dataReceived.data.jump.var1);
                Serial.print("Jump var2: ");
                Serial.println(dataReceived.data.jump.var2);
                Serial.print("Jump var3: ");
                Serial.println(dataReceived.data.jump.var3);
            }
        } else {
            Serial.println("Received data length mismatch");
        }
    }
};



void sendData() {
  if (deviceConnected) {
    // Sending data to client
    txCharacteristic->setValue((uint8_t*)&dataToSend, sizeof(dataToSend));
    txCharacteristic->notify();
    Serial.println("Data sent to client");
  }
}

void updatePongData() {
  // Update the Pong data based on game logic
  dataToSend.type = 0; // 0 for PongData
  dataToSend.data.pong.var1 = Pong_P1_Score;
  dataToSend.data.pong.var2 = 20;
  dataToSend.data.pong.var3 = 30;
  dataToSend.data.pong.var4 = 40;

  // Send the updated data
  sendData();
}

void updateJumpData() {
  // Update the Jump data based on game logic
  dataToSend.type = 1; // 1 for JumpData
  dataToSend.data.jump.var1 = 10;
  dataToSend.data.jump.var2 = 20;
  dataToSend.data.jump.var3 = 30.5;

  // Send the updated data
  sendData();
}

void setup() {
  Serial.begin(115200);

  // Initialize BLE
  BLEDevice::init("ESP32_Server");
  BLEServer *pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());

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

  // Create BLE characteristics
  txCharacteristic = pService->createCharacteristic(
                      CHARACTERISTIC_UUID_TX,
                      BLECharacteristic::PROPERTY_NOTIFY
                    );
  txCharacteristic->addDescriptor(new BLE2902());

  rxCharacteristic = pService->createCharacteristic(
                      CHARACTERISTIC_UUID_RX,
                      BLECharacteristic::PROPERTY_WRITE
                    );
  rxCharacteristic->setCallbacks(new MyCallbacks());

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

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

void loop() {
  Pong_P1_Score += 1;
  
  // Call the function to update Pong data and send it
  updatePongData();

  delay(1000); // Adjust the delay as needed
}



Both codes compile fine if you can/want to try it out.

ChatGPT tries to suggest to adjust the following Server code:

        String value = pCharacteristic->getValue().c_str();

to:

    std::string value = pCharacteristic->getValue();  // This line causes issues

But then the Arduino IDE can’t compile it anymore.

I’m stuck. How can I make that the Server can read what the client is sending?

Thank you very much for your time and help.

Hi there,
and Welcome here,
I would look to use the NOTIFY property, subscribe to it and the change by data being present you can read it.
Look to the notify examples and implement that to do it.
HTH
GL :slight_smile: PJ
:v:

You may need use the Uart Service also in this configuration.
Look at the examples the chatGPT may not have the right info. YMMV :+1:

let us know if you get it working! I would like to try myself

Hi there,
So I compiled and running the Server code on a C3 and Using the Nrf Connect for Desktop and the BLE nordic Dongle($9) I see the Attribute value changing (incrementing) when I sub to the Notify.
and The WRITE appears to work ?


something in the code logic doesn’t seem correct?
I can try the Client side next but not sure if that is the issue.
HTH
GL :slight_smile: PJ
:v:

Client appears to work the same way… So it’s NOT going to work like that.
Take another look at the Arduino examples for BLE and use that as a guide instead of chatGPT would be my suggestion. LOL :face_with_peeking_eye:

Thanks so much for all the replies! I’ll dive into the suggestions today and report back. Thanks