Guias · 17 min de leitura

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?.

Placa de desenvolvimento ESP32 com Bluetooth e BLE
Placa de desenvolvimento ESP32: Wi-Fi, Bluetooth Classic e BLE ficam todos dentro do mesmo chip, então você não precisa de um módulo Bluetooth externo para se conectar a um celular.

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ísticaBluetooth Classic (SPP)Bluetooth Low Energy (BLE)
Biblioteca ArduinoBluetoothSerial (incluída no pacote ESP32)BLEDevice (incluída no pacote ESP32) ou NimBLE-Arduino
Modelo de dadosFluxo contínuo de bytes, como uma porta serialTabela GATT formada por serviços e características
Quantidade de dadosAlta, adequada a fluxos contínuosPacotes pequenos; 20 bytes por notificação por padrão
Consumo de energiaSempre alto enquanto conectadoMuito baixo; pode funcionar meses com uma bateria
PareamentoFeito nas configurações de Bluetooth do celularNão é necessário; a conexão é feita dentro do app
AndroidSuportadoSuportado
iPhone (iOS)Não suportadoSuportado
ESP32 compatíveisSó o ESP32 original (WROOM-32, WROVER)ESP32, S3, C3, C6 e H2 (não o S2)
Uso típicoTerminal serial, carrinho robô, substituto do HC-05Sensores, 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 / chipBluetooth ClassicBLEObservação
ESP32 (WROOM-32, WROVER, DevKit V1)SimSim (4.2)A única família ESP32 que roda o BluetoothSerial
ESP32-S2NãoNãoSó Wi-Fi
ESP32-S3NãoSim (Bluetooth 5)O BluetoothSerial não compila, use BLE
ESP32-C3NãoSim (Bluetooth 5)Placas BLE pequenas e baratas
ESP32-C6NãoSim (Bluetooth 5)Também tem Wi-Fi 6, Zigbee e Thread
ESP32-H2NãoSim (Bluetooth 5)Sem Wi-Fi; focado em Zigbee e Thread
ESP8266 (NodeMCU, Wemos D1)NãoNãoPrecisa de um HC-05 ou HM-10 para Bluetooth
Raspberry Pi Pico W / Pico 2 WNo hardwareSimO MicroPython suporta só BLE
Raspberry Pi Pico / Pico 2NãoNãoPrecisa de um módulo Bluetooth externo
Se você compilar um código com BluetoothSerial tendo um ESP32-S3, C3, C6 ou H2 selecionado no menu de placas, a linha de verificação dos exemplos da Espressif (e do nosso código abaixo) interrompe a compilação com "Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip." Isso não é problema de instalação nem de biblioteca; o chip simplesmente não tem hardware de Bluetooth Classic. Se você tem uma dessas placas, vá direto para o exemplo BLE (Nordic UART) abaixo.

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:

C++ (Arduino IDE)
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

  1. Depois de carregar, abra o Monitor Serial em 115200 baud e procure a mensagem “Eslesmeye hazir / Ready to pair” (pronto para parear).
  2. 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.
  3. Instale pelo Google Play o app Serial Bluetooth Terminal, de Kai Morich.
  4. No app, abra Devices no menu lateral e escolha BlueGrays-ESP32 na aba Bluetooth Classic.
  5. Toque no ícone de plugue na barra superior; quando aparecer “Connected” na tela, você está conectado.
  6. 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.
Pressione longamente os botões de macro M1 e M2 na parte de baixo do Serial Bluetooth Terminal e salve os comandos “1” e “0” para ligar e desligar o LED com um toque. A opção Send > Newline nas configurações do app é CR+LF por padrão; o trim() do código já remove o CR extra. Ligue um relé no lugar do LED e o mesmo código pode ligar uma lâmpada ou uma tomada; veja os detalhes em O que é um módulo relé?.

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.

TermoSignificadoExemplo neste guia
Advertising (anúncio)Pacote curto em que o dispositivo anuncia o nome e os serviçosAnúncio com o nome “BlueGrays-BLE”
ServiçoGrupo que reúne características relacionadasServiço Nordic UART, Environmental Sensing (0x181A)
CaracterísticaUm único valor que é lido ou escritoComando 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 mudaUma nova temperatura a cada 2 segundos
Descritor 0x2902 (CCCD)Chave que liga e desliga as notificaçõesBotão de seta tripla no nRF Connect
UUIDNúmero de identificação de serviços e características6E400001-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ísticaUUIDFormato dos dados
Battery Service / Battery Level (bateria)0x180F / 0x2A191 byte, porcentagem (0–100)
Environmental Sensing / Temperature (temperatura)0x181A / 0x2A6EInteiro com sinal de 2 bytes, 0,01 °C
Environmental Sensing / Humidity (umidade)0x181A / 0x2A6FInteiro sem sinal de 2 bytes, 0,01 %
Heart Rate / Heart Rate Measurement (frequência cardíaca)0x180D / 0x2A37Byte de flags + valor da frequência cardíaca
Nordic UART Service (NUS)6E400001-B5A3-F393-E0A9-E50E24DCCA9ERX 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.

