ESP32 e HC-06: come accoppiare il dispositivo

Allora, come accennato ho preparato due semplici esempi.
Anche se non mi piace parlare di server/client quando si ha a che fare con il BLE, ho mantenuto i nomi già usati negli esempi inclusi nel core.

File Server.ino (che nel mondo BLE sarebbe un Peripheral).
Si tratta dell'esempio incluso con alcune piccole modifiche dove ho cambiato lo UUID usando quello del tuo codice.
Ogni 10 secondi notifica ai dispositivi connessi una stringa del tipo "Counter:xxx". Nel tuo caso dovrebbe essere il codice a barre letto, giusto?

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

#define SERVICE_UUID "0000ae30-0000-1000-8000-00805f9b34fb"         // Servizio personalizzato
#define CHARACTERISTIC_UUID "0000ae02-0000-1000-8000-00805f9b34fb"  // Prima caratteristica notificabile

BLECharacteristic *pCharacteristic;

int counter;
char message[32];
bool connected = false;

class MyServerCallbacks : public BLEServerCallbacks {
  void onConnect(BLEServer *pServer) {
    Serial.println("Device connected to central");
    BLEDevice::stopAdvertising();
    connected = true;
  };

  void onDisconnect(BLEServer *pServer) {
    Serial.println("Device disconneted from central");
    BLEDevice::startAdvertising();
    connected = false;
  }
};

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

  BLEDevice::init("ESP32 Peripeheral");
  BLEServer *pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());

  BLEService *pService = pServer->createService(SERVICE_UUID);
  pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
  pCharacteristic->addDescriptor(new BLE2902());
  pCharacteristic->setValue("Hello World!");
  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();
  Serial.println("Characteristic defined!");
}

void loop() {
  static uint32_t updateTime;
  if (millis() - updateTime >= 10000 && connected) {
    updateTime = millis();
    snprintf(message, sizeof(message), "Counter%d", counter++);
    pCharacteristic->setValue(message);
    pCharacteristic->notify();
    Serial.println(message);
  }
}

Il file Client.ino (BLE Central) invece è dove viene usato sia il BLE che il WiFi.
Si tratta di copia&incolla dell'esempio BLE + il codice proveniente dall'esempio della libreria HTTPClient.
Come dicevo prima, ho dovuto usare lo schema di partizione "Huge App" perchè BLE + WiFi richiedono parecchio spazio in flash.

Quando il BLE central riceve la notifica dal peripheral (per la caratteristicha che ovviamente ha sottoscritto in fase di connessione), la elabora e successivamente invia una richiesta HTTPS GET all'host remoto dove ho salvato uno script PHP semplice semplice (fatto al volo con ChatGPT :stuck_out_tongue_winking_eye:) che aggiorna un file di testo locale con il contenuto dela parametro "text" che viene passato con la richiesta stessa.
La stessa pagina PHP, richiamata con il browser senza passare nessun paramento visualizza una pagina HTML che mostra il contenuto del file aggiornato in modo asincrono usando javascript.

#include "BLEDevice.h"
#include <WiFi.h>
#include <WiFiMulti.h>
#include <HTTPClient.h>
#include <NetworkClientSecure.h>
#include "secrets.h"

WiFiMulti WiFiMulti;
const char *url = "https://foodphotographyitalia.it/temp/test.php?";


#define SERVICE_UUID "0000ae30-0000-1000-8000-00805f9b34fb"         // Servizio personalizzato
#define CHARACTERISTIC_UUID "0000ae02-0000-1000-8000-00805f9b34fb"  // Prima caratteristica notificabile

static BLEUUID serviceUUID(SERVICE_UUID);
static BLEUUID charUUID(CHARACTERISTIC_UUID);

static boolean doScan = false;
static boolean doConnect = false;
static boolean connected = false;
static BLERemoteCharacteristic *pRemoteCharacteristic;
static BLEAdvertisedDevice *myDevice;

///////////////////////////////////////////////////////  WIFI ///////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setupWiFi() {
  WiFi.mode(WIFI_STA);
  WiFiMulti.addAP(ssid, password);
  Serial.print("Waiting for WiFi to connect...");
  while ((WiFiMulti.run() != WL_CONNECTED)) {
    Serial.print(".");
    delay(500);
  }
  Serial.println("\nconnected!");
}

