ESP32 Bluetooth: guida a Classic e BLE con lo smartphone
Guida Bluetooth ESP32: LED dal telefono con BluetoothSerial, server BLE con servizi e UUID, test con nRF Connect, dati dei sensori, iPhone ed errori comuni.
Il Bluetooth dell'ESP32 sfrutta la radio wireless integrata nel chip per collegare il tuo ESP32 a uno smartphone, un tablet o un computer senza moduli aggiuntivi. L'ESP32 originale supporta contemporaneamente due tipi di Bluetooth: il Bluetooth Classic, che si comporta come una porta seriale, e il Bluetooth Low Energy (BLE), che consuma pochissima energia. Con Arduino Uno ti serve un modulo HC-05 separato per parlare con il telefono; con l'ESP32 bastano poche righe di codice. In questa guida configurerai entrambi i metodi passo dopo passo, accenderai un LED dal telefono, scriverai il tuo servizio BLE e invierai i dati di un sensore allo smartphone.
Il metodo da scegliere dipende dal tuo telefono e dalla tua scheda. Se hai uno smartphone Android e un ESP32 DevKit classico, il modo più veloce per iniziare è la libreria BluetoothSerial. Se usi un iPhone o la tua scheda è un ESP32-S3 o un ESP32-C3, il Bluetooth Classic non ti serve: devi usare il BLE. Verso la fine troverai anche lo stesso progetto BLE per il Raspberry Pi Pico W in MicroPython. Se usi l'ESP32 per la prima volta, completa prima l'installazione dell'Arduino IDE descritta in Cos'è l'ESP32?

