Problema con writeValue() ESP32 BLE

buongiorno, chiedo supporto per un problema a cui sto dietro da un sacco senza trovare una soluzione.
sto scrivendo un programma da eseguire su una scheda esp32 che controlli un anello led. tramite l'app nRF Connect ho 'sniffato' il traffico tra l'app per controllare il led, e il led e ho scoperto che per cambiare colore viene mandato questo byte: 0x7b 0xff 0x07 R G B 0x00 0xff 0xbf, dove r,g e b sono chiaramente i valori rgb; per esempio, quindi, per cambiare il colore in bianco il byte mandato sarebbe questo: 0x7b 0xff 0x07 0xff 0xff 0xff 0x00 0xff 0xbf (ff = 255 in esadecimale).

lo scopo del programma sarebbe di cambiare rapidamente colore tramite un interruttore di cui però non sono ancora a disposizione, quindi sto scrivendo il codice in una versione "test".

questo programma dovrebbe svolgere sia funzione di client, che server BLE: la funzione 'client' è quella precedentemente descritta che si collega al led, mentre quella 'server' ha lo scopo di creare una connessione con il telefono, in modo tale che scrivendo sulla sua caratteristica un valore rgb, si potrà scegliere il colore nel momento in cui l'interruttore non è in posizione "bianco".

il problema è che nel momento in cui chiamo il metodo caratteristicaRemota->writeValue(), il programma crasha e la scheda si riavvia, con questo errore nel monitor seriale: Guru Meditation Error: Core 1 panic'ed (LoadProhibited). Exception was unhandled.

questo, però, succede solo se il byte viene costruito utilizzando per i valori r, g e b delle variabili; mentre se le inserisco manualmente nel codice non si presenta nessun problema.

ecco il codice:

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

#define serverName "esp32-server"

BLEClient* bleClient;
BLERemoteService* servizioRemoto;
BLERemoteCharacteristic* caratteristicaRemota;

#define SERVIZIO_UUID "ffe0"
#define COLORE_UUID "ffe1"
#define ANIMAZIONE_UUID "ffe2"
#define INDIRIZZO_MAC_LED "CF:FF:F3:0F:00:71"
bool deviceConnected = false;
uint8_t rgb[3];
int i = 0;
int animazione;
bool coloreAggiornato = false;
bool animazioneAggiornata = false; 

void setColor(int red, int green, int blue) {

  String redHexString = String(red, HEX);
  Serial.println(redHexString);

  String greenHexString = String(green, HEX);
  Serial.println(greenHexString);

  String blueHexString = String(blue, HEX);
  Serial.println(blueHexString);

  char redHexChar[10];
  redHexString.toCharArray(redHexChar, 10);
  Serial.println(redHexChar);
  
  char greenHexChar[10];
  greenHexString.toCharArray(greenHexChar, 10);
  Serial.println(greenHexChar);

  char blueHexChar[10];
  blueHexString.toCharArray(blueHexChar, 10);
  Serial.println(blueHexChar);

  uint8_t redHex = strtol(redHexChar, NULL, 16);
  Serial.println(redHex);
  uint8_t greenHex = strtol(greenHexChar, NULL, 16);
  Serial.println(greenHex);
  uint8_t blueHex = strtol(blueHexChar, NULL, 16);
  Serial.println(blueHex);

  uint8_t data[9];

  data[0] = 0x7b;
  data[1] = 0xff;
  data[2] = 0x07;
  data[3] = 0XFF;
  data[4] = 0XFF;
  data[5] = 0XFF;
  data[6] = 0x00;
  data[7] = 0xff;
  data[8] = 0xbf;

  caratteristicaRemota->writeValue(data, sizeof(data));
}

class CaratteristicheCallback: public BLECharacteristicCallbacks {
  
  void onWrite(BLECharacteristic *pCharacteristic) {
    std::string uuid_intero = pCharacteristic->getUUID().toString(); // UUID completo
    std::string uuid_abbreviato = uuid_intero.substr(4, 4); // estrae "ffe1" da x esempio "0000ffe1-0000-1000-8000-00805f9b34fb"
    std::string value = pCharacteristic->getValue();

    if(uuid_abbreviato == COLORE_UUID) {
     // --- cosa fare in caso di scrittura nella caratteristica del colore ---
      Serial.println("[ server | ~ ] dati ricevuti su caratteristica colore");

      // --- check decimale ---
      bool valido = true;        
      for(char c: value) {
        if(!isdigit(c)) {
          valido = false;
          break;
        }
      } 

      // --- check lunghezza (3 cifre x 3)
      if(valido && value.length() == 9) {        
        // --- nel caso in cui il valore rispetta tutti i "requisiti" suddividere in tre variabili r,g,b e stampare nel monitor seriale ---    
        rgb[0] = atoi(value.substr(0, 3).c_str());
        rgb[1] = atoi(value.substr(3, 3).c_str());
        rgb[2] = atoi(value.substr(6, 3).c_str());
        
        if(rgb[0]<=255 && rgb[1]<=255 && rgb[2]<=255) {
          coloreAggiornato = true; 
        }        
      } else { 
        Serial.println("[ server | ! ] il formato dei dati inseriti non è corretto"); 
      };
    } else if (uuid_abbreviato == ANIMAZIONE_UUID) {
      // --- cosa fare in caso di scirttura nella caratteristica dell'animazione ---
      if(value.length() == 1) {
        Serial.println("[ server | ~ ] dati ricevuti su caratteristica animazione");
        animazioneAggiornata = true;
      } else { Serial.println("[ server | ! ] il formato dei dati inseriti non è corretto"); };
    }
  }
};

// --- gestione eventi server ---
class ServerCallback : public BLEServerCallbacks {
  // --- evento di connessione ---
  void onConnect(BLEServer *pServer) {
    deviceConnected = true;
    Serial.println("[ server | + ] un client si è connesso!");
  };

  // --- evento di disconessione ---
  void onDisconnect(BLEServer *pServer) {
    deviceConnected = false;
    Serial.println("[ server | - ] un client si è disconnesso");
  }
};

void setup() {
  // --- inizializzare il monitor seriale ---
  Serial.begin(115200);

  // --- dual role ---


  // --- inizializzare server BLE e assegnare callback per gestire connessioni, disconnessioni e traffico dati ---
  BLEDevice::init(serverName);

  xTaskCreatePinnedToCore(
    client,
    "client",
    5000,
    NULL,
    0,
    NULL,
    0
  );

  BLEServer *pServer = BLEDevice::createServer();
  pServer->setCallbacks(new ServerCallback());

  // --- creazione servizio ---
  BLEService *testService = pServer->createService(SERVIZIO_UUID);

  BLECharacteristic *caratteristicaColore = testService->createCharacteristic(COLORE_UUID, BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_READ);
  BLECharacteristic *caratteristicaAnimazione = testService->createCharacteristic(ANIMAZIONE_UUID, BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_READ);

  caratteristicaColore->setCallbacks(new CaratteristicheCallback());
  caratteristicaAnimazione->setCallbacks(new CaratteristicheCallback());

  // --- inizializzazione servizio ---
  testService->start();

  // --- inizializzazione advertising (comparsa sulle liste bluetooth) ---
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVIZIO_UUID);
  pAdvertising->setScanResponse(true);
  pAdvertising->setMinPreferred(0x06);  // functions that help with iPhone connections issue
  pAdvertising->setMinPreferred(0x12);
  BLEDevice::startAdvertising();
  Serial.println("[ server | ... ] attendo connesioni");
}

void client(void *pvParameters) {
  BLEAddress indirizzoLed(INDIRIZZO_MAC_LED);

  // --- connessione all'angel eye ---
  bleClient = BLEDevice::createClient();
  // BLERemoteService *servizioRemoto = client->getService(BLEUUID("ffe0"));
  // BLERemoteCharacteristic *caratteristicaRemota = servizioRemoto->getCharacteristic(BLEUUID("ffe1"));


  // --- verificare connessione ---
  if (bleClient->connect(indirizzoLed)) {
    Serial.println("[ client | ~ ] connessione a angel eye effetuata!");

    servizioRemoto = bleClient->getService(BLEUUID("ffe0"));
    // --- creazione e gestione servizio remoto ---
    if (servizioRemoto == nullptr) {
      Serial.println("[ client | ! ] è stato impossibile trovare il servizio con UUID=ffe0");
    } else {
      Serial.println("[ client | ~ ] servizio con UUID=ffe0 trovato");
    }

    caratteristicaRemota = servizioRemoto->getCharacteristic(BLEUUID("ffe1"));
    // --- creazione e gestione caratteristica remota ---
    if (caratteristicaRemota == nullptr) {
      Serial.println("[ client | ! ] è stato impossibile trovare la caratteristica con UUID=ffe1");
    } else {
      Serial.println("[ client | ~ ] caratteristica con UUID=ffe1 trovato");
    }

  } else {
    Serial.println("[ client | ! ] connessione a angel eye fallita!");
  }

  

  // --- deallocare la memoria dedicata al client per evitare leak ---
  delete bleClient;
  // --- termina il task parallelo ---
  vTaskDelete(NULL);
}



