Tutorial de Bluetooth no ESP32: Classic ou BLE, controle de LED e sensores
Guia de Bluetooth no ESP32: controle um LED pelo celular com o BluetoothSerial, crie um servidor BLE com serviços e UUIDs, teste com o nRF Connect e envie dados de sensores.
O Bluetooth do ESP32 usa o rádio sem fio integrado ao chip para conectar o ESP32 a um celular, tablet ou computador sem nenhum módulo extra. O ESP32 original suporta dois tipos diferentes de Bluetooth ao mesmo tempo: o Bluetooth Classic, que se comporta como uma porta serial, e o Bluetooth Low Energy (BLE), que consome muito pouca energia. No Arduino Uno você precisa de um módulo HC-05 separado para falar com o celular; no ESP32, algumas linhas de código bastam. Neste guia você vai configurar os dois métodos passo a passo, acender um LED pelo celular, escrever o seu próprio serviço BLE e enviar dados de sensores para o celular.
O método que você escolhe depende do seu celular e da sua placa. Se você tem um celular Android e um ESP32 DevKit clássico, o começo mais rápido é a biblioteca BluetoothSerial. Se você usa iPhone, ou se a sua placa é um ESP32-S3 ou ESP32-C3, o Bluetooth Classic não vai ajudar; você precisa do BLE. Perto do fim, você também encontra o mesmo projeto BLE para o Raspberry Pi Pico W em MicroPython. Se é a sua primeira vez com o ESP32, termine antes a configuração do Arduino IDE em O que é o ESP32?.

O que é o Bluetooth do ESP32? Classic ou BLE
O Bluetooth Classic é o tipo mais antigo e conhecido, usado em fones sem fio e em módulos como o HC-05. O ESP32 o usa pelo SPP (Serial Port Profile): os bytes passam um atrás do outro, como se houvesse um cabo serial invisível entre o celular e a placa. Primeiro você pareia o dispositivo nas configurações de Bluetooth do celular e depois se conecta com um app de terminal. O BLE, por outro lado, é um sistema à parte, lançado com o Bluetooth 4.0 em 2010 e pensado para dispositivos que funcionam meses com uma bateria. O BLE não tem um fluxo contínuo de dados; o ESP32 mantém uma pequena tabela de valores (GATT), e o celular lê esses valores, escreve neles ou se inscreve para ser avisado quando mudam. Apesar dos nomes parecidos, os dois não conversam entre si: um app feito para BLE não consegue se conectar a um dispositivo Bluetooth Classic.
| Característica | Bluetooth Classic (SPP) | Bluetooth Low Energy (BLE) |
|---|---|---|
| Biblioteca Arduino | BluetoothSerial (incluída no pacote ESP32) | BLEDevice (incluída no pacote ESP32) ou NimBLE-Arduino |
| Modelo de dados | Fluxo contínuo de bytes, como uma porta serial | Tabela GATT formada por serviços e características |
| Quantidade de dados | Alta, adequada a fluxos contínuos | Pacotes pequenos; 20 bytes por notificação por padrão |
| Consumo de energia | Sempre alto enquanto conectado | Muito baixo; pode funcionar meses com uma bateria |
| Pareamento | Feito nas configurações de Bluetooth do celular | Não é necessário; a conexão é feita dentro do app |
| Android | Suportado | Suportado |
| iPhone (iOS) | Não suportado | Suportado |
| ESP32 compatíveis | Só o ESP32 original (WROOM-32, WROVER) | ESP32, S3, C3, C6 e H2 (não o S2) |
| Uso típico | Terminal serial, carrinho robô, substituto do HC-05 | Sensores, vestíveis, casa inteligente, beacons |
Uma regra prática: se você quer enviar e receber texto de um celular Android e ver resultados rápido, use o Classic; se precisa de suporte a iPhone, de baixo consumo ou de uma das placas ESP32 mais novas, use o BLE. Se você está começando um projeto novo, vale a pena aprender BLE, porque ele funciona nos dois tipos de celular e está em todos os chips novos da Espressif com Bluetooth. Se você já trabalhou com o HC-05, vai ver que a lógica de comandos do módulo Bluetooth HC-05 é exatamente a mesma com o BluetoothSerial.
Quais placas ESP32 têm qual Bluetooth?
Nem todo chip da família ESP32 tem o mesmo hardware de Bluetooth. O erro mais comum é tentar carregar em uma placa ESP32-S3 ou ESP32-C3 um exemplo de BluetoothSerial encontrado na internet. A Espressif colocou o Bluetooth Classic só no chip ESP32 original; os chips posteriores que têm Bluetooth suportam apenas BLE. Você descobre qual chip a sua placa tem pelo texto na blindagem metálica do módulo (por exemplo ESP32-WROOM-32 ou ESP32-S3-WROOM-1).
| Placa / chip | Bluetooth Classic | BLE | Observação |
|---|---|---|---|
| ESP32 (WROOM-32, WROVER, DevKit V1) | Sim | Sim (4.2) | A única família ESP32 que roda o BluetoothSerial |
| ESP32-S2 | Não | Não | Só Wi-Fi |
| ESP32-S3 | Não | Sim (Bluetooth 5) | O BluetoothSerial não compila, use BLE |
| ESP32-C3 | Não | Sim (Bluetooth 5) | Placas BLE pequenas e baratas |
| ESP32-C6 | Não | Sim (Bluetooth 5) | Também tem Wi-Fi 6, Zigbee e Thread |
| ESP32-H2 | Não | Sim (Bluetooth 5) | Sem Wi-Fi; focado em Zigbee e Thread |
| ESP8266 (NodeMCU, Wemos D1) | Não | Não | Precisa de um HC-05 ou HM-10 para Bluetooth |
| Raspberry Pi Pico W / Pico 2 W | No hardware | Sim | O MicroPython suporta só BLE |
| Raspberry Pi Pico / Pico 2 | Não | Não | Precisa de um módulo Bluetooth externo |
Bluetooth Classic: controlando um LED pelo celular com o BluetoothSerial
A biblioteca BluetoothSerial apresenta o ESP32 ao celular como um módulo HC-05 e usa os mesmos comandos do objeto Serial: available(), read(), print() e println(). A biblioteca vem com o pacote de placas ESP32, então não há nada extra para instalar. Para o circuito, basta o LED azul integrado, ligado ao GPIO 2 na maioria das placas DevKit; se usar um LED externo, ligue-o ao GPIO 2 por um resistor de 220 Ω. Você pode conferir o valor do resistor com a calculadora de resistor para LED e ver os outros pinos seguros do ESP32 na ferramenta Pinagem. Confira se ESP32 Dev Module está selecionado em Ferramentas > Placa e carregue este código:
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}O código lê cada linha que chega do celular como um comando. readStringUntil() espera até chegar o caractere de nova linha, e trim() remove o retorno de carro (CR) que o celular acrescenta, além de espaços; se você esquecer essa linha, enviar “1” na verdade entrega “1” mais um caractere CR invisível, e nenhum comando bate. Como os comandos são convertidos para minúsculas, “AC” e “ac” fazem a mesma coisa. As duas linhas do fim repassam ao celular tudo o que você digita no Monitor Serial, então dá para manter uma conversa sem fio entre o computador e o celular. As verificações #error no topo fazem a compilação parar com uma mensagem clara se a placa errada estiver selecionada.
Conectando pelo celular com o Serial Bluetooth Terminal
- Depois de carregar, abra o Monitor Serial em 115200 baud e procure a mensagem “Eslesmeye hazir / Ready to pair” (pronto para parear).
- No celular Android, procure novos dispositivos em Configurações > Bluetooth e pareie com BlueGrays-ESP32. Por padrão, o ESP32 pareia sem pedir PIN; se o celular mostrar um código de confirmação, aceite.
- Instale pelo Google Play o app Serial Bluetooth Terminal, de Kai Morich.
- No app, abra Devices no menu lateral e escolha BlueGrays-ESP32 na aba Bluetooth Classic.
- Toque no ícone de plugue na barra superior; quando aparecer “Connected” na tela, você está conectado.
- Digite 1 na caixa de baixo e envie: o LED acende e aparece a resposta “LED acik / LED on”. 0 apaga o LED, e durum informa o estado atual.
Como o BLE funciona? Serviços, características e UUIDs
O BLE tem dois papéis. O ESP32 faz o papel de servidor (periférico): ele guarda os valores e anuncia “estou aqui” enviando, em intervalos curtos, pequenos pacotes de advertising (anúncio) de no máximo 31 bytes (a cada 20–40 ms por padrão no ESP32). O celular faz o papel de cliente (central): ele varre os arredores, encontra o dispositivo e se conecta. Depois de conectado, o celular lê a tabela GATT (Generic Attribute Profile) do ESP32. Pense nessa tabela como um arquivo de gavetas: cada gaveta é um serviço, e cada pasta dentro de uma gaveta é uma característica. Uma característica guarda um único valor (como uma temperatura ou um comando de LED) e propriedades que dizem o que se pode fazer com ele.
| Termo | Significado | Exemplo neste guia |
|---|---|---|
| Advertising (anúncio) | Pacote curto em que o dispositivo anuncia o nome e os serviços | Anúncio com o nome “BlueGrays-BLE” |
| Serviço | Grupo que reúne características relacionadas | Serviço Nordic UART, Environmental Sensing (0x181A) |
| Característica | Um único valor que é lido ou escrito | Comando do LED, temperatura (0x2A6E) |
| READ (leitura) | O celular lê o valor quando quiser | Última temperatura medida |
| WRITE / WRITE_NR (escrita) | O celular escreve um valor (com ou sem resposta) | Escrever “1” acende o LED |
| NOTIFY (notificação) | O ESP32 avisa o celular por conta própria quando o valor muda | Uma nova temperatura a cada 2 segundos |
| Descritor 0x2902 (CCCD) | Chave que liga e desliga as notificações | Botão de seta tripla no nRF Connect |
| UUID | Número de identificação de serviços e características | 6E400001-B5A3-F393-E0A9-E50E24DCCA9E |
Cada serviço e característica é identificado por um UUID. Recursos padrão definidos pelo Bluetooth SIG usam números curtos de 16 bits, como 0x180F para o serviço de bateria e 0x2A6E para a característica de temperatura. Para os seus próprios dados, sem equivalente padrão, gere um UUID aleatório de 128 bits; em Python, import uuid; print(uuid.uuid4()) basta. Ao escolher o seu UUID, não use um valor que siga o padrão xxxxxxxx-0000-1000-8000-00805F9B34FB, porque esse padrão é reservado para os números de 16 bits do Bluetooth SIG. Se quiser funcionar sem atrito com apps existentes, é ainda mais prático usar UUIDs que todo mundo reconhece:
| Serviço / característica | UUID | Formato dos dados |
|---|---|---|
| Battery Service / Battery Level (bateria) | 0x180F / 0x2A19 | 1 byte, porcentagem (0–100) |
| Environmental Sensing / Temperature (temperatura) | 0x181A / 0x2A6E | Inteiro com sinal de 2 bytes, 0,01 °C |
| Environmental Sensing / Humidity (umidade) | 0x181A / 0x2A6F | Inteiro sem sinal de 2 bytes, 0,01 % |
| Heart Rate / Heart Rate Measurement (frequência cardíaca) | 0x180D / 0x2A37 | Byte de flags + valor da frequência cardíaca |
| Nordic UART Service (NUS) | 6E400001-B5A3-F393-E0A9-E50E24DCCA9E | RX 6E400002 (escrita), TX 6E400003 (notificação) |
Servidor BLE no ESP32: controlando um LED pelo celular (Nordic UART)
No nosso primeiro projeto BLE, transformamos o ESP32 em um servidor Nordic UART Service (NUS). O NUS é um serviço de “porta serial sobre BLE” definido pela Nordic Semiconductor que virou um padrão de fato: o celular escreve na característica RX, e o ESP32 envia notificações pela característica TX. A vantagem é que muitos apps prontos o reconhecem; o nRF Connect, a aba Bluetooth LE do Serial Bluetooth Terminal e o Bluefruit Connect no iPhone funcionam todos com o mesmo código. Assim, o código desta seção funciona com Android e com iPhone, e também em placas só com BLE, como o ESP32-S3 e o C3. A biblioteca BLE faz parte do pacote de placas ESP32, então não há nada extra para instalar.
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}A parte mais importante do código são as classes de callback. onConnect e onDisconnect acompanham o estado da conexão, e onWrite pega o valor que o celular escreve na característica RX. Essas funções rodam na tarefa própria da pilha Bluetooth, então, em vez de fazer trabalhos longos dentro delas, só mudamos uma variável; o loop() faz o trabalho de verdade. O descritor BLE2902 acrescentado à característica TX é necessário para o celular poder ligar as notificações. Quando a conexão cai, o ESP32 não volta a anunciar sozinho; sem a linha startAdvertising() no loop(), o dispositivo não pode ser encontrado de novo depois da primeira conexão. As linhas setMinPreferred e setMaxPreferred acrescentam ao anúncio o intervalo de conexão preferido, o que facilita a conexão com iPhones.
Testando o BLE com o nRF Connect passo a passo
- Instale no celular o app gratuito nRF Connect for Mobile, da Nordic Semiconductor; ele existe para Android e iPhone.
- No Android, o app pede permissão de Bluetooth e de Dispositivos por perto (no Android 11 e anteriores, permissão de localização com os serviços de localização ligados); sem ela, a lista de varredura fica vazia.
- Na aba Scanner, toque em SCAN e encontre BlueGrays-BLE na lista. O valor RSSI ao lado é a intensidade do sinal: -50 dBm significa muito perto, -90 dBm é o limite do alcance.
- Toque em CONNECT. A tela que abre mostra o Nordic UART Service; se você tivesse usado UUIDs gerados por você, apareceria “Unknown Service” aqui.
- Toque no ícone de seta tripla para baixo ao lado da característica TX (6E400003) para ligar as notificações.
- Toque no ícone de seta para cima ao lado da característica RX (6E400002), escolha Text (UTF-8) como tipo de dado, digite 1 e envie. O LED acende e aparece “LED acik” na linha TX.
Este teste é o jeito mais rápido de provar que o lado do ESP32 funciona antes de escrever um app de celular. Se algo der errado, tente primeiro o nRF Connect: se o dispositivo não estiver na lista, o problema é o anúncio; se ele conecta mas o serviço não aparece, falta a chamada start() do serviço; se você escreve um valor e o LED continua apagado, confira a comparação dentro do onWrite. Você também pode testar o mesmo código pela aba Bluetooth LE do Serial Bluetooth Terminal; o app reconhece o serviço NUS e você conversa como no Bluetooth Classic. Se quiser criar o seu próprio app Android, dê uma olhada em App Inventor e Bluetooth no Arduino.
Transmitindo dados de sensor por BLE (DHT22)

