Abdelrahman
Abdelrahman

Reputation: 75

how to notify the client in BLE with more than 20 bytes and prevent nrf connect app from displaying value in hex or not showing value at all?

I made an Arduino code for esp32 that lets the user connect to esp32 via Bluetooth low energy and control an LED ws2812 display... everything is working fine except that the NRF connect app is not working in showing the value of the notify status and in one attempt to change the code I managed to get it to change the value but it was in hex and very short... on the other hand, other apps do end up displaying the value but they only display a small portion of it not all since I read that the notify cannot send more than 20 bytes and also these apps don't have the same functionalities as the NRF app anyways... so I tried implementing ways to split the data into packets or by increasing MTU size and it didn't work so here's the code without the failed attempts so that if anyone has a solution for the NRF app it becomes easy to figure it out even if there a way to send the status of the led display door sign through some other ways other than the notify if there's any... note: I am a beginner in esp32 and BLE, I code in Arduino IDE on windows, I use android for NRF app but I am also making this code to be compatible with IOS users since the user who will be using this has IOS:

#include <Adafruit_GFX.h>
#include <Adafruit_NeoMatrix.h>
#include <Adafruit_NeoPixel.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>

#ifndef PSTR
  #define PSTR
#endif

#define PIN 12

Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(32, 8, PIN,
  NEO_MATRIX_BOTTOM    + NEO_MATRIX_RIGHT +
  NEO_MATRIX_COLUMNS + NEO_MATRIX_ZIGZAG);

String msg = "";

int x_pos = matrix.width();
int y_size = matrix.height();
int msg_length = msg.length();
int x_size = matrix.width();
String received = "";
int duration = 70;
uint16_t textColor = matrix.Color(0,0,255); // Default color: blue
bool multiColor = false;

BLEServer* pServer = NULL;
BLECharacteristic* pCharacteristicTX = NULL;
BLECharacteristic* pCharacteristicRX = NULL;
bool deviceConnected = false;
bool oldDeviceConnected = false;
bool displayOn = true;

#define SERVICE_UUID        "8cdd366e-7eb4-442d-973f-61e2fd4b56f0"
#define CHARACTERISTIC_UUID_RX "dc994613-74f5-4c4f-b671-5a8d297f737a"
#define CHARACTERISTIC_UUID_TX "cc5e8e3a-f8e6-4889-8a18-9069272be2a5"

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

    void onDisconnect(BLEServer* pServer) {
      deviceConnected = false;
    }
};


void sendDisplayStatus() {
    // Extract RGB values from textColor
  uint8_t red = (textColor >> 11) & 0x1F;
  uint8_t green = (textColor >> 5) & 0x3F;
  uint8_t blue = textColor & 0x1F;

  // Scale the values to 8-bit (0-255) range
  red = map(red, 0, 31, 0, 255);
  green = map(green, 0, 63, 0, 255);
  blue = map(blue, 0, 31, 0, 255);
  String status = "Message: " + msg + "\n" +
    "Color: " + String(red) + "," + String(green) + "," + String(blue) + "\n" +
    "Speed: " + String(duration) + "\n" +
    "Brightness: " + String(matrix.getBrightness()) + "\n" +
    "Display On: " + String(displayOn ? "Yes" : "No") + "\n" +
    "Multicolor: " + String(multiColor ? "On" : "Off") + "\n";

  // Debug print to verify the status string
  Serial.println("Sending status:");
  Serial.println(status);

  // Convert the status string to a C-style string
  const char* statusCharArray = status.c_str();

  // Set the value and notify
  pCharacteristicTX->setValue((uint8_t*)statusCharArray, strlen(statusCharArray));
  pCharacteristicTX->notify();
  
}
class MyCallbacks: public BLECharacteristicCallbacks {
    void onWrite(BLECharacteristic* pCharacteristic) {
      std::string rxValue = pCharacteristic->getValue(); 
      if (rxValue.length() > 0) {
        Serial.println("===start=receive===");
        Serial.println("Received value: " + String(rxValue.c_str()));
        received = String(rxValue.c_str());
      }
      // Check if the received value is a speed command
      if (received.startsWith("speed:")) {
        int newDuration = received.substring(6).toInt();
        if (newDuration > 0 && newDuration <= 1000) {
          duration = newDuration;
          Serial.println("Set duration to: " + String(duration));
        }
      }
      // Check if the received value is a brightness command
      else if (received.startsWith("brightness:")) {
        int brightness = received.substring(11).toInt();
        if (brightness >= 0 && brightness <= 255) {
          matrix.setBrightness(brightness);
          Serial.println("Set brightness to: " + String(brightness));
        }
      }
      // Check if the received value is a color command
      else if (received.startsWith("color:")) {
        int r = received.substring(6, 9).toInt();
        int g = received.substring(10, 13).toInt();
        int b = received.substring(14, 17).toInt();
        textColor = matrix.Color(r, g, b);
        Serial.println("Set text color to: " + String(r) + ", " + String(g) + ", " + String(b));
      } 
      // Check if the received value is a multicolor command
      else if (received == "multicolor:on") {
        multiColor = true;
        Serial.println("Multicolor text enabled");
      }
      else if (received == "multicolor:off") {
        multiColor = false;
        Serial.println("Multicolor text disabled");
      }   
      // Check if the received value is "off" or "on"
      else if (received == "off") {
          displayOn = false;
          matrix.fillScreen(0); // Turn off all LEDs
          matrix.show();
          Serial.println("Display turned off");
      }
      else if (received == "on") {
          displayOn = true;
          Serial.println("Display turned on");
      }
      else if (received == "reset") {
        ESP.restart();
      }
      else {
        msg = String(rxValue.c_str());
        x_pos = matrix.width();
      }
      // Send notification with current display status
      sendDisplayStatus();
    }
};