void loop() {
if (coloreAggiornato) {
  Serial.print("rgb[0]: ");
  Serial.println(rgb[0]);
  Serial.print("rgb[1]: ");
  Serial.println(rgb[1]);
  Serial.print("rgb[2]: ");
  Serial.println(rgb[2]);

  String rgbString = String(rgb[0]) + String(rgb[1]) + String(rgb[2]);
  const char* utf8Data = rgbString.c_str();
  uint8_t data[] = {123, 255, 7, utf8Data[0], utf8Data[1], utf8Data[2], 0, 255, 191};
  Serial.println(sizeof(data));
  Serial.println(sizeof(rgb));

  for (int i = 0; i < 9; i++) {
      Serial.println(data[i]);
  }

  caratteristicaRemota->writeValue(data, 9);
  coloreAggiornato = false;
}
  if(animazioneAggiornata) {
    Serial.println("animazione aggiornata");
    animazioneAggiornata = false;
  }
}

scusatemi se mi sono dilungato, e se non mi sono spiegato bene,
grazie in anticipo

Buonasera e benvenuto nella sezione Italiana del forum,

cortesemente, come prima cosa, leggi attentamente il REGOLAMENTO di detta sezione, (... e, per evitare future possibili discussioni/incomprensioni, prestando molta attenzione al punto 15), dopo di che, come da suddetto regolamento (punto 16.7), fai la tua presentazione NELL'APPOSITA DISCUSSIONE (... quello che vedi in blu è un link, fai click su di esso per raggiungere la discussione) spiegando bene quali esperienze hai in elettronica e programmazione, affinché noi possiamo conoscere la tua esperienza ed esprimerci con termini adeguati.

Grazie,

Guglielmo

P.S.: Ti ricordo che, purtroppo, fino a quando non sarà fatta la presentazione nell’apposita discussione, nel rispetto del succitato regolamento nessuno ti risponderà (eventuali risposte o tuoi ulteriori post, verrebbero temporaneamente nascosti), quindi ti consiglio di farla al più presto. :wink:

grazie di avermelo ricordato, ho appena mandato la presentazione

Secondo me cerchi di scrivere la caratteristica remota, ma il device non è connesso.
Non mi è chiaro poi perché usi un task per la connessione che però muore nel setup e non fa altro visto che all'interno del task non c'è alcun loop.

Io credo che tu debba rivedere tutto il meccanismo di connessione "client", anche usare il MAC address per la connessione non è una buona idea. Cosa succede se cambi il dispositivo? Devi ricaricare tutto il firmware aggiornando il MAC!
Prendi a riferimento l'esempio incluso nel core BLE_client.ino.

il dispositivo sono sicuro che si connetta, sia perché viene annunciato nel monitor seriale anche dicendo di aver trovato caratteristica e servizio remoto corrispondenti al loro uuid, sia perché avevo provato a mettere il byte composto manualmente prima di quello con le variabili e funzionava perfettamente.

per quanto riguarda l’indirizzo mac non penso possa essere un problema perché sono sicuro che il dispositivo sarà sempre quello

il task l’ho creato perché avevo paura che il client andasse in conflitto con il server , ma è la prima volta che mi capita di usarlo quindi è plausibile che non sia necessario o abbia sbagliato.. guarderò l’esempio che hai linkato, grazie

Il dispositivo si connette nel setup(), ma poi il task viene chiuso e con tutta probabilità perdi la connessione.

chiudo la task perché incorrevo in un altro problema altrimenti, penso un memory leak dato che in loop si riavviava e dava l’errore memory overload.. poi non so, è l’unico modo che ho trovato per risolverlo. dici che conviene togliere completamente la task parallela?

Non è questione di task, puoi anche lasciare il codice li solo che devi partire dal concetto che la connessione BLE la puoi perdere in qualsiasi momento e quindi va gestita.

Il principio di funzionamento del BLE è quello rappresentato in questa immagine

image

Il tuo ESP32 dovrà svolgere entrambi i ruoli, ma quello con cui hai problemi al momento è quello di destra.
Tu assumi che il device ci sia e che sia connesso sempre, ma siccome hai chiuso il task cancellando dalla memoria il riferimento al "client" con l'istruzione

delete bleClient;

la caratteristica che vuoi scrivere rimane orfana e non sa a cosa fare riferimento e da qui nasce il core panic.

Se vuoi mantenere l'utilizzo del task, che mi sembra anche una buona cosa, devi fare in modo che il task rimanga attivo gestendo correttamente il meccanismo BLE di advertising/scanning per instaurare la connessione e mantenerla attiva come mostrato nell'esempio di cui ti ho messo il link.
Un modo terra terra potrebbe essere proprio quello di trasformare tutto l'esempio in questione in un task ricordando che la struttura tipica di un task FreeRTOS è la seguente:

void vTaskCode( void * pvParameters )
{
    // Istruzioni che vengono eseguite all'avvio del task
    // analogamente a quanto fa il setup() di Arduino

    for( ;; )
    {
       // Istruzioni che vengono eseguite in loop
    }
}

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