BLE limited to one characteristic?

I have a simple BLE example running on an ESP32. It has a single characteristic and appears to work fine. It shows up in nRFconnect and I can communicate with it on my Android app. However, when I add a 2nd charateristic, it doesn't appear to be advertised. No errors, either in the client or server, and I can still access the 1st characteristic OK.

It looks like it's a resource limitation but I can't see what to change. Any ideas?

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <BLE2902.h>  // Include this for BLE2902 (notifications)

...

  BLEDevice::init("ESP32-c3-zero test");
  
  pServer = BLEDevice::createServer ();
  
  pServer->setCallbacks (new MyServerCallbacks ());  // Set the disconnect callback
  
  pService          = pServer->createService (SERVICE_UUID);

  pCharacteristic_1 = pService->createCharacteristic (CHARACTERISTIC_UUID_1,
                                                      BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE);
  pCharacteristic_1->setValue("Characteristic output: Hello world");
  pCharacteristic_1->setCallbacks (new MyCallbacks ());

  pCharacteristic_2 = pService->createCharacteristic (CHARACTERISTIC_UUID_2,
                                                      BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE);
  pCharacteristic_2->setValue("Initial value for Characteristic 2");
  pCharacteristic_2->setCallbacks (new MyCallbacks ());

  Serial.println("Starting service");

  pService->start();


  pAdvertising = BLEDevice::getAdvertising ();
  pAdvertising->addServiceUUID (SERVICE_UUID);
  pAdvertising->setScanResponse (true);  // true => the ESP32 responds to scan requests with extra data (e.g., device name, more service UUIDs).
  pAdvertising->setMinPreferred (0x06);  // Sets the preferred connection interval (in BLE units, where 1 unit = 1.25ms).
  pAdvertising->setMinPreferred (0x12);  // These values influence how often the device communicates with a connected client.

  delay(100); // 

  BLEDevice::startAdvertising();  // Start advertising
  // pServer->getAdvertising()->start(); is this a better alternative?

  Serial.println("Characteristic defined! Now you can read it in your phone!");

Please post complete code which compiles and can be tested by others. It's certainly possible to have more than one characteristic in a sketch.

Simplified complete example.

Characteristic 1 shows on nRFconnect but not characteristic 2. If you swap the order of

 pCharacteristic_1 = pService->createCharacteristic (CHARACTERISTIC_UUID_1, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE);
  pCharacteristic_1->setValue("Characteristic #1 output");
  pCharacteristic_1->setCallbacks (new MyCallbacks ());

and

pCharacteristic_2 = pService->createCharacteristic (CHARACTERISTIC_UUID_2, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE);
  pCharacteristic_2->setValue("Characteristic #2 output");
  pCharacteristic_2->setCallbacks (new MyCallbacks ());

then characteristic 2 is there but not characteristic 1.

#include <Arduino.h>

#include <FastLED.h>

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <BLE2902.h>  // Include this for BLE2902 (notifications)

const uint8_t DATA_PIN = 10;
const uint8_t NUM_LEDS = 1;
CRGB leds[NUM_LEDS];

const char* SERVICE_UUID        = "cfc41eac-8a60-43f3-9ef5-dfb3c70d1c07";

const char* CHARACTERISTIC_UUID_1 = "a795b595-dc62-4550-b091-0a2d302c097f";  // For write commands (LED colour)
const char* CHARACTERISTIC_UUID_2 = "2baa2d64-1928-4a1b-8a67-4d91a873d56d";  // For notifications

String receivedData = "";

int notification_count = 1;

class MyCallbacks: public BLECharacteristicCallbacks
{
  void onWrite(BLECharacteristic *pCharacteristic)
  {
    receivedData = pCharacteristic->getValue().c_str();
    Serial.print("Received data: ");
    Serial.println(receivedData);
  
    if (receivedData == "red")
    {
      leds[0] = CRGB::Red;
    }
    else if (receivedData == "green")
    {
      leds[0] = CRGB::Green;
    }
   
    else
    {
      leds[0] = CRGB::Black;
    }
  
    FastLED.show();
  }
    
};

class MyServerCallbacks : public BLEServerCallbacks
{
  void onConnect(BLEServer* pServer)
  {
    Serial.println("Client connected");
  }

  void onDisconnect(BLEServer* pServer)
  {
    Serial.println("Client disconnected, restarting advertising...");
    delay(500);  // Short delay to allow clean disconnection
    BLEDevice::startAdvertising();  // Restart advertising
    
    leds[0] = CRGB (0x101010); //CRGB::Grey;
    FastLED.show();
  }
};


class Bluetooth
{
public:

  BLEServer* pServer = nullptr;
  BLEService* pService = nullptr;
  BLECharacteristic* pCharacteristic_1 = nullptr;
  BLECharacteristic* pCharacteristic_2 = nullptr;

  BLE2902* pDescriptorNotify = nullptr;

  BLEAdvertising* pAdvertising = nullptr;

public:

  Bluetooth () = default;

  void Init ();
};

void Bluetooth::Init ()
{
  BLEDevice::init("ESP32-c3-zero test");
  
  pServer = BLEDevice::createServer ();
  
  pServer->setCallbacks (new MyServerCallbacks ());  // Set the disconnect callback
  
  pService          = pServer->createService (SERVICE_UUID);

  pCharacteristic_1 = pService->createCharacteristic (CHARACTERISTIC_UUID_1, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE);
  pCharacteristic_1->setValue("Characteristic #1 output");
  pCharacteristic_1->setCallbacks (new MyCallbacks ());

  pCharacteristic_2 = pService->createCharacteristic (CHARACTERISTIC_UUID_2, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE);
  pCharacteristic_2->setValue("Characteristic #2 output");
  pCharacteristic_2->setCallbacks (new MyCallbacks ());

  Serial.println("Starting service");

  pService->start();

  pAdvertising = BLEDevice::getAdvertising ();
  pAdvertising->addServiceUUID (SERVICE_UUID);
  pAdvertising->setScanResponse (true);  // true => the ESP32 responds to scan requests with extra data (e.g., device name, more service UUIDs).
  pAdvertising->setMinPreferred (0x06);  // Sets the preferred connection interval (in BLE units, where 1 unit = 1.25ms).
  pAdvertising->setMinPreferred (0x12);  // These values influence how often the device communicates with a connected client.

  delay(100); // Possible race for startAdvertising

  BLEDevice::startAdvertising();

  Serial.println("Characteristic defined! Now you can read it in your phone!");
}

Bluetooth bluetooth;

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

  Serial.println("setup()");

  FastLED.addLeds <WS2812, DATA_PIN, GRB>(leds, NUM_LEDS);
  leds[0] = CRGB::Grey;
  FastLED.show();
  delay(200);

  Serial.println("Starting BLE work!");

  bluetooth.Init ();
}

void loop ()
{
  delay(2000);
}

(Note: needs FastLED v3.9.13. Some earlier versions don't compile)

Thanks for the heads up on FastLed.

I can not confirm your findings. I can read both characteristics in nrfConnect. LightBlue reads both as well. Android phone with v14.

Do I misunderstand your issue?

Thanks for trying it.

I think the problem might be in nRFconnect. Even though it claims to be discovering services and characteristics, it actually has some info cached. It's very inconsistent though. It has a 'bonded' state which appears to imply caching is happening, but the effect appears to be that it remembers the number of characteristics but not the UUIDs. I'm still working out the details but a step forward at least, so many thanks for your help.

Restart your phone to clear old ble cached stuff.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.