Cos'è il Bluetooth dell'ESP32? Differenze tra Classic e BLE
Il Bluetooth Classic è il tipo più vecchio e conosciuto, quello usato dalle cuffie wireless e da moduli come l'HC-05. L'ESP32 lo usa tramite SPP (Serial Port Profile): i byte scorrono uno dopo l'altro come se tra telefono e scheda ci fosse un cavo seriale invisibile. Prima associ il dispositivo nelle impostazioni Bluetooth del telefono, poi ti colleghi con un'app terminale. Il BLE, invece, è un sistema separato arrivato nel 2010 con il Bluetooth 4.0 e pensato per dispositivi che funzionano per mesi a batteria. Nel BLE non c'è un flusso di dati continuo: l'ESP32 conserva una piccola tabella di valori (GATT) e il telefono li legge, li scrive o si iscrive per essere avvisato quando cambiano. Nonostante i nomi simili, i due non possono comunicare tra loro: un'app scritta per il BLE non può collegarsi a un dispositivo Bluetooth Classic.
| Caratteristica | Bluetooth Classic (SPP) | Bluetooth Low Energy (BLE) |
|---|---|---|
| Libreria Arduino | BluetoothSerial (inclusa nel pacchetto ESP32) | BLEDevice (inclusa nel pacchetto ESP32) o NimBLE-Arduino |
| Modello dei dati | Flusso continuo di byte, come una porta seriale | Tabella GATT fatta di servizi e caratteristiche |
| Quantità di dati | Alta, adatta a flussi continui | Pacchetti piccoli; 20 byte per notifica di default |
| Consumo | Sempre alto durante la connessione | Molto basso; può funzionare per mesi a batteria |
| Associazione | Nelle impostazioni Bluetooth del telefono | Non serve; la connessione avviene dall'app |
| Android | Supportato | Supportato |
| iPhone (iOS) | Non supportato | Supportato |
| ESP32 compatibili | Solo l'ESP32 originale (WROOM-32, WROVER) | ESP32, S3, C3, C6 e H2 (non l'S2) |
| Uso tipico | Terminale seriale, auto robot, sostituto dell'HC-05 | Sensori, dispositivi indossabili, domotica, beacon |
Una regola pratica: se vuoi inviare e ricevere testo da un telefono Android e vedere subito un risultato, usa il Classic; se ti servono la compatibilità con iPhone, consumi bassi o una delle schede ESP32 più recenti, usa il BLE. Se inizi un nuovo progetto vale la pena imparare il BLE, perché funziona con entrambi i tipi di telefono ed è presente in tutti i nuovi chip Espressif con Bluetooth. Se hai già lavorato con l'HC-05, vedrai che la logica dei comandi di Modulo Bluetooth HC-05 è esattamente la stessa con BluetoothSerial.
Quale Bluetooth ha ogni scheda ESP32?
Non tutti i chip della famiglia ESP32 hanno lo stesso hardware Bluetooth. L'errore più comune è provare a caricare un esempio BluetoothSerial trovato online su una scheda ESP32-S3 o ESP32-C3. Espressif ha inserito il Bluetooth Classic solo nel chip ESP32 originale; i chip successivi dotati di Bluetooth supportano solo il BLE. Puoi capire quale chip monta la tua scheda dalla scritta sullo schermo metallico del modulo (per esempio ESP32-WROOM-32 o ESP32-S3-WROOM-1).
| Scheda / chip | Bluetooth Classic | BLE | Nota |
|---|---|---|---|
| ESP32 (WROOM-32, WROVER, DevKit V1) | Sì | Sì (4.2) | L'unica famiglia ESP32 su cui funziona BluetoothSerial |
| ESP32-S2 | No | No | Solo Wi-Fi |
| ESP32-S3 | No | Sì (Bluetooth 5) | BluetoothSerial non compila, usa il BLE |
| ESP32-C3 | No | Sì (Bluetooth 5) | Schede BLE piccole ed economiche |
| ESP32-C6 | No | Sì (Bluetooth 5) | Anche Wi-Fi 6, Zigbee e Thread |
| ESP32-H2 | No | Sì (Bluetooth 5) | Niente Wi-Fi; pensato per Zigbee e Thread |
| ESP8266 (NodeMCU, Wemos D1) | No | No | Per il Bluetooth serve un HC-05 o un HM-10 |
| Raspberry Pi Pico W / Pico 2 W | Presente nell'hardware | Sì | MicroPython supporta solo il BLE |
| Raspberry Pi Pico / Pico 2 | No | No | Serve un modulo Bluetooth esterno |
Bluetooth Classic: controllare un LED dal telefono con BluetoothSerial
La libreria BluetoothSerial presenta l'ESP32 al telefono come se fosse un modulo HC-05 e usa gli stessi comandi dell'oggetto Serial: available(), read(), print() e println(). È inclusa nel pacchetto schede ESP32, quindi non devi installare altro. Per il circuito basta il LED blu integrato collegato al GPIO 2 nella maggior parte delle schede DevKit; se usi un LED esterno, collegalo al GPIO 2 con una resistenza da 220 Ω. Puoi verificare il valore con il calcolatore della resistenza per LED e vedere gli altri pin sicuri dell'ESP32 nello strumento Pinout. Assicurati che in Strumenti > Scheda sia selezionata ESP32 Dev Module e carica questo codice:
1#include "BluetoothSerial.h"
2
3// Klasik Bluetooth yalnızca ilk ESP32'de var / Classic Bluetooth exists only on the original ESP32
4#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
5#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
6#endif
7#if !defined(CONFIG_BT_SPP_ENABLED)
8#error Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip.
9#endif
10
11BluetoothSerial SerialBT;
12const int LED_PIN = 2; // DevKit dahili LED / DevKit onboard LED
13bool ledAcik = false;
14
15void ledAyarla(bool acik) {
16 ledAcik = acik;
17 digitalWrite(LED_PIN, acik ? HIGH : LOW);
18 SerialBT.println(acik ? "LED acik / LED on" : "LED kapali / LED off");
19}
20
21void setup() {
22 Serial.begin(115200);
23 pinMode(LED_PIN, OUTPUT);
24 SerialBT.begin("BlueGrays-ESP32"); // telefonda görünecek ad / name shown on the phone
25 Serial.println("Eslesmeye hazir / Ready to pair");
26}
27
28void loop() {
29 if (SerialBT.available()) {
30 String komut = SerialBT.readStringUntil('\n'); // satır sonuna kadar oku / read until newline
31 komut.trim(); // CR ve boşlukları sil / strip CR and spaces
32 komut.toLowerCase();
33
34 if (komut == "1" || komut == "ac") {
35 ledAyarla(true);
36 } else if (komut == "0" || komut == "kapat") {
37 ledAyarla(false);
38 } else if (komut == "durum") {
39 SerialBT.println(ledAcik ? "LED acik / LED on" : "LED kapali / LED off");
40 } else if (komut.length() > 0) {
41 SerialBT.print("Bilinmeyen komut / Unknown command: ");
42 SerialBT.println(komut);
43 }
44 }
45
46 if (Serial.available()) { // Seri Monitör'den telefona / Serial Monitor to phone
47 SerialBT.write(Serial.read());
48 }
49}Il codice legge ogni riga che arriva dal telefono come un comando. readStringUntil() aspetta l'arrivo del carattere di fine riga e trim() elimina il ritorno a capo (CR) aggiunto dal telefono e gli spazi; se dimentichi questa riga, inviando "1" la scheda riceve in realtà "1" più un CR invisibile e nessun comando corrisponde. Dato che i comandi vengono convertiti in minuscolo, "AC" e "ac" fanno la stessa cosa. Le ultime due righe inoltrano al telefono tutto ciò che scrivi nel Monitor seriale, così puoi chattare senza fili tra computer e telefono. I controlli #error all'inizio fanno fermare la compilazione con un messaggio chiaro se è selezionata la scheda sbagliata.
Collegarsi dal telefono con Serial Bluetooth Terminal
- Dopo il caricamento, apri il Monitor seriale a 115200 baud e controlla che compaia il messaggio "Eslesmeye hazir / Ready to pair".
- Sul tuo telefono Android cerca nuovi dispositivi in Impostazioni > Bluetooth e associa BlueGrays-ESP32. Di default l'ESP32 si associa senza chiedere un PIN; se il telefono mostra un codice di conferma, accettalo.
- Installa da Google Play l'app Serial Bluetooth Terminal di Kai Morich.
- Nell'app apri Devices dal menu laterale e scegli BlueGrays-ESP32 nella scheda Bluetooth Classic.
- Tocca l'icona della spina nella barra in alto; quando compare "Connected" la connessione è stabilita.
- Scrivi 1 nella casella in basso e invialo: il LED si accende e compare la risposta "LED acik / LED on". Con 0 si spegne e con durum scopri il suo stato attuale.
Come funziona il BLE? Servizi, caratteristiche e UUID
Nel BLE ci sono due ruoli. L'ESP32 fa da server (periferica): conserva i valori e dice "sono qui" inviando a brevi intervalli (di default ogni 20–40 ms sull'ESP32) piccoli pacchetti di advertising (annuncio) da massimo 31 byte. Il telefono fa da client (centrale): scansiona l'ambiente, trova il dispositivo e si collega. Dopo la connessione, il telefono legge la tabella GATT (Generic Attribute Profile) dell'ESP32. Immagina questa tabella come uno schedario: ogni cassetto è un servizio e ogni cartella nel cassetto è una caratteristica. Una caratteristica contiene un singolo valore (per esempio una temperatura o un comando per il LED) e delle proprietà che dicono cosa si può fare con esso.
| Termine | Significato | Esempio in questa guida |
|---|---|---|
| Advertising (annuncio) | Breve pacchetto con cui il dispositivo annuncia nome e servizi | Annuncio con il nome "BlueGrays-BLE" |
| Servizio | Gruppo che riunisce caratteristiche collegate | Servizio Nordic UART, Environmental Sensing (0x181A) |
| Caratteristica | Un singolo valore che viene letto o scritto | Comando del LED, temperatura (0x2A6E) |
| READ | Il telefono legge il valore quando vuole | Ultima temperatura misurata |
| WRITE / WRITE_NR | Il telefono scrive un valore (con o senza risposta) | Scrivere "1" accende il LED |
| NOTIFY | L'ESP32 avvisa da solo il telefono quando il valore cambia | Una nuova temperatura ogni 2 secondi |
| Descrittore 0x2902 (CCCD) | Interruttore che attiva e disattiva le notifiche | Pulsante a tripla freccia di nRF Connect |
| UUID | Numero identificativo di servizi e caratteristiche | 6E400001-B5A3-F393-E0A9-E50E24DCCA9E |
Ogni servizio e ogni caratteristica sono identificati da un UUID. Le funzioni standard definite dal Bluetooth SIG usano brevi numeri a 16 bit, come 0x180F per il servizio batteria e 0x2A6E per la caratteristica della temperatura. Per i tuoi dati senza un equivalente standard genera un UUID casuale a 128 bit; in Python basta import uuid; print(uuid.uuid4()). Quando scegli il tuo UUID non usare un valore che segue lo schema xxxxxxxx-0000-1000-8000-00805F9B34FB, perché è riservato ai numeri a 16 bit del Bluetooth SIG. Se vuoi lavorare senza problemi con app già pronte, è ancora più pratico usare UUID che tutti riconoscono:
| Servizio / caratteristica | UUID | Formato dei dati |
|---|---|---|
| Battery Service / Battery Level | 0x180F / 0x2A19 | 1 byte, percentuale (0–100) |
| Environmental Sensing / Temperature | 0x181A / 0x2A6E | Intero con segno a 2 byte, 0,01 °C |
| Environmental Sensing / Humidity | 0x181A / 0x2A6F | Intero senza segno a 2 byte, 0,01 % |
| Heart Rate / Heart Rate Measurement | 0x180D / 0x2A37 | Byte di flag + valore del battito |
| Nordic UART Service (NUS) | 6E400001-B5A3-F393-E0A9-E50E24DCCA9E | RX 6E400002 (scrittura), TX 6E400003 (notifica) |
Server BLE con ESP32: controllare un LED dal telefono (Nordic UART)
Nel nostro primo progetto BLE trasformiamo l'ESP32 in un server Nordic UART Service (NUS). NUS è un servizio di "porta seriale su BLE" definito da Nordic Semiconductor e diventato uno standard di fatto: il telefono scrive nella caratteristica RX e l'ESP32 invia notifiche dalla caratteristica TX. Il vantaggio è che molte app già pronte lo riconoscono; nRF Connect, la scheda Bluetooth LE di Serial Bluetooth Terminal e Bluefruit Connect su iPhone funzionano con lo stesso codice. Quindi il codice di questa sezione funziona sia con Android sia con iPhone, e anche su schede solo BLE come l'ESP32-S3 e il C3. La libreria BLE fa parte del pacchetto schede ESP32, non devi installare altro.
1#include <BLEDevice.h>
2#include <BLEServer.h>
3#include <BLEUtils.h>
4#include <BLE2902.h>
5
6// Nordic UART Service UUID'leri / Nordic UART Service UUIDs
7#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
8#define RX_UUID "6E400002-B5A3-F393-E0A9-E50E24DCCA9E" // telefon -> ESP32 / phone -> ESP32
9#define TX_UUID "6E400003-B5A3-F393-E0A9-E50E24DCCA9E" // ESP32 -> telefon / ESP32 -> phone
10
11const int LED_PIN = 2;
12
13BLEServer *sunucu = nullptr;
14BLECharacteristic *txKarakteristik = nullptr;
15volatile bool bagli = false;
16bool oncekiBagli = false;
17volatile int bekleyenKomut = -1; // -1: yeni komut yok / no new command
18
19class SunucuOlaylari : public BLEServerCallbacks {
20 void onConnect(BLEServer *s) { bagli = true; }
21 void onDisconnect(BLEServer *s) { bagli = false; }
22};
23
24class RxOlaylari : public BLECharacteristicCallbacks {
25 void onWrite(BLECharacteristic *c) {
26 String deger = c->getValue().c_str(); // çekirdek 2.x ve 3.x ile uyumlu / works on core 2.x and 3.x
27 deger.trim();
28 if (deger == "1") bekleyenKomut = 1;
29 else if (deger == "0") bekleyenKomut = 0;
30 }
31};
32
33void cevapGonder(const char *mesaj) {
34 txKarakteristik->setValue((uint8_t *)mesaj, strlen(mesaj));
35 txKarakteristik->notify(); // telefona bildirim / notify the phone
36}
37
38void setup() {
39 Serial.begin(115200);
40 pinMode(LED_PIN, OUTPUT);
41
42 BLEDevice::init("BlueGrays-BLE"); // yayında görünecek ad / advertised name
43 sunucu = BLEDevice::createServer();
44 sunucu->setCallbacks(new SunucuOlaylari());
45
46 BLEService *servis = sunucu->createService(SERVICE_UUID);
47
48 txKarakteristik = servis->createCharacteristic(TX_UUID, BLECharacteristic::PROPERTY_NOTIFY);
49 txKarakteristik->addDescriptor(new BLE2902()); // bildirim anahtarı (CCCD) / notification switch (CCCD)
50
51 BLECharacteristic *rx = servis->createCharacteristic(
52 RX_UUID, BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR);
53 rx->setCallbacks(new RxOlaylari());
54
55 servis->start();
56
57 BLEAdvertising *yayin = BLEDevice::getAdvertising();
58 yayin->addServiceUUID(SERVICE_UUID);
59 yayin->setScanResponse(true);
60 yayin->setMinPreferred(0x06); // iPhone bağlantısına yardımcı / helps iPhone connections
61 yayin->setMaxPreferred(0x12);
62 BLEDevice::startAdvertising();
63 Serial.println("BLE yayini basladi / BLE advertising started");
64}
65
66void loop() {
67 if (bekleyenKomut != -1) { // telefondan komut geldi / command received
68 int komut = bekleyenKomut;
69 bekleyenKomut = -1;
70 digitalWrite(LED_PIN, komut == 1 ? HIGH : LOW);
71 if (bagli) cevapGonder(komut == 1 ? "LED acik" : "LED kapali");
72 }
73
74 if (!bagli && oncekiBagli) { // bağlantı koptu / disconnected
75 delay(500); // yığına zaman tanı / give the stack time
76 sunucu->startAdvertising(); // yeniden görünür ol / advertise again
77 Serial.println("Yeniden yayin / Advertising again");
78 oncekiBagli = false;
79 }
80 if (bagli && !oncekiBagli) { // yeni bağlantı / new connection
81 Serial.println("Telefon baglandi / Phone connected");
82 oncekiBagli = true;
83 }
84}La parte più importante del codice sono le classi di callback. onConnect e onDisconnect tengono traccia dello stato della connessione, mentre onWrite cattura il valore che il telefono scrive nella caratteristica RX. Queste funzioni girano nel task dello stack Bluetooth, quindi invece di farci dentro lavori lunghi cambiamo solo una variabile; il lavoro vero lo fa loop(). Il descrittore BLE2902 aggiunto alla caratteristica TX serve perché il telefono possa attivare le notifiche. Quando la connessione cade, l'ESP32 non riprende l'advertising da solo; senza la riga startAdvertising() in loop() il dispositivo non si trova più dopo la prima connessione. Le righe setMinPreferred e setMaxPreferred aggiungono all'annuncio l'intervallo di connessione preferito, che facilita la connessione con gli iPhone.
Testare il BLE con nRF Connect passo dopo passo
- Installa sul telefono l'app gratuita nRF Connect for Mobile di Nordic Semiconductor; esiste per Android e per iPhone.
- Su Android l'app chiede il Bluetooth e l'autorizzazione Dispositivi nelle vicinanze (fino ad Android 11 l'autorizzazione alla posizione con la localizzazione attiva); senza di essa l'elenco della scansione resta vuoto.
- Nella scheda Scanner premi SCAN e trova BlueGrays-BLE nell'elenco. Il valore RSSI accanto è l'intensità del segnale: -50 dBm significa molto vicino, -90 dBm è il limite della portata.
- Tocca CONNECT. Nella schermata che si apre compare Nordic UART Service; se avessi usato UUID generati da te, qui ci sarebbe scritto "Unknown Service".
- Tocca l'icona a tripla freccia verso il basso accanto alla caratteristica TX (6E400003) per attivare le notifiche.
- Tocca l'icona a freccia verso l'alto accanto alla caratteristica RX (6E400002), scegli Text (UTF-8) come tipo di dato, scrivi 1 e invia. Il LED si accende e sulla riga TX compare "LED acik".
Questo test è il modo più rapido per dimostrare che la parte ESP32 funziona prima di scrivere un'app per il telefono. Se qualcosa non va, prova prima con nRF Connect: se il dispositivo non è nell'elenco, il problema è l'advertising; se si collega ma il servizio non compare, manca la chiamata start() del servizio; se scrivi un valore e il LED resta spento, controlla il confronto dentro onWrite. Puoi provare lo stesso codice anche dalla scheda Bluetooth LE di Serial Bluetooth Terminal: l'app riconosce il servizio NUS e scambi messaggi come con il Bluetooth Classic. Se vuoi creare la tua app Android, dai un'occhiata a App Inventor con Arduino e Bluetooth.
Inviare i dati di un sensore via BLE (DHT22)

| Pin del DHT22 | ESP32 | Nota |
|---|---|---|
| VCC (+) | 3V3 | Funziona da 3,3 a 5 V |
| DATA (OUT) | GPIO 4 | Con il sensore nudo aggiungi una resistenza da 10 kΩ tra DATA e 3V3 |
| GND (−) | GND | Massa comune |
In questo esempio usiamo il servizio standard Environmental Sensing (0x181A) e le sue caratteristiche di temperatura (0x2A6E) e umidità (0x2A6F). Il bello degli UUID standard è che app come nRF Connect riconoscono il valore e lo mostrano direttamente come "23.45 °C". Lo standard chiede di inviare i valori non come testo, ma come interi moltiplicati per 100: una temperatura di 23,45 °C diventa il numero 2345 e viene inviata in 2 byte, prima il byte basso (little-endian). Per leggere il sensore usiamo la DHT sensor library di Adafruit, che abbiamo spiegato nella guida al DHT11; puoi installarla dal Gestore librerie.
1#include <BLEDevice.h>
2#include <BLEServer.h>
3#include <BLE2902.h>
4#include <DHT.h>
5
6#define DHT_PIN 4
7DHT dht(DHT_PIN, DHT22);
8
9BLEServer *sunucu = nullptr;
10BLECharacteristic *sicaklikKar = nullptr;
11BLECharacteristic *nemKar = nullptr;
12volatile bool bagli = false;
13bool oncekiBagli = false;
14unsigned long sonOlcum = 0;
15
16class SunucuOlaylari : public BLEServerCallbacks {
17 void onConnect(BLEServer *s) { bagli = true; }
18 void onDisconnect(BLEServer *s) { bagli = false; }
19};
20
21BLECharacteristic *karakteristikEkle(BLEService *servis, uint16_t uuid) {
22 BLECharacteristic *k = servis->createCharacteristic(
23 BLEUUID(uuid), BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
24 k->addDescriptor(new BLE2902()); // bildirim için gerekli / needed for notify
25 return k;
26}
27
28void setup() {
29 Serial.begin(115200);
30 dht.begin();
31
32 BLEDevice::init("BlueGrays-Sensor");
33 sunucu = BLEDevice::createServer();
34 sunucu->setCallbacks(new SunucuOlaylari());
35
36 BLEService *servis = sunucu->createService(BLEUUID((uint16_t)0x181A)); // Environmental Sensing
37 sicaklikKar = karakteristikEkle(servis, 0x2A6E); // sıcaklık, 0,01 °C / temperature, 0.01 °C
38 nemKar = karakteristikEkle(servis, 0x2A6F); // nem, %0,01 / humidity, 0.01 %
39 servis->start();
40
41 BLEAdvertising *yayin = BLEDevice::getAdvertising();
42 yayin->addServiceUUID(BLEUUID((uint16_t)0x181A));
43 yayin->setScanResponse(true);
44 BLEDevice::startAdvertising();
45}
46
47void loop() {
48 if (millis() - sonOlcum >= 2000) { // DHT22 en fazla 2 sn'de bir / DHT22 at most every 2 s
49 sonOlcum = millis();
50 float t = dht.readTemperature();
51 float h = dht.readHumidity();
52 if (!isnan(t) && !isnan(h)) {
53 int16_t t100 = (int16_t)lroundf(t * 100); // 23,45 °C -> 2345
54 uint16_t h100 = (uint16_t)lroundf(h * 100); // %41,2 -> 4120
55 sicaklikKar->setValue((uint8_t *)&t100, 2); // 2 bayt, düşük bayt önce / 2 bytes, little-endian
56 nemKar->setValue((uint8_t *)&h100, 2);
57 if (bagli) {
58 sicaklikKar->notify();
59 nemKar->notify();
60 }
61 Serial.printf("%.2f C %.2f %%\n", t, h);
62 }
63 }
64
65 if (!bagli && oncekiBagli) { // bağlantı koptu / disconnected
66 delay(500);
67 sunucu->startAdvertising(); // yeniden yayın / advertise again
68 oncekiBagli = false;
69 }
70 if (bagli && !oncekiBagli) oncekiBagli = true;
71}Il codice esegue una misura ogni due secondi, perché il DHT22 non si può leggere più spesso. I valori vengono prima scritti nella caratteristica con setValue(); così il telefono ottiene l'ultima misura anche quando fa una lettura (READ) senza aspettare una notifica. Se il telefono è collegato, notify() invia subito il nuovo valore. Se nRF Connect non riesce a decodificare il valore, vedrai i byte grezzi: per 23,45 °C lo schermo mostra (0x) 29-09. Invertendo i byte ottieni 0x0929, cioè 2345 in decimale; diviso per 100 dà la temperatura. Dovrai fare lo stesso calcolo quando scriverai la tua app. Inviare il valore come testo può sembrare più facile, ma il formato standard usa meno byte e funziona con le app già esistenti.
Bluetooth dell'ESP32 con iPhone (iOS)
La domanda più frequente di chi usa un iPhone è perché l'ESP32 non compare nelle impostazioni Bluetooth. Apple apre il profilo porta seriale (SPP) del Bluetooth Classic solo agli accessori con certificazione MFi, quindi da un iPhone non puoi collegarti a un ESP32 che usa BluetoothSerial. La soluzione è il BLE, e gli esempi NUS e del sensore qui sopra funzionano senza problemi su iPhone. Ci sono alcuni punti da tenere presenti:
- Ricerca nelle Impostazioni: i dispositivi BLE non compaiono nelle impostazioni Bluetooth di iOS; collegati da un'app come nRF Connect, LightBlue o Bluefruit Connect.
- Cache: iOS ricorda il nome e l'elenco dei servizi del dispositivo. Se dopo aver cambiato i servizi nel codice vedi ancora l'elenco vecchio, spegni e riaccendi il Bluetooth del telefono.
- Niente indirizzo MAC: iOS non mostra alle app l'indirizzo MAC del dispositivo e assegna a ciascuno un proprio identificativo; se usi più ESP32, dai a ognuno un nome diverso.
- Intervallo di connessione: non cancellare le righe setMinPreferred(0x06) e setMaxPreferred(0x12); anche gli esempi di Espressif le consigliano contro i problemi di connessione con iPhone.
- La tua app: su iOS si usa Core Bluetooth con Swift; non serve l'associazione, l'app cerca direttamente l'UUID del servizio.
BLE con il Raspberry Pi Pico W e MicroPython

Nella famiglia Raspberry Pi Pico il Bluetooth c'è solo sul Pico W e sul Pico 2 W. Il chip CYW43439 della scheda supporta a livello hardware sia il Bluetooth Classic sia il BLE, ma MicroPython usa solo il BLE (dalla versione 1.20). Il codice qui sotto è l'equivalente MicroPython dell'esempio NUS dell'ESP32; dato che usa gli stessi UUID, lo provi allo stesso modo con nRF Connect e Serial Bluetooth Terminal. Sulla scheda deve essere installata la versione di MicroPython pensata per il Pico W. Il codice legge anche il sensore di temperatura interno della scheda e lo invia al telefono collegato ogni 2 secondi.
1import bluetooth
2import struct
3import time
4from machine import Pin, ADC
5from micropython import const
6
7_IRQ_CENTRAL_CONNECT = const(1)
8_IRQ_CENTRAL_DISCONNECT = const(2)
9_IRQ_GATTS_WRITE = const(3)
10_FLAG_READ = const(0x0002)
11_FLAG_WRITE_NO_RESPONSE = const(0x0004)
12_FLAG_WRITE = const(0x0008)
13_FLAG_NOTIFY = const(0x0010)
14
15# Nordic UART Service: ESP32 örneğiyle aynı UUID'ler / same UUIDs as the ESP32 example
16NUS_UUID = bluetooth.UUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
17TX = (bluetooth.UUID("6E400003-B5A3-F393-E0A9-E50E24DCCA9E"), _FLAG_READ | _FLAG_NOTIFY)
18RX = (bluetooth.UUID("6E400002-B5A3-F393-E0A9-E50E24DCCA9E"), _FLAG_WRITE | _FLAG_WRITE_NO_RESPONSE)
19
20led = Pin("LED", Pin.OUT) # Pico W dahili LED / Pico W onboard LED
21sensor = ADC(4) # dahili sıcaklık sensörü / internal temperature sensor
22
23ble = bluetooth.BLE()
24ble.active(True)
25((tx_h, rx_h),) = ble.gatts_register_services(((NUS_UUID, (TX, RX)),))
26baglanti = None
27
28
29def alan(tur, veri): # uzunluk + tür + veri / length + type + data
30 return struct.pack("BB", len(veri) + 1, tur) + veri
31
32
33YAYIN = alan(0x01, b"\x06") + alan(0x09, b"BlueGrays-Pico") # bayraklar + ad / flags + name
34YANIT = alan(0x07, bytes(NUS_UUID)) # 128 bit UUID / 128-bit UUID
35
36
37def yayinla():
38 ble.gap_advertise(100000, adv_data=YAYIN, resp_data=YANIT) # 100 ms aralık / interval
39
40
41def olay(event, data):
42 global baglanti
43 if event == _IRQ_CENTRAL_CONNECT:
44 baglanti = data[0]
45 elif event == _IRQ_CENTRAL_DISCONNECT:
46 baglanti = None
47 yayinla() # yeniden görünür ol / advertise again
48 elif event == _IRQ_GATTS_WRITE and data[1] == rx_h:
49 komut = ble.gatts_read(rx_h).decode().strip()
50 if komut == "1":
51 led.on()
52 elif komut == "0":
53 led.off()
54
55
56ble.irq(olay)
57yayinla()
58
59while True:
60 if baglanti is not None:
61 volt = sensor.read_u16() * 3.3 / 65535
62 sicaklik = 27 - (volt - 0.706) / 0.001721 # veri sayfası formülü / datasheet formula
63 try:
64 ble.gatts_notify(baglanti, tx_h, "T:{:.1f}\n".format(sicaklik).encode())
65 except OSError:
66 pass # bağlantı yeni koptu / just disconnected
67 time.sleep(2)Poiché il modulo bluetooth di MicroPython è di basso livello, costruiamo da soli il pacchetto di advertising. Ogni campo è formato da un byte di lunghezza, un byte di tipo e i dati: 0x01 sono i flag, 0x09 il nome completo del dispositivo e 0x07 l'UUID di servizio a 128 bit. Dato che un pacchetto di advertising contiene al massimo 31 byte, abbiamo messo il campo UUID da 18 byte nella risposta alla scansione (resp_data). gatts_register_services() restituisce gli handle delle caratteristiche; con questi handle leggiamo il valore scritto dal telefono e inviamo le notifiche. Se vuoi un codice più corto, puoi installare con mip la libreria aioble, basata su asyncio. Per far girare il codice sull'ESP32 con MicroPython basta cambiare la riga del LED in Pin(2, Pin.OUT) e leggere, per esempio, un DHT11 al posto del sensore di temperatura interno.
Wi-Fi e Bluetooth insieme: memoria, portata e alimentazione
Sull'ESP32 Wi-Fi e Bluetooth condividono la stessa radio a 2,4 GHz. Il chip li fa funzionare a divisione di tempo: puoi usarli insieme, ma ognuno diventa un po' più lento, e il Bluetooth Classic insieme a un traffico Wi-Fi intenso è lo scenario più impegnativo. Il problema vero però è di solito la memoria: lo stack Bluetooth è grande e, insieme al Wi-Fi, il programma può superare la partizione dell'applicazione predefinita da 1,2 MB (1.310.720 byte); già da solo l'esempio BLE di questa guida ne occupa circa l'80%. Se la compilazione termina con "Sketch too big" o "text section exceeds available space in board" e non usi gli aggiornamenti via etere (OTA), scegli Huge APP (3MB No OTA/1MB SPIFFS) in Strumenti > Partition Scheme. Anche la libreria NimBLE-Arduino, che occupa meno flash e RAM, è una buona alternativa.
La portata dipende dal design dell'antenna della scheda e dall'ambiente. Con un ESP32 dotato di antenna PCB integrata puoi aspettarti 10–20 metri in casa, a seconda dei muri, e di più all'aperto; i moduli con connettore per antenna esterna (per esempio l'ESP32-WROOM-32U) arrivano ancora più lontano. Coprire l'antenna con una scatola metallica, una batteria o dei cavi riduce molto la portata. Dato che la corrente sale per un attimo quando si accende la radio, un cavo USB scadente può far riavviare la scheda con il messaggio "Brownout detector was triggered"; trovi le soluzioni in ESP32 Brownout e Guru Meditation.
Idee di progetti Bluetooth con l'ESP32
- Auto robot Bluetooth: passa i comandi F, B, L e R ricevuti via BluetoothSerial a un driver motori L298N; per la parte meccanica vedi Costruire un'auto robot con Arduino.
- Presa intelligente: sostituisci il LED dell'esempio NUS con un modulo relè e accendi una lampada dall'iPhone.
- Termometro wireless: aggiungi un display OLED all'esempio del DHT22, così il valore compare sia sul display sia sul telefono.
- Luce d'atmosfera colorata: cambia il colore di una striscia WS2812B NeoPixel con i valori R, G e B inviati dal telefono.
- La tua app Android: crea in poche ore un'app telecomando con pulsanti e cursori con App Inventor.
- Comunicazione tra due ESP32: per far parlare due schede senza telefono, prova al posto del Bluetooth il metodo ESP-NOW, più semplice e veloce.
Errori frequenti del Bluetooth dell'ESP32 e soluzioni
| Sintomo / errore | Causa | Soluzione |
|---|---|---|
| "Serial Bluetooth not available or not enabled" | La scheda scelta è un ESP32-S3, C3, C6 o H2, senza Bluetooth Classic | Usa un ESP32 classico o passa all'esempio BLE (NUS) |
| "conversion from 'String' to non-scalar type 'std::string' requested" | Codice scritto per il core 2.x (std::string) compilato con un core 3.x | Assegna getValue().c_str() a una variabile String |
| "Sketch too big" | Bluetooth e Wi-Fi insieme superano la partizione dell'applicazione | Partition Scheme: scegli Huge APP o usa NimBLE-Arduino |
| L'iPhone non trova l'ESP32 | Il Bluetooth Classic (SPP) non è supportato su iOS | Usa il BLE e collegati dall'interno di un'app |
| Il dispositivo sparisce dopo la prima connessione | L'advertising non è ripartito dopo la disconnessione | Chiama startAdvertising() dopo la disconnessione |
| Le notifiche non arrivano | Notifiche non attivate sul telefono o BLE2902 mancante | Tocca la tripla freccia in nRF Connect; aggiungi BLE2902 alla caratteristica |
| "read failed, socket might closed or timeout" | Dati di associazione vecchi o un altro telefono già collegato | Dimentica il dispositivo sul telefono e associalo di nuovo; l'SPP accetta una sola connessione alla volta |
| La fine dei messaggi lunghi viene tagliata | MTU predefinito di 23 byte, cioè 20 byte di dati per notifica | Dividi il messaggio in pezzi da 20 byte o consenti un MTU più grande con BLEDevice::setMTU(185) |
| Le modifiche ai servizi non compaiono sul telefono | Il telefono tiene in cache la vecchia tabella GATT | Spegni e riaccendi il Bluetooth del telefono; su Android dimentica il dispositivo |
Con il Bluetooth dell'ESP32, trasformare il telefono in un telecomando e l'ESP32 in un sensore wireless o in un dispositivo smart richiede solo poche righe di codice. BluetoothSerial è la scelta giusta per iniziare in fretta con Android, il BLE per la compatibilità con iPhone e i consumi bassi. Se vuoi continuare con progetti wireless con l'ESP32, nell'app BlueGrays trovi progetti come LED, LED RGB, misura della temperatura con il DHT22 e un'auto comandata via Bluetooth, ognuno con lo schema passo dopo passo e il codice pronto.
Domande frequenti
L'ESP32 ha il Bluetooth?
Sì. Il chip ESP32 originale supporta con il Bluetooth 4.2 sia il Bluetooth Classic (BR/EDR) sia il BLE e non richiede moduli aggiuntivi. ESP32-S3, C3, C6 e H2 supportano solo il BLE, mentre l'ESP32-S2 non ha affatto il Bluetooth.
Qual è la portata del Bluetooth dell'ESP32?
Con un ESP32 dotato di antenna PCB integrata si ottengono di solito 10–20 metri in casa; muri, oggetti metallici e orientamento dell'antenna influiscono sulla distanza. All'aperto la portata aumenta, e i moduli con antenna esterna (per esempio il WROOM-32U) arrivano ancora più lontano.
L'ESP32 si può collegare a un iPhone via Bluetooth?
Sì con il BLE, ma non con il Bluetooth Classic (BluetoothSerial), perché iOS apre il profilo porta seriale (SPP) solo agli accessori con certificazione MFi. Su iPhone puoi collegarti al servizio BLE dell'ESP32 con un'app come nRF Connect, LightBlue o Bluefruit Connect.
BluetoothSerial funziona su ESP32-S3 ed ESP32-C3?
No. Questi chip non hanno l'hardware Bluetooth Classic, quindi BluetoothSerial non compila. Per avere la stessa logica di porta seriale via BLE puoi usare il Nordic UART Service (NUS); app come Serial Bluetooth Terminal lo riconoscono.
L'ESP32 può usare Wi-Fi e Bluetooth contemporaneamente?
Sì, funzionano insieme dividendosi nel tempo la stessa radio a 2,4 GHz. La velocità però cala un po' e il programma diventa più grande; se ottieni l'errore "Sketch too big", scegli Huge APP in Partition Scheme o usa la libreria NimBLE-Arduino, più leggera.
Serve un HC-05 con l'ESP32?
Di solito no. Sull'ESP32 classico la libreria BluetoothSerial fa internamente il lavoro dell'HC-05 e funziona con le stesse app terminale. L'HC-05 serve solo su schede senza Bluetooth (Arduino Uno, ESP8266, Pico standard, ESP32-S2) o se un progetto richiede il Bluetooth Classic e usi un ESP32 solo BLE.
Progetti da realizzare dopo questo articolo
Vedi tutti i progetti
Lampeggio del LED interno dell'ESP32
Far lampeggiare il LED blu integrato nell'ESP32 DevKit V1. Non sono necessari componenti esterni! Il primo progetto da provare quando si acquista un ESP32. Il GPIO 2 è collegato al LED interno.

LED Lampeggiante con ESP32
Insegna l'uso di base delle uscite GPIO con l'ESP32. Otteniamo un effetto lampeggiante accendendo e spegnendo un LED dal pin digitale. Versione ESP32 del classico progetto blink su Arduino UNO; i comandi di base di blink nell'ambiente Arduino sono gli stessi, ma la disposizione dei pin e i livelli GPIO a 3,3 V dell'ESP32 sono diversi da quelli di Arduino UNO.

Controllo LED ESP32 con pulsante (Toggle)
Ogni volta che si preme il pulsante, lo stato del LED cambia: se è acceso si spegne, se è spento si accende. Classico schema toggle. Con il debounce puliamo il rumore meccanico del pulsante per garantire una singola commutazione con una sola pressione.

Controllare un LED dal telefono tramite Bluetooth
Una piccola prova per accendere e spegnere le luci della tua stanza dal telefono. Il modulo Bluetooth passa all'Arduino le lettere che invii dal telefono; una lettera accende il LED rosso, un'altra qu

Auto controllata via Bluetooth con L298N
Trasforma il telefono in un telecomando e guida la tua auto robot. L'Arduino riceve tramite un modulo Bluetooth i comandi di un'app di controllo remoto sul telefono e fa girare le ruote con un driver

Controllo LED RGB ESP32
Generazione illimitata di colori con un singolo LED RGB invece di 3 LED separati. La transizione arcobaleno avviene impostando i valori R/G/B con analogWrite() come in Arduino. La teoria dei colori (come rosso+verde=giallo) viene vista in pratica.

ESP32 DHT22 Temperatura e umidità (con display LCD)
Si tratta di un progetto che misura la temperatura e l'umidità dell'ambiente tramite il sensore DHT22 e le visualizza in tempo reale su un display LCD I2C 16x2. I dati vengono letti dal sensore utilizzando il protocollo one-wire tramite la libreria Adafruit DHT. Questo progetto costituisce la base per stazioni meteorologiche IoT, sistemi di automazione domestica intelligente o sistemi di monitoraggio ambientale nelle serre. La comunicazione con il DHT22 avviene tramite il pin GPIO 4 dell'ESP32.

Controllo della striscia LED NeoPixel WS2812B con ESP32
I LED indirizzabili WS2812B (NeoPixel) sono LED RGB intelligenti che possono essere controllati in cascata tramite un unico pin dati. In questo progetto controlleremo una striscia WS2812B da 8 LED utilizzando la libreria Adafruit_NeoPixel per creare un'animazione fluida dell'arcobaleno. Sebbene il livello logico a 3,3 V dell'ESP32 funzioni senza problemi con la maggior parte dei componenti WS2812B, in caso di instabilità mostreremo la soluzione con una resistenza in serie da 470 Ω e un condensat
Altri articoli
App BlueGrays
Costruisci questo circuito passo passo sul telefono
Schema, codice ed elenco componenti in un’unica schermata. Oltre 200 progetti Arduino, ESP32 e Pico da realizzare.
Inquadra con il telefono
Punta la fotocamera sul codice: si apre lo store del tuo telefono.
oppure apri lo store

