esp32 nodemcu iot com wifi e bluetooth 30 pinos com cp2102 Saravati

Como Fazer um Localizador de Celular com ESP32, Bluetooth BLE, OLED e Buzzer

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:

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:

OLEDESP32
VCC3V3
GNDGND
SDAGPIO21
SCLGPIO22

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 estimadaIntervalo
2,0 m ou mais600 ms
1,5 m450 ms
1,0 m300 ms
0,5 m150 ms
0,3 m90 ms
0,2 m60 ms
0,1 m30 ms
~5 cm20 ms
muito próximo15 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:

  1. abra o nRF Connect;
  2. abra Advertiser;
  3. ative CELULAR_FELIPE;
  4. deixe Connectable desativado.

No localizador:

  1. ligue o ESP32;
  2. espere o OLED inicializar;
  3. caminhe pelo ambiente;
  4. observe o aumento da barra;
  5. 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.

@saravatirobotica no Instagram e Tiktok

@sara.educacao no Instagram e Tiktok

Leave a Reply

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *