esp32-snippets icon indicating copy to clipboard operation
esp32-snippets copied to clipboard

Getting two Services when I only start one

Open gamadan opened this issue 2 years ago • 0 comments

Hello!

Thank you for the ESP32 BLE Arduino library! Huge help! But I have run in to an issue that I can't not seem to solve.

I created a header and CPP files to serve as a wrapper for your library. In my setup() function, I start a freeRTOS task, and before that forever loop, I declare and initialize my BLE wrapper class, then create one server, one service, and one characteristics. Then in my forever loop, I have conditional to handle connecting, disconnecting, and advertising.

The problem is I would like to keep my header and CPP wrapper files compliant with good coding practices, namely the Open-Closed principle, so that those files never need to be changed, and instead all modifications take place in the freeRTOS task.

More than a fix, because I am able to modify my code enough to get only one service, but breaks the Open-Closed principle. I'd love to know why I am seeing two services.

Main sketch:

#include <freertos/stream_buffer.h>
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include "BLE.h"
#include "ServerCallbacks.h"
#include "CharCallbacks.h"

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


TaskHandle_t ble_handle;

TickType_t lastReceviedTickCount;
StreamBufferHandle_t rxStreamBuffer;
const size_t xStreamBufferSizeBytes = 100, xTriggerLevel = 10;

void setup() {
  Serial.begin(115200);
  Serial.println("Starting BLE work!");


  rxStreamBuffer = xStreamBufferCreate( xStreamBufferSizeBytes, xTriggerLevel );

  xTaskCreatePinnedToCore(ble_task,
                          "BLE Task",
                          1024 * 5,
                          NULL,
                          20,
                          &ble_handle,
                          1);

  
}

void loop() {
  // put your main code here, to run repeatedly:
  char rxData[ 100 ];
  size_t xReceivedBytes;
  const TickType_t xBlockTime = pdMS_TO_TICKS( 20 );
  
  // Receive up to another sizeof( ucRxData ) bytes from the stream buffer.
  // Wait in the Blocked state (so not using any CPU processing time) for a
  // maximum of 100ms for the full sizeof( ucRxData ) number of bytes to be
  // available.
  xReceivedBytes = xStreamBufferReceive( rxStreamBuffer,
                                        ( void * ) rxData,
                                        50,
                                        xBlockTime );
  
  if( xReceivedBytes > 0 )
  {
    char incoming[xReceivedBytes+1];
    for(int i = 0; i < xReceivedBytes; i++) {
      incoming[i] = rxData[i];
    }
    incoming[xReceivedBytes] = '\0';
    std::string rx_message = incoming;
    Serial.print("rx_message:");
    Serial.println(rx_message.c_str());
  }
  vTaskDelay( 100 / portTICK_PERIOD_MS );
}

BLE_task

void ble_task(void * parameters) {
  Serial.println("Task: ble_task");
  
  BLE ble = BLE();
  ble.init("Noon");

  ble.getServer()->setCallbacks(new ServerCallbacks(ble.getDeviceConnected()));
  
  BLEService* pService = ble.getServer()->createService(SERVICE_UUID);
  BLECharacteristic* pCharacteristic = pService->createCharacteristic(
                                         CHARACTERISTIC_UUID,
                                         BLECharacteristic::PROPERTY_READ |
                                         BLECharacteristic::PROPERTY_WRITE
                                       );
  pCharacteristic->setCallbacks(new CharCallbacks(&lastReceviedTickCount, &rxStreamBuffer));
  
  //pCharacteristic->setValue("Hello World says Neil");
  pService->start();
  
  BLEAdvertising* pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->setScanResponse(true);
  pAdvertising->setMinPreferred(0x06);  // functions that help with iPhone connections issue
  pAdvertising->setMinPreferred(0x12);
  BLEDevice::startAdvertising();
  
  for( ;; ) {
    // disconnecting
    if (!ble.isDeviceConnected() && ble.isOldDeviceConnected()) {
        delay(500); // give the bluetooth stack the chance to get things ready
        
        Serial.println("Device disconnected!");
        BLEDevice::startAdvertising();
        
        Serial.println("start advertising");
        
        ble.setOldDeviceConnected(ble.isDeviceConnected());
        //oldDeviceConnected = deviceConnected;
    }
    
    // connecting
    if (ble.isDeviceConnected() && !ble.isOldDeviceConnected()) {
      // do stuff here on connecting
      Serial.println("Device connected!");
      ble.setOldDeviceConnected(ble.isDeviceConnected());
      //oldDeviceConnected = deviceConnected;
    }
    vTaskDelay( 100 / portTICK_PERIOD_MS );
  }
}

BLE.h/BLE.cpp:

#ifndef BLE_H_
#define BLE_H_

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

#define SERVICE_UUID        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"

class BLE {
  public:
    BLE();
    ~BLE();
    void init(std::string name);
    std::string getName();
    void setName(std::string n);
    BLEServer* getServer();
    void setServer(BLEServer* s);

    bool* getDeviceConnected();
    bool isDeviceConnected();
    void setDeviceConnected(bool c);