void updateRemoteData(const char* field, uint8_t * data, size_t length) {
  NetworkClientSecure *client = new NetworkClientSecure;
  if (client) {
    client->setInsecure();

    {  // Add a scoping block for HTTPClient https to make sure it is destroyed before NetworkClientSecure *client is
      HTTPClient https;
      char request[128];
      strcpy(request, url);
      strcat(request, field);
      strcat(request, "=");
      strncat(request, (char*)data, length);

      if (https.begin(*client, request)) {  // HTTPS
        Serial.print("[HTTPS] GET ");
        Serial.println(request);

        // start connection and send HTTP header
        int httpCode = https.GET();

        // httpCode will be negative on error
        if (httpCode > 0) {
          if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
            Serial.println("Counter updated");
          }
        } 
        else {
          Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str());
        }
        https.end();
      } 
      else {
        Serial.printf("[HTTPS] Unable to connect\n");
      }
    }  // End extra scoping block

    delete client;
  } 
  else {
    Serial.println("Unable to create client");
  }
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



//////////////////////////////////////////////////  BLE  ////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static void notifyCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify) {
  Serial.print("Notify callback for characteristic ");
  Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str());
  Serial.print(" of data length ");
  Serial.println(length);
  Serial.print("data: ");
  Serial.write(pData, length);
  Serial.println();

  updateRemoteData("text", pData, length);
}

class MyClientCallback : public BLEClientCallbacks {
  void onConnect(BLEClient *pclient) {
    BLEDevice::getScan()->stop();
  }

  void onDisconnect(BLEClient *pclient) {
    connected = false;
    Serial.println("onDisconnect");
    doScan = true;
  }
};

bool connectToServer() {
  Serial.print("Forming a connection to ");
  Serial.println(myDevice->getAddress().toString().c_str());

  BLEClient *pClient = BLEDevice::createClient();
  Serial.println(" - Created client");

  pClient->setClientCallbacks(new MyClientCallback());

  // Connect to the remove BLE Server.
  pClient->connect(myDevice);  // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private)
  Serial.println(" - Connected to server");
  pClient->setMTU(517);  //set client to request maximum MTU from server (default is 23 otherwise)

  // Obtain a reference to the service we are after in the remote BLE server.
  BLERemoteService *pRemoteService = pClient->getService(serviceUUID);
  if (pRemoteService == nullptr) {
    Serial.print("Failed to find our service UUID: ");
    Serial.println(serviceUUID.toString().c_str());
    pClient->disconnect();
    return false;
  }
  Serial.println(" - Found our service");

  // Obtain a reference to the characteristic in the service of the remote BLE server.
  pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
  if (pRemoteCharacteristic == nullptr) {
    Serial.print("Failed to find our characteristic UUID: ");
    Serial.println(charUUID.toString().c_str());
    pClient->disconnect();
    return false;
  }
  Serial.println(" - Found our characteristic");

  // Read the value of the characteristic.
  if (pRemoteCharacteristic->canRead()) {
    String value = pRemoteCharacteristic->readValue();
    Serial.print("The characteristic value was: ");
    Serial.println(value.c_str());
  }

  if (pRemoteCharacteristic->canNotify()) {
    pRemoteCharacteristic->registerForNotify(notifyCallback);
  }

  connected = true;
  return connected;
}
/**
 * Scan for BLE servers and find the first one that advertises the service we are looking for.
 */
class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks {
  /**
   * Called for each advertising BLE server.
   */
  void onResult(BLEAdvertisedDevice advertisedDevice) {
    Serial.print("BLE Advertised Device found: ");
    Serial.println(advertisedDevice.toString().c_str());

    // We have found a device, let us now see if it contains the service we are looking for.
    if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) {
      BLEDevice::getScan()->stop();
      myDevice = new BLEAdvertisedDevice(advertisedDevice);
      doConnect = true;
    }  // Found our server
  }    // onResult
};     // MyAdvertisedDeviceCallbacks

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