| Pino do DHT22 | ESP32 | Observação |
|---|---|---|
| VCC (+) | 3V3 | Funciona de 3,3 a 5 V |
| DATA (OUT) | GPIO 4 | No sensor sem módulo, coloque um resistor de 10 kΩ entre DATA e 3V3 |
| GND (−) | GND | GND comum |
Neste exemplo, usamos o serviço padrão Environmental Sensing (0x181A) e as características de temperatura (0x2A6E) e umidade (0x2A6F). A beleza dos UUIDs padrão é que apps como o nRF Connect conseguem reconhecer o valor e mostrá-lo direto como “23.45 °C”. O padrão pede que os valores sejam enviados não como texto, mas como inteiros multiplicados por 100: uma temperatura de 23,45 °C vira o número 2345 e é enviada em 2 bytes, com o byte menos significativo primeiro (little-endian). Para ler o sensor, usamos a DHT sensor library da Adafruit, que já apresentamos no guia do DHT11; você pode instalá-la pelo Gerenciador de Bibliotecas.
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}O código faz uma leitura a cada dois segundos, porque o DHT22 não pode ser lido com mais frequência. Os valores são escritos primeiro na característica com setValue(), então o celular também recebe a última medição quando faz uma leitura (READ), sem esperar uma notificação. Se o celular estiver conectado, notify() envia o novo valor na hora. Se o nRF Connect não conseguir decodificar o valor, você vai ver os bytes crus: para 23,45 °C, a tela mostra (0x) 29-09. Inverta os bytes e você tem 0x0929, que é 2345 em decimal; divida por 100 e você tem a temperatura. Você vai precisar fazer a mesma conta quando escrever o seu próprio app. Enviar o valor como texto pode parecer mais fácil, mas o formato padrão usa menos bytes e funciona com os apps existentes.
Bluetooth do ESP32 com o iPhone (iOS)
A pergunta que os usuários de iPhone mais fazem é por que o ESP32 não aparece nas configurações de Bluetooth. A Apple libera o perfil de porta serial do Bluetooth Classic (SPP) só para acessórios com certificação MFi, então não dá para se conectar pelo iPhone a um ESP32 rodando o BluetoothSerial. A solução é o BLE, e os exemplos de NUS e de sensor acima funcionam bem no iPhone. Há algumas coisas a lembrar:
- Buscar nos Ajustes: dispositivos BLE não aparecem nos ajustes de Bluetooth do iOS; faça a conexão dentro de um app como nRF Connect, LightBlue ou Bluefruit Connect.
- Cache: o iOS lembra o nome e a lista de serviços do dispositivo. Se você ainda vê a lista antiga depois de mudar os serviços no código, desligue e ligue de novo o Bluetooth do celular.
- Sem endereço MAC: o iOS não mostra aos apps o endereço MAC do dispositivo e dá a cada dispositivo um identificador próprio; se você usa vários ESP32, dê um nome diferente a cada um.
- Intervalo de conexão: não apague as linhas setMinPreferred(0x06) e setMaxPreferred(0x12); os exemplos da Espressif também as recomendam contra problemas de conexão com iPhone.
- O seu próprio app: no iOS você usa o Core Bluetooth com Swift; não é preciso parear, e o app procura direto pelo UUID do serviço.
BLE com o Raspberry Pi Pico W e MicroPython