    bool* getOldDeviceConnected();
    bool isOldDeviceConnected();
    void setOldDeviceConnected(bool c);

    
  private:
    std::string bleName;
    BLEServer* pServer;
    bool deviceConnected;
    bool oldDeviceConnected;
};

#endif

BLE::BLE() {
  deviceConnected = false;
  oldDeviceConnected = false;
}

BLE::~BLE() {
  
}

void BLE::init(std::string name) {
  setName(name);
  BLEDevice::init(getName());
  setServer(BLEDevice::createServer());  //BLEServer* pServer = BLEDevice::createServer();
}

std::string BLE::getName() {
  return bleName;
}
void BLE::setName(std::string n) {
  bleName = n;
}

BLEServer* BLE::getServer() {
  return pServer;
}

void BLE::setServer(BLEServer* s) {
  pServer = s;
}



bool* BLE::getDeviceConnected() {
  return &deviceConnected;
}
bool BLE::isDeviceConnected() {
  return deviceConnected;
}
void BLE::setDeviceConnected(bool c) {
  deviceConnected = c;
}

bool* BLE::getOldDeviceConnected() {
  return &oldDeviceConnected;
}
bool BLE::isOldDeviceConnected() {
  return oldDeviceConnected;
}
void BLE::setOldDeviceConnected(bool c) {
  oldDeviceConnected = c;
}

ServerCallbacks.h/ServerCallbacks.cpp:

#ifndef SERVERCALLBACKS_H_
#define SERVERCALLBACKS_H_

#include <BLEServer.h>

class ServerCallbacks: public BLEServerCallbacks {
  public:
    ServerCallbacks(bool* connnectionStatus);
    virtual ~ServerCallbacks();
    void onConnect(BLEServer* pServer);
    void onDisconnect(BLEServer* pServer);

  private:
    bool *bConnected;  
};

#endif
ServerCallbacks::ServerCallbacks(bool* connnectionStatus) {
  bConnected = connnectionStatus;
}
ServerCallbacks::~ServerCallbacks() {
  
}
void ServerCallbacks::onConnect(BLEServer* pServer) {
  *bConnected = true;
}

void ServerCallbacks::onDisconnect(BLEServer* pServer) {
  *bConnected = false;
}

CharCallbacks.h/CharCallbacks.cpp:

#ifndef CHARCALLBACKS_H_
#define CHARCALLBACKS_H_

#include "Arduino.h"
#include <freertos/stream_buffer.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>

class CharCallbacks: public BLECharacteristicCallbacks {
  public:
    CharCallbacks(TickType_t* lastReceivedTick, StreamBufferHandle_t* streamBuffer);
    virtual ~CharCallbacks();
    void onWrite(BLECharacteristic *pCharacteristic);
    void onRead(BLECharacteristic* pCharacteristic);

    StreamBufferHandle_t* getStreamBuffer();
    void setStreamBuffer(StreamBufferHandle_t* buf);
  private:
    TickType_t* lastReceviedTickCount;
    StreamBufferHandle_t* rxStreamBuffer;
    size_t xBytesSent;
    TickType_t x100ms;
};

#endif
CharCallbacks::CharCallbacks(TickType_t* lastReceivedTick, StreamBufferHandle_t* streamBuffer) {
  lastReceviedTickCount = lastReceivedTick;
  x100ms = pdMS_TO_TICKS( 100 );
  rxStreamBuffer = streamBuffer;
}

CharCallbacks::~CharCallbacks() {
  
}

void CharCallbacks::onWrite(BLECharacteristic *pCharacteristic) {
  Serial.println("BLE received data!");
  
  std::string rxValue = pCharacteristic->getValue();

  // Send the string to the stream buffer.  Return immediately if there is not
  // enough space in the buffer.
  Serial.print("Sending to BLE rx stream buffer: ");Serial.println(rxValue.c_str());
  xBytesSent = xStreamBufferSend( *rxStreamBuffer, ( void * ) rxValue.c_str(), strlen( rxValue.c_str() ), 0 );
  
  if( xBytesSent != strlen( rxValue.c_str() ) )
  {
     // The entire string could not be added to the stream buffer because
     // there was not enough free space in the buffer, but xBytesSent bytes
     // were sent.  Could try again to send the remaining bytes.
     Serial.println("The entire string could not be added to the stream buffer.");
  }
}

void CharCallbacks::onRead(BLECharacteristic* pCharacteristic) {
}


StreamBufferHandle_t* CharCallbacks::getStreamBuffer() {
  return rxStreamBuffer;
}
void CharCallbacks::setStreamBuffer(StreamBufferHandle_t* buf) {
  rxStreamBuffer = buf;
}

Ideally, the BLE task would handle all the application specific settings like BLE name, services and their characteristics, connection, disconnection, and advertising. And then a BLE message comes in, it sends it to a Stream Buffer for my application logic to receive and process. (This whole thing came about when I was dropping messages because I was busy processing the previous one)

Thank you in advance and please know I have gotten a single service to work, but any, I mean any, modification creates a random second service.

gamadan avatar Mar 01 '23 20:03 gamadan