void setup() {
  Serial.begin(115200);
  Serial.println("\nStarting WiFi configuration...");
  setupWiFi();

  Serial.println("Starting Arduino BLE Client application...");
  BLEDevice::init("ESP32 Central");

  // Retrieve a Scanner and set the callback we want to use to be informed when we
  // have detected a new device.  Specify that we want active scanning and start the
  // scan to run for 5 seconds.
  Serial.println("Starting BLE Scan...");
  BLEScan *pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
  pBLEScan->setInterval(1349);
  pBLEScan->setWindow(449);
  pBLEScan->setActiveScan(true);
  pBLEScan->start(5, false);

}  // End of setup.

// This is the Arduino main loop function.
void loop() {

  if (doScan) {
    doScan = false;
    BLEDevice::getScan()->start(5, false);
  }

  // If the flag "doConnect" is true then we have scanned for and found the desired
  // BLE Server with which we wish to connect.  Now we connect to it.  Once we are
  // connected we set the connected flag to be true.
  if (doConnect == true) {
    if (connectToServer()) {
      Serial.println("We are now connected to the BLE Server.");
    } else {
      Serial.println("We have failed to connect to the server; there is nothing more we will do.");
    }
    doConnect = false;
  }
}  // End of loop

Per completezza aggiungo il sorgente dello script PHP.
Lascio i due dispositivi connessi per un po' cosi se ti connetti alla pagina in questione dovresti vedere il contatore che viene incrementato ogni 10 secondi.

<?php
// Nome del file in cui salvare il contenuto
$filename = 'data.txt';

// Controlla se il parametro 'text' è passato nella query string e salva il contenuto
if (isset($_GET['text'])) {
    $text = $_GET['text'];
    file_put_contents($filename, $text);
    exit; // Dopo aver scritto il testo, non servono ulteriori output
}

// Se è una richiesta AJAX, restituisce solo il contenuto del file
if (isset($_GET['fetch'])) {
    echo file_exists($filename) ? file_get_contents($filename) : "Nessun contenuto disponibile";
    exit; // Non serve più continuare
}

// Funzione per restituire l'HTML completo
function renderPage($content) {
    echo <<<HTML
<!DOCTYPE html>
<html lang="it">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Aggiorna Testo Asincrono</title>
    <script>
        // Funzione per fare una richiesta asincrona al server
        function fetchData() {
            // Crea una richiesta XMLHttpRequest
            const xhr = new XMLHttpRequest();
            
            // Imposta la richiesta per il file PHP (con parametro 'fetch' per ottenere solo il contenuto)
            xhr.open('GET', '{$_SERVER['PHP_SELF']}?fetch=1', true);
            
            // Definisci cosa fare quando la risposta è pronta
            xhr.onload = function() {
                if (xhr.status === 200) {
                    // Aggiorna il contenuto del div con il nuovo testo
                    document.getElementById('content').innerText = xhr.responseText;
                }
            };
            
            // Invia la richiesta
            xhr.send();
        }

        // Aggiorna il contenuto ogni 5 secondi
        setInterval(fetchData, 5000);

        // Chiama fetchData quando la pagina è caricata per la prima volta
        window.onload = fetchData;
    </script>
</head>
<body>
    <h1>Contenuto aggiornato</h1>
    <div id="content">{$content}</div>
</body>
</html>
HTML;
}

// Leggi il contenuto del file per la prima visualizzazione della pagina
$content = file_exists($filename) ? file_get_contents($filename) : "Nessun contenuto disponibile";

// Restituisci l'HTML completo
renderPage($content);

1 Like

ciao, scusa se non ho risposto prima, ma non sono stata a casa per provare.
Stavo riportando le tue modifiche sul mio script, ma mi sono fermata perchè in compilazione ottengo errore

Compilation error: expected type-specifier before 'MyAdvertisedDeviceCallbacks'

sulla riga

pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());

appena riesco, provo a sistemare

Metti il testo dell'errore completo perché non si capisce quale sia il problema di compilazione.

Eventualmente prova a fare il contrario, cioè partire dallo sketch funzionante e modificarlo in accordo alle tue esigenze.

aggiornamento: non ho lasciato perdere, ma purtroppo con il lavoro non ho avuto un secondo di tempo.
Spero la prossima settimana di poter provare e vi aggiorno sui risultati.

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