Na família Raspberry Pi Pico, o Bluetooth só existe no Pico W e no Pico 2 W. O chip CYW43439 da placa suporta Bluetooth Classic e BLE no hardware, mas o MicroPython usa só o BLE (a partir da versão 1.20). O código abaixo é o equivalente em MicroPython do exemplo NUS do ESP32; como usa os mesmos UUIDs, você testa do mesmo jeito com o nRF Connect e o Serial Bluetooth Terminal. É preciso ter gravado o firmware MicroPython feito para o Pico W. O código também lê o sensor de temperatura interno da placa e o envia ao celular conectado a cada 2 segundos.
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)Como o módulo bluetooth do MicroPython é de baixo nível, montamos o pacote de anúncio nós mesmos. Cada campo é formado por um byte de tamanho, um byte de tipo e os dados: 0x01 são os flags, 0x09 o nome completo do dispositivo e 0x07 o UUID de serviço de 128 bits. Como um pacote de anúncio cabe no máximo 31 bytes, colocamos o campo de UUID de 18 bytes na resposta de varredura (resp_data). gatts_register_services() retorna os handles das características; usamos esses handles para ler o valor que o celular escreveu e para enviar notificações. Se preferir um código mais curto, você pode instalar com o mip a biblioteca aioble, baseada em asyncio. Para rodar o código no ESP32 com MicroPython, basta mudar a linha do LED para Pin(2, Pin.OUT) e ler, por exemplo, um DHT11 no lugar do sensor de temperatura interno.
Wi-Fi e Bluetooth juntos: memória, alcance e energia
No ESP32, o Wi-Fi e o Bluetooth dividem o mesmo rádio de 2,4 GHz. O chip roda os dois por divisão de tempo, então dá para usar ao mesmo tempo, mas cada um fica um pouco mais lento, e o Bluetooth Classic com tráfego pesado de Wi-Fi é o cenário mais difícil. Mas o problema real geralmente é a memória: a pilha Bluetooth é grande, e junto com o Wi-Fi o programa pode passar da partição de app padrão de 1,2 MB (1.310.720 bytes); só o exemplo BLE deste guia já ocupa uns 80% desse espaço. Se a compilação terminar com "Sketch too big" ou "text section exceeds available space in board" e você não usa atualizações pelo ar (OTA), escolha Huge APP (3MB No OTA/1MB SPIFFS) em Ferramentas > Partition Scheme. A biblioteca NimBLE-Arduino, que usa menos flash e RAM, também é uma boa alternativa.
O alcance depende do desenho da antena da placa e do ambiente. Com um ESP32 com antena de PCB na placa, espere 10–20 metros em ambiente fechado, dependendo das paredes, e mais em espaço aberto; módulos com conector para antena externa (por exemplo, o ESP32-WROOM-32U) vão ainda mais longe. Cobrir a antena com uma caixa metálica, uma bateria ou cabos reduz muito o alcance. Como a corrente sobe por um instante quando o rádio liga, um cabo USB fraco pode fazer a placa reiniciar com a mensagem “Brownout detector was triggered”; as soluções estão em brownout e Guru Meditation no ESP32.
Ideias de projetos com Bluetooth no ESP32
- Carrinho robô Bluetooth: passe os comandos F, B, L e R recebidos pelo BluetoothSerial para um driver de motor L298N; para a mecânica, veja como fazer um carrinho robô com Arduino.
- Tomada inteligente: troque o LED do exemplo NUS por um módulo relé e acenda uma lâmpada pelo iPhone.
- Termômetro sem fio: acrescente um display OLED ao exemplo do DHT22 para o valor aparecer na tela e no celular.
- Luz ambiente colorida: mude a cor de uma fita WS2812B NeoPixel com valores R, G e B enviados pelo celular.
- O seu próprio app Android: crie em poucas horas um app de controle remoto com botões e controles deslizantes no App Inventor.
- Comunicação entre dois ESP32: para fazer duas placas conversarem sem celular, experimente o ESP-NOW, mais simples e mais rápido, em vez do Bluetooth.
Problemas comuns de Bluetooth no ESP32 e soluções
| Sintoma / erro | Causa | Solução |
|---|---|---|
| "Serial Bluetooth not available or not enabled" (Bluetooth serial indisponível) | A placa selecionada é um ESP32-S3, C3, C6 ou H2, que não tem Bluetooth Classic | Use um ESP32 clássico ou passe para o exemplo BLE (NUS) |
| "conversion from 'String' to non-scalar type 'std::string' requested" (erro de conversão de tipo) | Código escrito para o core 2.x (std::string) compilado em um core 3.x | Atribua getValue().c_str() a uma variável String |
| "Sketch too big" (sketch grande demais) | Bluetooth e Wi-Fi juntos passaram da partição de app | Partition Scheme: escolha Huge APP ou use o NimBLE-Arduino |
| O iPhone não encontra o ESP32 | O Bluetooth Classic (SPP) não é suportado no iOS | Use BLE e conecte de dentro de um app |
| O dispositivo some depois da primeira conexão | O anúncio não foi reiniciado depois da desconexão | Chame startAdvertising() depois de uma desconexão |
| Nenhuma notificação chega | As notificações não foram ligadas no celular, ou falta o BLE2902 | Toque na seta tripla no nRF Connect; acrescente o BLE2902 à característica |
| "read failed, socket might closed or timeout" (falha na leitura) | Dados de pareamento antigos, ou outro celular já está conectado | Esqueça o dispositivo no celular e pareie de novo; o SPP aceita só uma conexão por vez |
| O fim de uma mensagem longa é cortado | O MTU padrão é de 23 bytes, ou seja, 20 bytes de dados por notificação | Divida a mensagem em pedaços de 20 bytes ou permita um MTU maior com BLEDevice::setMTU(185) |
| As mudanças no serviço não aparecem no celular | O celular guarda a tabela GATT antiga no cache | Desligue e ligue o Bluetooth do celular; no Android, esqueça o dispositivo |
Com o Bluetooth do ESP32, transformar o celular em controle remoto e o ESP32 em um sensor sem fio ou dispositivo inteligente leva só algumas linhas de código. O BluetoothSerial é a escolha certa para começar rápido com Android, e o BLE para suporte a iPhone e baixo consumo. Se quiser continuar com projetos sem fio de ESP32, o app BlueGrays tem projetos como LEDs, LEDs RGB, medição de temperatura com o DHT22 e um carrinho controlado por Bluetooth, cada um com esquema do circuito passo a passo e código pronto para usar.
Perguntas frequentes
O ESP32 tem Bluetooth?
Sim. O chip ESP32 original suporta Bluetooth Classic (BR/EDR) e BLE com Bluetooth 4.2 e não precisa de módulo extra. O ESP32-S3, C3, C6 e H2 suportam só BLE, e o ESP32-S2 não tem Bluetooth nenhum.
Qual é o alcance do Bluetooth do ESP32?
Com um ESP32 com antena de PCB na placa, você geralmente consegue 10–20 metros em ambiente fechado; paredes, objetos metálicos e a orientação da antena afetam essa distância. O alcance aumenta em espaço aberto, e módulos com antena externa (por exemplo, o WROOM-32U) vão ainda mais longe.
O ESP32 consegue se conectar a um iPhone por Bluetooth?
Sim, por BLE, mas não pelo Bluetooth Classic (BluetoothSerial), porque o iOS libera o perfil de porta serial (SPP) só para acessórios com certificação MFi. No iPhone, você pode se conectar ao serviço BLE do ESP32 com um app como nRF Connect, LightBlue ou Bluefruit Connect.
O BluetoothSerial funciona no ESP32-S3 e no ESP32-C3?
Não. Esses chips não têm hardware de Bluetooth Classic, então o BluetoothSerial não compila. Para ter a mesma lógica de porta serial sobre BLE, você pode usar o Nordic UART Service (NUS); apps como o Serial Bluetooth Terminal o reconhecem.
O ESP32 consegue usar Wi-Fi e Bluetooth ao mesmo tempo?
Sim, os dois funcionam juntos dividindo no tempo o mesmo rádio de 2,4 GHz. Mas a velocidade cai um pouco e o programa fica maior; se aparecer o erro “Sketch too big”, escolha Huge APP em Partition Scheme ou use a biblioteca NimBLE-Arduino, mais leve.
Preciso de um HC-05 com o ESP32?
Geralmente não. A biblioteca BluetoothSerial no ESP32 clássico faz internamente o trabalho do HC-05 e funciona com os mesmos apps de terminal. Você só precisa de um HC-05 em placas sem Bluetooth (Arduino Uno, ESP8266, o Pico padrão, ESP32-S2) ou se um projeto exigir Bluetooth Classic e você estiver usando um ESP32 só com BLE.
Projetos para montar depois desta leitura
Ver todos os projetos
LED integrado do ESP32 piscando
Faça o LED azul integrado do ESP32 DevKit V1 piscar. Nenhum componente externo é necessário! O primeiro projeto para experimentar ao comprar um ESP32. GPIO 2 está conectado ao LED integrado.