// arrays holding color config data for scrolling text and for graphics drawings
const uint16_t textColors[] = { matrix.Color(255, 0, 0), matrix.Color(0, 255, 0), matrix.Color(0, 0, 255), matrix.Color(255,255,0), matrix.Color(137,112,219)};

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

  // Create the BLE Device
  BLEDevice::init("ESP32");

  // 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
  pCharacteristicTX = pService->createCharacteristic(
    CHARACTERISTIC_UUID_TX,
    BLECharacteristic::PROPERTY_NOTIFY
  );
  BLE2902 *pBLE2902;
  pBLE2902 = new BLE2902();
  pBLE2902->setNotifications(true);
  pCharacteristicTX->addDescriptor(new BLE2902());

  // Create a BLE Characteristic for RX
  pCharacteristicRX = pService->createCharacteristic(
    CHARACTERISTIC_UUID_RX,
    BLECharacteristic::PROPERTY_WRITE 
  );
  pCharacteristicRX->setCallbacks(new MyCallbacks());

  pService->start();
  // Start advertising
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->setScanResponse(false);
  pAdvertising->setMinPreferred(0x0);  // set value to 0x00 to not advertise this parameter
  BLEDevice::startAdvertising();
  Serial.println("Waiting a client connection to notify...");
  // Request MTU size
  pServer->getAdvertising()->setMinPreferred(0x12);  // Functions that help with iPhone connections issue
  BLEDevice::setMTU(517);  // Request MTU size of 517 bytes

  matrix.begin();
  matrix.setTextWrap(false);
  matrix.setBrightness(40);
  matrix.setTextColor(textColors[0]);
  Serial.println("NeoMatrix pixel grid test");
  Serial.println((String)"Matrix Pixel Width x Height: " + x_size + " x " + y_size);
  
  // Display startup message
  msg = "Welcome";
}


void loop() {

      // notify changed value
    if (deviceConnected) {
        static String lastStatus = "";
        String currentStatus = "Message: " + msg + "\n" +
                               "Color: " + String((textColor >> 16) & 0xFF) + "," + String((textColor >> 8) & 0xFF) + "," + String(textColor & 0xFF) + "\n" +
                               "Speed: " + String(duration) + "\n" +
                               "Brightness: " + String(matrix.getBrightness()) + "\n" +
                               "Display On: " + String(displayOn ? "Yes" : "No") + "\n" +
                               "Multicolor: " + String(multiColor ? "On" : "Off") + "\n";
        if (currentStatus != lastStatus) {
            sendDisplayStatus();
            lastStatus = currentStatus;
        }
      delay(3); // 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;
    }
  
    if (displayOn) {
        displayMessage(msg, duration);
    }
}

void displayMessage(String message, int duration) {
  matrix.fillScreen(0);
  matrix.setCursor(x_pos, 0);
  msg = message;
  msg_length = msg.length();
    // Set text color based on message content
  if (msg == "Unavailable" || msg == "unavailable") {
    matrix.setTextColor(RED);
  } else if (msg == "Available" || msg == "available") {
    matrix.setTextColor(GREEN);
  } else {
    matrix.setTextColor(textColor);
  }
  matrix.print(msg);

  // if(--x_pos < -(msg_length * 6)) {
  //   x_pos = matrix.width();

  if (msg_length * 6 > x_size) { // Only scroll if message is wider than the screen
    for (int i = 0; i < msg_length; i++) {
      matrix.setCursor(x_pos + i * 6, 0);
      if (multiColor) {
        matrix.setTextColor(textColors[i % 5]);
      }
      matrix.print(msg[i]);
    }
    if (--x_pos < -(msg_length * 6)) {
      x_pos = matrix.width();
    }
  } else {
    x_pos = (x_size - msg_length * 6) / 2; // Center the text if it's shorter than the screen width
    for (int i = 0; i < msg_length; i++) {
      matrix.setCursor(x_pos + i * 6, 0);
      if (multiColor) {
        matrix.setTextColor(textColors[i % 3]);
      }
      matrix.print(msg[i]);
    }
  }
  matrix.show();
  delay(duration);
} 

Upvotes: 0

Views: 75

Answers (0)

Related Questions