C++ (Arduino IDE)
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.

O código deste artigo foi compilado com a versão 2.0.11 do core Arduino para ESP32 (pacote de placas). Nessa versão, funções BLE como getValue() retornam std::string, e a maioria dos exemplos da internet também usa esse tipo. A partir do core 3.0, as funções BLE passaram a usar o tipo String do Arduino, então um código escrito com std::string falha no 3.x com “conversion from 'String' to non-scalar type 'std::string' requested”. Se você atribuir o resultado a uma variável String e acrescentar .c_str() no fim, como no nosso código, a linha compila tanto no core 2.x quanto no 3.x.

Testando o BLE com o nRF Connect passo a passo

  1. Instale no celular o app gratuito nRF Connect for Mobile, da Nordic Semiconductor; ele existe para Android e iPhone.
  2. 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.
  3. 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.
  4. Toque em CONNECT. A tela que abre mostra o Nordic UART Service; se você tivesse usado UUIDs gerados por você, apareceria “Unknown Service” aqui.
  5. Toque no ícone de seta tripla para baixo ao lado da característica TX (6E400003) para ligar as notificações.
  6. 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)

Sensor de temperatura e umidade DHT22
O DHT22 mede a temperatura com resolução de 0,1 °C; como funciona com 3,3 V, ele se liga direto ao ESP32.
Pino do DHT22ESP32Observação
VCC (+)3V3Funciona de 3,3 a 5 V
DATA (OUT)GPIO 4No sensor sem módulo, coloque um resistor de 10 kΩ entre DATA e 3V3
GND (−)GNDGND 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.

C++ (Arduino IDE)
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.

Também é possível enviar dados sem conexão: o ESP32 pode pôr a leitura direto no campo de dados do fabricante do pacote de anúncio, e qualquer celular ou gateway por perto consegue lê-la sem se conectar. Esse método se chama beacon, e o formato aberto BTHome, reconhecido pelo Home Assistant, funciona do mesmo jeito. Se o ESP32 entrar em deep sleep depois de cada leitura, a duração da bateria chega a meses; veja como em deep sleep no ESP32.

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

Placa Raspberry Pi Pico
Na família Raspberry Pi Pico, só as placas com W no nome (Pico W, Pico 2 W) têm Bluetooth; o Pico padrão não tem conexão sem fio.

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.

MicroPython
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 / erroCausaSoluçã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 ClassicUse 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.xAtribua getValue().c_str() a uma variável String
"Sketch too big" (sketch grande demais)Bluetooth e Wi-Fi juntos passaram da partição de appPartition Scheme: escolha Huge APP ou use o NimBLE-Arduino
O iPhone não encontra o ESP32O Bluetooth Classic (SPP) não é suportado no iOSUse BLE e conecte de dentro de um app
O dispositivo some depois da primeira conexãoO anúncio não foi reiniciado depois da desconexãoChame startAdvertising() depois de uma desconexão
Nenhuma notificação chegaAs notificações não foram ligadas no celular, ou falta o BLE2902Toque 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á conectadoEsqueça o dispositivo no celular e pareie de novo; o SPP aceita só uma conexão por vez
O fim de uma mensagem longa é cortadoO MTU padrão é de 23 bytes, ou seja, 20 bytes de dados por notificaçãoDivida 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 celularO celular guarda a tabela GATT antiga no cacheDesligue e ligue o Bluetooth do celular; no Android, esqueça o dispositivo
Ao investigar problemas de conexão, escolha Debug ou Verbose em Ferramentas > Core Debug Level. O Monitor Serial passa a mostrar os registros de conexão, desconexão e erros da pilha Bluetooth, que revelam rápido se o problema está no celular ou no ESP32. Repetir o mesmo teste no celular com o nRF Connect também mostra se a causa é o seu próprio app.

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.

Ver todos os projetos
LED integrado do ESP32 piscando
#106Iniciante

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.

4.8(29)
53
LED piscando com ESP32
#103Iniciante

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.

4.7(37)
99
Controle de LED com botão no ESP32 (alternância)
#105Iniciante

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.

4.6(39)
57
Controle de LED pelo celular via Bluetooth
#34Iniciante
Premium

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

4.8(68)
89
Carrinho controlado por Bluetooth com L298N
#80Avançado
Premium

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

5.0(38)
65
Controle de LED RGB com ESP32
#111Iniciante

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.

4.6(29)
51
Temperatura e umidade com ESP32 e DHT22 (com display LCD)
#117Intermediário

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.

4.8(38)
56
Controle de fita WS2812B NeoPixel com ESP32
#126Intermediário
Premium

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.

4.9(19)
30