BLE su ESP32 in due ruoli
L'ESP32 può essere contemporaneamente:
- BLE Central (scanner): cerca dispositivi vicini, legge advertising, si connette come client
- BLE Peripheral (server): pubblica servizi e caratteristiche, accetta connessioni
Scenario 1: scanner di beacon
Sketch che elenca tutti i dispositivi BLE in raggio ogni 10 secondi (utile per presence detection in domotica):
#include <BLEDevice.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>
BLEScan* scanner;
class Listener: public BLEAdvertisedDeviceCallbacks {
void onResult(BLEAdvertisedDevice d) {
Serial.printf("MAC: %s | Nome: %s | RSSI: %d\n",
d.getAddress().toString().c_str(),
d.haveName() ? d.getName().c_str() : "-",
d.getRSSI());
}
};
void setup() {
Serial.begin(115200);
BLEDevice::init("");
scanner = BLEDevice::getScan();
scanner->setAdvertisedDeviceCallbacks(new Listener());
scanner->setActiveScan(true);
}
void loop() {
scanner->start(10, false); // 10 secondi
scanner->clearResults();
delay(1000);
}
Triangolazione tramite RSSI
L'RSSI (Received Signal Strength Indicator) indica grossolanamente la distanza dal beacon:
- -30 dBm → contatto/molto vicino
- -60 dBm → 1-3 metri
- -80 dBm → 5-10 metri
- -95 dBm → al limite della copertura
Per Home Assistant: pubblica via MQTT MAC + RSSI, e bermuda o esphome-bluetooth-proxy calcolano la posizione approssimata in stanza.
Scenario 2: server BLE personalizzato
Esponiamo una caratteristica leggibile/scrivibile dal client (es. un'app smartphone):
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#define SERVICE_UUID "12345678-1234-1234-1234-1234567890ab"
#define CHARACTERISTIC_UUID "abcd1234-1234-1234-1234-1234567890ab"
BLECharacteristic* pChar;
void setup() {
BLEDevice::init("ESP32-Sensor");
BLEServer *pServer = BLEDevice::createServer();
BLEService *pService = pServer->createService(SERVICE_UUID);
pChar = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_NOTIFY
);
pChar->addDescriptor(new BLE2902());
pChar->setValue("22.5");
pService->start();
pServer->getAdvertising()->start();
}
void loop() {
float t = 22.0 + random(-50, 50) / 10.0;
String s = String(t, 2);
pChar->setValue(s.c_str());
pChar->notify();
delay(2000);
}
Da smartphone usa nRF Connect (gratuito Android/iOS): si connette al ESP32-Sensor, vede la caratteristica e riceve i valori in push.
Sniffing iBeacon Apple
Gli iBeacon hanno un formato standard nei manufacturer data. Estrai UUID, major, minor:
std::string md = d.getManufacturerData();
if (md.length() >= 23 && md[0] == 0x4C && md[1] == 0x00
&& md[2] == 0x02 && md[3] == 0x15) {
// È un iBeacon
// bytes 4-19 = UUID, 20-21 = major, 22-23 = minor
}
Consumi BLE
Mantenere lo scanner attivo costa ~80 mA. Per progetti a batteria, alterna scan brevi (5 s) a deep sleep (60 s): media ~10 mA, batteria 18650 dura 12-15 giorni.
BLE vs Bluetooth Classic
Sull'ESP32 originale entrambi sono supportati ma occupano molta flash. Sul ESP32-S3 e ESP32-C3 è disponibile solo BLE (non Bluetooth Classic): per stampanti termiche o speaker A2DP serve un ESP32 originale.
Bluetooth Proxy per Home Assistant
ESPHome offre un firmware preconfezionato Bluetooth Proxy: l'ESP32 funge da hub BLE che inoltra tutti i pacchetti vicini a Home Assistant via WiFi. Estende dispositivi BLE (Xiaomi temperature, Switchbot) a tutta la casa con uno o più ESP32 sparsi.