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
) 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