LED piscando com ESP32
Ensina o uso básico de uma saída GPIO no ESP32. Fazemos um LED piscar ligando e desligando um pino digital. É a versão para ESP32 do clássico projeto blink do Arduino UNO: os comandos básicos de blink no ambiente Arduino são os mesmos, mas a disposição dos pinos e os níveis de 3,3 V dos GPIOs do ESP32 diferem dos do Arduino UNO.

Controle de LED com botão no ESP32 (alternância)
Cada vez que o botão é pressionado, o estado do LED muda: se estiver aceso, apaga; se estiver apagado, acende. É o padrão clássico de alternância. Com debounce, eliminamos o ruído mecânico dos contatos do botão para garantir uma única mudança a cada pressionamento.

Controle de LED pelo celular via Bluetooth
Um pequeno ensaio para acender e apagar as luzes do seu quarto pelo celular. O módulo Bluetooth repassa ao Arduino as letras enviadas pelo celular; uma letra acende o LED vermelho, outra acende o verd

Carrinho controlado por Bluetooth com L298N
Transforme seu celular em um controle remoto e dirija seu próprio carrinho robô. O Arduino recebe os comandos de um aplicativo de controle remoto no celular por meio de um módulo Bluetooth e aciona as

Controle de LED RGB com ESP32
Gere inúmeras cores com um único LED RGB em vez de 3 LEDs separados. Como no Arduino, ajustamos os valores R/G/B com analogWrite() para criar uma transição de arco-íris. Você vê a teoria das cores na prática, como vermelho + verde = amarelo.

