Índice:
Já imaginou construir um pequeno dispositivo capaz de ajudar a encontrar seu celular dentro de casa?
Neste projeto vamos utilizar um ESP32, um display OLED e um buzzer para criar um localizador de proximidade. O celular Android transmite continuamente um sinal Bluetooth Low Energy (BLE), enquanto o ESP32 monitora a intensidade desse sinal.
Quanto mais perto chegamos do celular, mais rapidamente o buzzer api de maneira semelhante ao sensor de estacionamento de um automóvel. Ao mesmo tempo, o display OLED mostra a intensidade do sinal e uma estimativa da distância.
O resultado é algo assim:
LOCALIZADOR BLE
~0.5 m
RSSI: -60 dBm
█████████░░░
PERTO
Enquanto isso:
Longe:
BIP……..BIP……..BIP
Mais perto:
BIP….BIP….BIP
Muito perto:
BIP.BIP.BIP.BIP
Encontrou:
BIPBIPBIPBIPBIPBIP
É um projeto interessante para aprender ESP32, Bluetooth BLE, RSSI, multitarefa com FreeRTOS, displays OLED e sistemas embarcados.
1. Como funciona o projeto
O funcionamento pode ser dividido em quatro partes:
CELULAR ANDROID
│
│ Advertising BLE
▼
ESP32
│
├──── mede RSSI
│
├──── estima proximidade
│
├──── OLED
│ └── mostra distância/sinal
│
└──── BUZZER
└── acelera ao aproximar
O celular não precisa estabelecer uma conexão Bluetooth com o ESP32.
Ele simplesmente fica transmitindo um pequeno pacote BLE chamado advertising.
O ESP32 procura especificamente pelo dispositivo:
CELULAR_FELIPE
Quando o encontra, obtém o RSSI.
2. O que é RSSI?
RSSI significa Received Signal Strength Indicator.
Basicamente, representa a intensidade do sinal recebido.
Podemos encontrar valores como:
-90 dBm → sinal muito fraco
-75 dBm → sinal fraco
-60 dBm → sinal forte
-50 dBm → sinal muito forte
-42 dBm → extremamente próximo
Existe um detalhe importante:
-42 dBm é MAIS FORTE que -75 dBm
Quanto mais próximo de zero, mais forte é o sinal.
Durante nossos testes com o celular utilizado neste projeto encontramos aproximadamente:
~2 metros → -75 dBm
~1 centímetro → -42 dBm
Esses valores serão utilizados como referência para nosso localizador.
3. A distância não é uma medição exata
É importante deixar isso claro.
Bluetooth RSSI não é um sensor de distância.
O sinal pode sofrer influência de:
- paredes;
- móveis;
- pessoas;
- orientação do celular;
- posição da antena;
- reflexões;
- interferências de outros dispositivos;
- ambiente.
Portanto, quando o display indicar:
~0.5 m
isso deve ser interpretado como uma estimativa de proximidade, e não como uma medição precisa de 50 centímetros.
Para nosso objetivo isso não é um grande problema.
Queremos principalmente saber:
Estou chegando mais perto ou mais longe do celular?
E o buzzer torna essa informação muito intuitiva.
4. Componentes necessários
Para montar o projeto utilizamos:
- ESP32 Dev Module;
- display OLED SSD1306 128×64 I²C;
- buzzer;
- celular Android;
- protoboard;
- jumpers;
- cabo USB.
No celular utilizaremos um aplicativo capaz de gerar BLE Advertising.
5. Ligações do OLED
O display utilizado é um SSD1306 I²C de 128×64 pixels.
Faça:
| OLED | ESP32 |
| VCC | 3V3 |
| GND | GND |
| SDA | GPIO21 |
| SCL | GPIO22 |
Esquematicamente:
OLED SSD1306 ESP32
VCC ─────────────── 3V3
GND ─────────────── GND
SDA ─────────────── GPIO21
SCL ─────────────── GPIO22
6. Ligação do buzzer
Neste projeto utilizamos:
GPIO19
para controlar o buzzer.
Ligação:
ESP32 BUZZER
GPIO19 ───────────── SINAL / +
GND ───────────── GND / –
Para um módulo buzzer com entrada lógica:
ESP32 MÓDULO BUZZER
GPIO19 ───────────── IN
GND ───────────── GND
VCC ───────────── alimentação adequada
Se o buzzer utilizado exigir uma corrente superior à que o GPIO pode fornecer com segurança, utilize um transistor ou módulo driver.
7. Preparando o celular Android
Precisamos transformar o Android em um BLE advertiser.
Neste projeto utilizamos o aplicativo nRF Connect for Mobile, da Nordic Semiconductor.
Abra o aplicativo e procure:
ADVERTISER
Crie um novo advertiser.
Utilizamos o nome:
CELULAR_FELIPE
Um detalhe importante encontrado durante nosso teste foi deixar:
Connectable = OFF
Com Connectable habilitado, o advertiser não iniciou no aparelho utilizado no projeto. Ao desabilitá-lo, o advertising começou normalmente.
Depois de configurado, ative a chave do advertiser.
O Android começará a transmitir.
8. Testando se o ESP32 encontra o Android
Antes de montar OLED e buzzer, é uma boa ideia testar apenas o Bluetooth.
O ESP32 deverá conseguir encontrar algo semelhante a:
Nome: CELULAR_FELIPE
RSSI: -55 dBm
Se o dispositivo não aparecer imediatamente, faça algumas varreduras.
Durante nossos testes, inicialmente apareceram diversos dispositivos sem nome antes de o CELULAR_FELIPE ser identificado.
9. Bibliotecas necessárias
Para o OLED utilizaremos:
Adafruit GFX Library
Adafruit SSD1306
Instale pelo Gerenciador de Bibliotecas da Arduino IDE.
Para BLE utilizaremos a biblioteca que já acompanha o pacote atual do ESP32:
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
Atenção a uma biblioteca BLE antiga
Durante o desenvolvimento encontramos um conflito importante.
Existia uma biblioteca antiga instalada manualmente em:
Documents\Arduino\libraries\ESP32_BLE_Arduino
e a Arduino IDE estava utilizando essa biblioteca em vez da biblioteca BLE fornecida pelo ESP32 Core.
Isso provocava erros de compilação.
Depois de remover a biblioteca antiga, a IDE passou a utilizar a versão correta presente no pacote do ESP32.
Se aparecer algo como:
Foram encontradas múltiplas bibliotecas
para “BLEDevice.h”
confira qual biblioteca está sendo utilizada.
10. Um problema importante: o buzzer ficava lento
A primeira versão funcionou, mas havia um problema.
Utilizávamos:
scanner->start(1, false);
O ESP32 realizava uma varredura BLE e, durante esse período, o fluxo principal ficava ocupado.
O resultado era algo parecido com:
BIP BIP BIP
pausa…
BIP BIP BIP
pausa…
Mesmo diminuindo o intervalo do buzzer para poucos milissegundos, ainda existia uma pausa grande entre os grupos.
O problema não estava no buzzer.
Estava no scan BLE bloqueando o programa.
11. A solução: multitarefa
O ESP32 utiliza FreeRTOS internamente e permite executar tarefas de maneira independente.
Criamos então uma tarefa exclusivamente para o buzzer:
TAREFA PRINCIPAL
│
├── BLE
├── RSSI
└── OLED
TAREFA BUZZER
│
└── BIP BIP BIP BIP…
Assim, mesmo enquanto o ESP32 está fazendo uma nova varredura Bluetooth, o buzzer continua utilizando a última distância conhecida.
Essa mudança fez uma diferença enorme no funcionamento.
Agora temos:
longe
BIP……BIP……BIP
aproximando
BIP….BIP….BIP
perto
BIP..BIP..BIP
muito perto
BIP.BIP.BIP.BIP
encontrou
BIPBIPBIPBIPBIPBIP
sem aquela pausa artificial entre as sequências.
12. Código completo
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// =====================================================
// CONFIGURACOES
// =====================================================
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define OLED_ADDRESS 0x3C
#define PIN_BUZZER 19
#define SDA_OLED 21
#define SCL_OLED 22
const char* NOME_CELULAR = "CELULAR_FELIPE";
// =====================================================
// OBJETOS
// =====================================================
Adafruit_SSD1306 display(
SCREEN_WIDTH,
SCREEN_HEIGHT,
&Wire,
OLED_RESET
);
BLEScan* scanner;
// =====================================================
// VARIAVEIS
// =====================================================
float rssiFiltrado = -100;
bool primeiraLeitura = true;
// Distancia utilizada pela tarefa do buzzer
volatile float distanciaAtual = 8.0;
// Informa se o celular foi localizado
volatile bool celularEncontrado = false;
// =====================================================
// FILTRO DO RSSI
// =====================================================
int filtrarRSSI(int novoRSSI) {
if (primeiraLeitura) {
rssiFiltrado = novoRSSI;
primeiraLeitura = false;
} else {
rssiFiltrado =
(rssiFiltrado * 0.60) +
(novoRSSI * 0.40);
}
return round(rssiFiltrado);
}
// =====================================================
// INTERPOLACAO
// =====================================================
float interpolar(
int rssi,
int rssi1,
float dist1,
int rssi2,
float dist2
) {
float proporcao =
(float)(rssi - rssi1) /
(float)(rssi2 - rssi1);
return dist1 +
proporcao *
(dist2 - dist1);
}
// =====================================================
// ESTIMATIVA DE DISTANCIA
// =====================================================
float calcularDistancia(int rssi) {
/*
CALIBRACAO UTILIZADA NO PROTOTIPO:
-42 dBm = aproximadamente 1 cm
-75 dBm = aproximadamente 2 metros
Os pontos intermediarios sao utilizados
para produzir uma estimativa de proximidade.
RSSI NAO E UMA MEDICAO PRECISA DE DISTANCIA.
*/
if (rssi >= -42)
return 0.01;
if (rssi >= -45)
return interpolar(
rssi,
-42, 0.01,
-45, 0.05
);
if (rssi >= -48)
return interpolar(
rssi,
-45, 0.05,
-48, 0.10
);
if (rssi >= -52)
return interpolar(
rssi,
-48, 0.10,
-52, 0.20
);
if (rssi >= -56)
return interpolar(
rssi,
-52, 0.20,
-56, 0.30
);
if (rssi >= -60)
return interpolar(
rssi,
-56, 0.30,
-60, 0.50
);
if (rssi >= -64)
return interpolar(
rssi,
-60, 0.50,
-64, 0.80
);
if (rssi >= -68)
return interpolar(
rssi,
-64, 0.80,
-68, 1.10
);
if (rssi >= -72)
return interpolar(
rssi,
-68, 1.10,
-72, 1.50
);
if (rssi >= -75)
return interpolar(
rssi,
-72, 1.50,
-75, 2.00
);
if (rssi >= -80)
return interpolar(
rssi,
-75, 2.00,
-80, 3.00
);
if (rssi >= -85)
return interpolar(
rssi,
-80, 3.00,
-85, 5.00
);
return 8.00;
}
// =====================================================
// VELOCIDADE DO BUZZER
// =====================================================
int calcularIntervaloBip(float distancia) {
/*
Quanto menor a distancia estimada,
menor o intervalo entre os bipes.
2,0 m -> 600 ms
1,5 m -> 450 ms
1,0 m -> 300 ms
0,5 m -> 150 ms
0,3 m -> 90 ms
0,2 m -> 60 ms
0,1 m -> 30 ms
*/
if (distancia >= 2.0)
return 600;
if (distancia <= 0.02)
return 15;
if (distancia <= 0.05)
return 20;
if (distancia <= 0.10)
return 30;
int intervalo =
round(distancia * 300.0);
if (intervalo < 15)
intervalo = 15;
if (intervalo > 600)
intervalo = 600;
return intervalo;
}
// =====================================================
// TAREFA INDEPENDENTE DO BUZZER
// =====================================================
void tarefaBuzzer(void* parameter) {
while (true) {
// Se nao encontrou celular,
// buzzer permanece desligado
if (!celularEncontrado) {
digitalWrite(
PIN_BUZZER,
LOW
);
vTaskDelay(
20 / portTICK_PERIOD_MS
);
continue;
}
// Copia ultima distancia conhecida
float distancia =
distanciaAtual;
int intervalo =
calcularIntervaloBip(
distancia
);
// Liga buzzer
digitalWrite(
PIN_BUZZER,
HIGH
);
// Duracao do bip
vTaskDelay(
10 / portTICK_PERIOD_MS
);
// Desliga
digitalWrite(
PIN_BUZZER,
LOW
);
// Aguarda ate o proximo bip
vTaskDelay(
intervalo /
portTICK_PERIOD_MS
);
}
}
// =====================================================
// BARRA DE SINAL
// =====================================================
int calcularBarra(int rssi) {
return map(
constrain(rssi, -90, -40),
-90,
-40,
0,
118
);
}
// =====================================================
// TEXTO DE PROXIMIDADE
// =====================================================
String textoProximidade(float distancia) {
if (distancia >= 3.0)
return "MUITO LONGE";
if (distancia >= 2.0)
return "LONGE";
if (distancia >= 1.0)
return "APROXIMANDO";
if (distancia >= 0.5)
return "PERTO";
if (distancia >= 0.10)
return "MUITO PERTO";
return "ENCONTREI!";
}
// =====================================================
// OLED
// =====================================================
void atualizarDisplay(
int rssi,
float distancia
) {
display.clearDisplay();
display.setTextColor(
SSD1306_WHITE
);
// TITULO
display.setTextSize(1);
display.setCursor(
19,
0
);
display.println(
"LOCALIZADOR BLE"
);
display.drawLine(
0,
10,
127,
10,
SSD1306_WHITE
);
// DISTANCIA
display.setTextSize(2);
if (distancia < 1.0) {
int centimetros =
round(
distancia * 100.0
);
display.setCursor(
20,
15
);
display.print("~");
display.print(
centimetros
);
display.print("cm");
} else {
display.setCursor(
18,
15
);
display.print("~");
display.print(
distancia,
1
);
display.print("m");
}
// RSSI
display.setTextSize(1);
display.setCursor(
0,
36
);
display.print(
"RSSI: "
);
display.print(
rssi
);
display.print(
" dBm"
);
// BARR
// =====================================================
// BARRA DE SINAL
// =====================================================
display.drawRect(
4,
46,
120,
8,
SSD1306_WHITE
);
int barra =
calcularBarra(rssi);
display.fillRect(
5,
47,
barra,
6,
SSD1306_WHITE
);
// =====================================================
// STATUS
// =====================================================
String status =
textoProximidade(
distancia
);
int largura =
status.length() * 6;
display.setCursor(
(128 - largura) / 2,
56
);
display.print(
status
);
display.display();
}
// =====================================================
// TELA DE PROCURA
// =====================================================
void telaProcurando() {
display.clearDisplay();
display.setTextColor(
SSD1306_WHITE
);
display.setTextSize(1);
display.setCursor(
19,
5
);
display.println(
"LOCALIZADOR BLE"
);
display.drawLine(
0,
16,
127,
16,
SSD1306_WHITE
);
display.setTextSize(2);
display.setCursor(
10,
25
);
display.println(
"PROCURANDO"
);
display.setTextSize(1);
display.setCursor(
19,
50
);
display.println(
"CELULAR_FELIPE"
);
display.display();
}
// =====================================================
// SETUP
// =====================================================
void setup() {
Serial.begin(
115200
);
delay(1000);
// ===================================================
// BUZZER
// ===================================================
pinMode(
PIN_BUZZER,
OUTPUT
);
digitalWrite(
PIN_BUZZER,
LOW
);
// ===================================================
// CRIA TAREFA INDEPENDENTE PARA O BUZZER
// ===================================================
xTaskCreatePinnedToCore(
tarefaBuzzer,
"Buzzer",
2048,
NULL,
1,
NULL,
1
);
// ===================================================
// OLED
// ===================================================
Wire.begin(
SDA_OLED,
SCL_OLED
);
if (!display.begin(
SSD1306_SWITCHCAPVCC,
OLED_ADDRESS)) {
Serial.println(
"Erro ao iniciar OLED!"
);
while (true);
}
display.clearDisplay();
display.setTextColor(
SSD1306_WHITE
);
display.setTextSize(1);
display.setCursor(
28,
20
);
display.println(
"LOCALIZADOR"
);
display.setCursor(
31,
35
);
display.println(
"INICIANDO..."
);
display.display();
// ===================================================
// BLUETOOTH BLE
// ===================================================
BLEDevice::init("");
scanner =
BLEDevice::getScan();
scanner->setActiveScan(
true
);
scanner->setInterval(
100
);
scanner->setWindow(
99
);
Serial.println();
Serial.println(
"============================"
);
Serial.println(
"LOCALIZADOR CELULAR FELIPE"
);
Serial.println(
"============================"
);
delay(1500);
}
// =====================================================
// LOOP
// =====================================================
void loop() {
Serial.println();
Serial.println(
"Atualizando sinal..."
);
// ===================================================
// SCAN BLE
// ===================================================
BLEScanResults* resultados =
scanner->start(
1,
false
);
bool encontrado =
false;
int melhorRSSI =
-100;
// ===================================================
// PROCURA PELO CELULAR
// ===================================================
for (
int i = 0;
i < resultados->getCount();
i++
) {
BLEAdvertisedDevice dispositivo =
resultados->getDevice(i);
if (
dispositivo.haveName()
) {
String nome =
dispositivo
.getName()
.c_str();
if (
nome ==
NOME_CELULAR
) {
encontrado =
true;
int rssi =
dispositivo
.getRSSI();
if (
rssi >
melhorRSSI
) {
melhorRSSI =
rssi;
}
}
}
}
// Limpa memoria dos resultados
scanner->clearResults();
// ===================================================
// CELULAR ENCONTRADO
// ===================================================
if (encontrado) {
int rssi =
filtrarRSSI(
melhorRSSI
);
float distancia =
calcularDistancia(
rssi
);
// =================================================
// ATUALIZA INFORMACOES PARA A TAREFA DO BUZZER
// =================================================
distanciaAtual =
distancia;
celularEncontrado =
true;
// =================================================
// MONITOR SERIAL
// =================================================
Serial.print(
"RSSI: "
);
Serial.print(
rssi
);
Serial.print(
" dBm | Distancia aproximada: "
);
if (
distancia < 1.0
) {
Serial.print(
distancia * 100.0,
0
);
Serial.print(
" cm"
);
} else {
Serial.print(
distancia,
2
);
Serial.print(
" m"
);
}
Serial.print(
" | Intervalo bip: "
);
Serial.print(
calcularIntervaloBip(
distancia
)
);
Serial.println(
" ms"
);
// =================================================
// ATUALIZA OLED
// =================================================
atualizarDisplay(
rssi,
distancia
);
}
// ===================================================
// CELULAR NAO ENCONTRADO
// ===================================================
else {
Serial.println(
"Celular nao encontrado."
);
celularEncontrado =
false;
primeiraLeitura =
true;
telaProcurando();
}
}
13. Gravando o programa no ESP32
Com todas as ligações realizadas, conecte o ESP32 ao computador.
Na Arduino IDE, selecione:
Ferramentas → Placa → ESP32 Dev Module
Depois selecione a porta COM correspondente ao ESP32.
Clique em Carregar.
Depois da gravação, abra:
Ferramentas → Monitor Serial
Configure para:
115200 baud
Com o advertiser do Android ligado, você deverá começar a encontrar informações semelhantes a:
Atualizando sinal…
RSSI: -72 dBm
Distancia aproximada: 1.50 m
Intervalo bip: 450 ms
Ao aproximar o celular:
RSSI: -60 dBm
Distancia aproximada: 50 cm
Intervalo bip: 150 ms
E muito próximo:
RSSI: -42 dBm
Distancia aproximada: 1 cm
Intervalo bip: 15 ms
14. Funcionamento do display
Quando o telefone é encontrado, o OLED apresenta três informações importantes.
Por exemplo:
LOCALIZADOR BLE
—————-
~1.5m
RSSI: -72 dBm
███████░░░░░
APROXIMANDO
Conforme o sinal aumenta:
LOCALIZADOR BLE
—————-
~30cm
RSSI: -56 dBm
██████████░░
MUITO PERTO
E praticamente em cima do telefone:
LOCALIZADOR BLE
—————-
~1cm
RSSI: -42 dBm
████████████
ENCONTREI!
A barra também aumenta conforme o RSSI fica mais forte.
15. Como funciona o buzzer
O buzzer é provavelmente a parte mais interessante do projeto.
Utilizamos a distância estimada para determinar o intervalo entre os bipes.
A lógica utilizada é aproximadamente:
| Distância estimada | Intervalo |
| 2,0 m ou mais | 600 ms |
| 1,5 m | 450 ms |
| 1,0 m | 300 ms |
| 0,5 m | 150 ms |
| 0,3 m | 90 ms |
| 0,2 m | 60 ms |
| 0,1 m | 30 ms |
| ~5 cm | 20 ms |
| muito próximo | 15 ms |
Na prática, o efeito sonoro fica assim:
LONGE
BIP………BIP………BIP
APROXIMANDO
BIP…..BIP…..BIP
PERTO
BIP…BIP…BIP
MUITO PERTO
BIP.BIP.BIP.BIP.BIP
ENCONTROU
BIPBIPBIPBIPBIPBIPBIP
Isso permite procurar o telefone praticamente sem precisar olhar para o OLED.
Você simplesmente caminha pelo ambiente.
Se o bip desacelerar, provavelmente está indo na direção errada.
Se acelerar, está se aproximando.
16. Por que usamos um filtro no RSSI?
O sinal Bluetooth não é estável.
Mesmo deixando o celular completamente parado, podemos receber:
-61 dBm
-66 dBm
-62 dBm
-68 dBm
-63 dBm
Se utilizássemos cada leitura diretamente, a indicação do OLED e a velocidade do buzzer mudariam excessivamente.
Por isso usamos:
rssiFiltrado =
(rssiFiltrado * 0.60) +
(novoRSSI * 0.40);
Isso significa que o novo valor considera:
60% → histórico anterior
40% → nova leitura
O resultado é uma resposta mais suave.
17. Calibrando para outro celular
Os valores utilizados neste projeto foram obtidos experimentalmente com o aparelho usado durante o desenvolvimento.
Encontramos aproximadamente:
-42 dBm → praticamente encostado
-75 dBm → aproximadamente 2 metros
Outro celular provavelmente apresentará valores diferentes.
Isso acontece porque diferentes smartphones possuem:
- antenas diferentes;
- potências de transmissão diferentes;
- posicionamentos diferentes da antena;
- hardware Bluetooth diferente.
Para calibrar outro aparelho, coloque-o em algumas distâncias conhecidas.
Por exemplo:
1 cm
10 cm
50 cm
1 m
2 m
Observe o RSSI médio em cada posição.
Depois altere a função:
calcularDistancia()
com os novos valores.
Na verdade, para o objetivo de encontrar o celular, a indicação sonora baseada na tendência do RSSI é mais importante do que o número de metros apresentado.
18. Por que não utilizamos o endereço MAC?
Durante a configuração do Android, o nRF Connect estava utilizando:
RANDOM ADDRESS
Dispositivos modernos podem utilizar endereços Bluetooth aleatórios por questões de privacidade.
Por isso, neste protótipo procuramos pelo nome:
const char* NOME_CELULAR =
“CELULAR_FELIPE”;
e verificamos:
if (
nome ==
NOME_CELULAR
)
Para uma versão mais robusta do projeto, uma alternativa interessante seria anunciar um Service UUID específico e procurar por esse UUID no ESP32.
19. Como utilizar o localizador
O procedimento completo fica muito simples.
No Android:
- abra o nRF Connect;
- abra Advertiser;
- ative CELULAR_FELIPE;
- deixe Connectable desativado.
No localizador:
- ligue o ESP32;
- espere o OLED inicializar;
- caminhe pelo ambiente;
- observe o aumento da barra;
- acompanhe os bipes.
A lógica de procura é quase como um jogo:
BIP lento
↓
ande em uma direção
BIP ficou mais rápido?
↓
SIM → continue
BIP ficou mais lento?
↓
volte e tente outra direção
BIP muito rápido?
↓
celular está próximo
20. Limitações do projeto
Esse localizador não possui a mesma tecnologia utilizada por rastreadores UWB profissionais.
Ele utiliza intensidade de sinal Bluetooth, portanto não consegue informar direção.
Ele não sabe:
← celular está à esquerda
→ celular está à direita
Ele sabe apenas:
sinal ficando mais forte
ou:
sinal ficando mais fraco
Por isso o usuário precisa se movimentar para descobrir a direção.
Também não devemos interpretar:
~30 cm
como exatamente 30 centímetros.
Pode fisicamente ser 20 cm, 50 cm ou até mais dependendo do ambiente.
21. Por que esse projeto funciona para encontrar objetos?
Apesar das limitações do RSSI, o conceito é muito interessante porque nosso cérebro consegue utilizar facilmente a informação sonora.
É exatamente a mesma lógica de vários instrumentos de busca:
sinal fraco
→ poucos bipes
sinal aumentando
→ bipes mais rápidos
sinal muito forte
→ som quase contínuo
Em vez de tentar transformar Bluetooth em um instrumento de distância de alta precisão, estamos utilizando-o como indicador de proximidade.
Isso combina muito melhor com as características reais do BLE.
22. Possíveis evoluções
Depois que o projeto básico estiver funcionando, existem várias possibilidades.
Podemos criar um pequeno dispositivo portátil alimentado por bateria.
Também podemos substituir o OLED por um display maior e criar uma interface estilo radar.
Outra evolução seria acrescentar LEDs:
VERDE → longe
AMARELO → aproximando
VERMELHO → muito perto
Também seria possível procurar diferentes objetos:
CELULAR_FELIPE
CHAVE_CARRO
MOCHILA
CONTROLE_TV
Cada objeto precisaria transmitir algum identificador BLE.
Uma versão mais avançada poderia ter um botão para selecionar qual dispositivo procurar.
23. Evolução com UWB
Se o objetivo fosse medir distância com precisão muito maior, a tecnologia mais indicada seria Ultra-Wideband (UWB).
Com módulos UWB apropriados é possível realizar ranging entre dois dispositivos.
A arquitetura poderia ser:
LOCALIZADOR
ESP32 + UWB
│
│ ranging
▼
TAG UWB
│
▼
OBJETO
Mas isso aumenta significativamente a complexidade e exige hardware adicional.
Para um projeto didático, o BLE tem uma enorme vantagem: o Android já possui Bluetooth, então conseguimos transformar o próprio celular no transmissor.
24. O que aprendemos neste projeto?
Apesar de o resultado parecer simples, utilizamos diversos conceitos importantes.
Bluetooth Low Energy
Aprendemos a trabalhar com advertising e scanning BLE.
RSSI
Utilizamos intensidade de sinal para inferir proximidade.
Conversão de dados
Transformamos RSSI em uma estimativa visual de distância.
Filtragem
Aplicamos um filtro para reduzir oscilações.
I²C
Utilizamos o barramento I²C para controlar o OLED SSD1306.
Interface gráfica
Criamos uma barra de intensidade, distância e estados de proximidade.
FreeRTOS
Criamos uma tarefa independente para impedir que o scan BLE interrompesse o funcionamento do buzzer.
Esse último ponto é particularmente interessante porque mostra uma vantagem importante do ESP32: executar diferentes tarefas de maneira concorrente.
25. Conclusão
Neste projeto construímos um localizador de celular utilizando ESP32 e Bluetooth Low Energy.
O Android transmite um advertising BLE chamado:
CELULAR_FELIPE
O ESP32 procura esse dispositivo e mede continuamente a intensidade do sinal recebido.
O OLED transforma essa informação em uma indicação visual de proximidade, enquanto o buzzer cria uma experiência semelhante a um sensor de estacionamento:
longe
BIP………BIP
↓
aproximando
BIP…..BIP
↓
perto
BIP..BIP
↓
muito perto
BIP.BIP.BIP
↓
encontrou
BIPBIPBIPBIPBIP
Durante o desenvolvimento descobrimos também um detalhe importante: simplesmente reduzir o intervalo do buzzer não era suficiente. O scan Bluetooth bloqueava o fluxo principal e criava grandes pausas entre os bipes.
A solução foi utilizar uma tarefa independente no FreeRTOS, permitindo que o ESP32 continue procurando o celular enquanto o buzzer funciona simultaneamente.
O resultado final é um dispositivo simples, mas que reúne eletrônica, programação, Bluetooth, IoT e sistemas embarcados em uma aplicação prática e bastante visual.
E a mesma ideia pode ser expandida para localizar outros objetos: basta que eles possuam algum dispositivo capaz de transmitir um identificador BLE.
Saiba mais sobre a parceria Saravati e Sara Educação
Este “Guia de Montagem” é uma colaboração especial entre a Saravati e a Sara Educação, criado pelo Professor Felipe Rosa. Nosso objetivo é enriquecer a comunidade de entusiastas da eletrônica, IoT e automação com recursos educacionais de alta qualidade. Através dessa parceria, buscamos inspirar e capacitar criadores em seus projetos, disponibilizando esses guias em nosso blog e nas redes sociais.