Temperatura e umidade com ESP32 e DHT22 (com display LCD)
Este projeto mede a temperatura e a umidade do ambiente com um sensor DHT22 e exibe os dados em tempo real em um LCD I2C 16x2. Os dados são lidos do sensor pelo protocolo de um fio (one-wire) com a biblioteca Adafruit DHT. Este projeto serve de base para estações meteorológicas IoT, automação residencial ou sistemas de monitoramento ambiental em estufas. A comunicação com o DHT22 é feita pelo pino GPIO 4 do ESP32.

Controle de fita WS2812B NeoPixel com ESP32
Os LEDs endereçáveis WS2812B (NeoPixel) são LEDs RGB inteligentes que podem ser controlados em cadeia por um único pino de dados. Neste projeto, vamos controlar uma fita WS2812B de 8 LEDs com a biblioteca Adafruit_NeoPixel para criar uma animação suave de arco-íris. Embora o nível lógico de 3,3V do ESP32 funcione sem problemas na maioria dos lotes de WS2812B, vamos mostrar uma solução com um resistor de 470Ω em série e um capacitor de 1.000µF para casos de instabilidade.
Mais artigos
App BlueGrays
Monte este circuito passo a passo pelo celular
Esquema do circuito, código e lista de componentes em uma só tela. Mais de 200 projetos com Arduino, ESP32 e Pico prontos para montar.
Escaneie com seu celular
Aponte a câmera para o código para abrir a loja de aplicativos do seu celular.
ou abrir a loja

