Conteúdo detalhado
🗣️ Como um sensor "fala" com a placa
Antes de ligar qualquer sensor, descubra em que "idioma" ele entrega o dado. São quatro: digital (sim ou não, como "tem obstáculo"), analógico (uma tensão que varia, como a refletância do chão), largura de pulso (o dado está na duração de um pulso, como no HC-SR04) e barramento (o sensor manda números prontos por um protocolo, como o I2C). O idioma decide o pino, a função de leitura e o cuidado elétrico.
🌱 Novo aqui?
Um módulo sensor é o sensor já montado numa plaquinha com resistores e, às vezes, um comparador que transforma o analógico em digital. A folha de dados (datasheet) é o documento do fabricante com tensão de trabalho, pinos e tempos. Um barramento é um par de fios compartilhado por vários dispositivos, cada um com seu endereço.
Como ler: cada quadro é um idioma, com a função do Arduino que o entende no canto. Em cima (verde), o valor está no nível da tensão. Embaixo (ciano), o valor está no TEMPO: na duração do pulso ou na sequência de bits. Os de baixo exigem que o programa espere ou converse, por isso aparecem de novo no tópico 6.
| Sensor do kit | Idioma | Alimentação | Cuidado |
|---|---|---|---|
| HC-SR04 | largura de pulso | 5 V | ECHO volta em 5 V: divisor |
| IR de obstáculo | digital | 3,3 V | ajustar o trimpot |
| TCRT5000 (linha) | analógico (AO) ou digital (DO) | 3,3 V | usar pino do ADC1 |
| VL53L0X, MPU-6050 | I2C | 3,3 V (módulo) | endereços diferentes |
Por que aprender
Sabendo o idioma, qualquer sensor novo vira uma pergunta rápida: qual pino, qual função, qual tensão. Você para de depender de um tutorial para cada peça e passa a ler a folha de dados.
Conceitos-chave
🦇 HC-SR04: distância por eco
O HC-SR04 funciona como um morcego. Você manda um pulso curto no pino TRIG; ele emite um chiado de ultrassom (40 kHz, que você não ouve) e deixa o pino ECHO em nível alto até o eco voltar. A duração desse pulso é o tempo de ida e volta. Como o som anda cerca de 343 m/s (0,0343 cm por microssegundo), a distância é duração × 0,0343 ÷ 2, dividindo por 2 porque o som foi e voltou.
Como ler: em cima, o caminho do som até o obstáculo e de volta. No meio, os dois sinais no tempo: o pulso curto do TRIG e o pulso longo do ECHO, cuja largura é o que você mede. À direita, a proteção: o ECHO sai em 5 V e passa pelo divisor 1 kΩ/2 kΩ, que entrega cerca de 3,3 V ao GPIO 18. O TRIG pode ir direto: 3,3 V do ESP32 é suficiente para disparar.
Montagem em três passos
Alimentação
VCC no 5 V (pino VIN/5V do DevKit alimentado por USB) e GND no GND comum. A versão HC-SR04P aceita 3,3 V e dispensa o divisor; confira a sua.
TRIG direto
TRIG no GPIO 5, com jumper simples.
ECHO pelo divisor
ECHO → 1 kΩ → ponto do meio → GPIO 18; do ponto do meio, 2 kΩ até o GND. Conta do módulo 1.2: 5 × 2 ÷ 3 ≈ 3,3 V. No Wokwi o divisor não é necessário, mas monte para treinar.
Objetivo: medir a distância cinco vezes por segundo e mostrar em centímetros, avisando quando não houver eco. No Wokwi, adicione "HC-SR04" e clique nele durante a simulação para mudar a distância.
// HC-SR04: distância por eco (ESP32, pacote esp32 3.x)
const int PINO_TRIG = 5;
const int PINO_ECHO = 18; // pelo divisor 1k/2k no hardware real
const unsigned long TIMEOUT_US = 30000; // ~5 m: sem eco depois disso
float medirCm() {
digitalWrite(PINO_TRIG, LOW); delayMicroseconds(2);
digitalWrite(PINO_TRIG, HIGH); delayMicroseconds(10); // pulso de disparo
digitalWrite(PINO_TRIG, LOW);
unsigned long dur = pulseIn(PINO_ECHO, HIGH, TIMEOUT_US);
if (dur == 0) return -1; // nenhum eco
return dur * 0.0343 / 2.0; // cm (ida e volta)
}
void setup() {
Serial.begin(115200);
pinMode(PINO_TRIG, OUTPUT);
pinMode(PINO_ECHO, INPUT);
Serial.println("HC-SR04 pronto");
}
void loop() {
float cm = medirCm();
if (cm < 0) Serial.println("sem eco (longe demais ou superficie macia)");
else { Serial.print("distancia: "); Serial.print(cm, 1); Serial.println(" cm"); }
delay(200);
}
Como verificar
- ✓Mão a uns 20 cm do sensor: o Monitor mostra perto de "distancia: 20.0 cm"; confira com uma régua (erro de 1 a 2 cm é normal).
- ✓No Wokwi, o valor acompanha o controle deslizante do sensor.
- ✗Sempre "sem eco"? Confira o 5 V no VCC e se TRIG e ECHO não estão trocados.
✓ O HC-SR04 vê bem
- ✓Parede, caixa, perna de cadeira de frente.
- ✓Entre uns 2 cm e 2 a 3 m, em ambiente fechado.
- ✓Superfícies duras e lisas.
✗ O HC-SR04 se confunde
- ✗Cortina, sofá, almofada: absorvem o som.
- ✗Parede muito inclinada: o eco vai para o lado.
- ✗Dois HC-SR04 disparando juntos: um ouve o eco do outro.
Por que aprender
É o "olho" do rover que desvia de obstáculos no módulo 1.6. Saber os limites dele (superfícies macias, ângulos) explica por que o robô às vezes bate numa cortina e não numa parede.
Conceitos-chave
🔦 Infravermelho: linha e obstáculo
Os sensores infravermelhos (IR) de reflexão têm um LED que emite luz invisível e um receptor ao lado. Superfície clara reflete muito; superfície preta absorve. No sensor de linha (TCRT5000), apontado para o chão a poucos milímetros, isso separa a fita preta do piso. No sensor de obstáculo, apontado para a frente, um objeto perto devolve luz e o módulo avisa. Muitos módulos trazem um comparador com trimpot (parafusinho azul) que transforma o analógico em digital.
🌱 Novo aqui?
O limiar (threshold) é o valor que separa "linha" de "chão": acima dele é uma coisa, abaixo é outra. Calibrar é medir o valor no claro e no escuro na superfície real e pôr o limiar no meio. Em muitos módulos a saída digital DO fica em LOW quando detecta; confira o LED indicador da plaquinha.
Como ler: as setas vermelhas são a luz infravermelha. À esquerda ela volta inteira; no meio, quase nada volta. Nos módulos TCRT5000 comuns, menos luz no receptor = número MAIOR no ADC (os valores 300 e 3500 são exemplo; os seus vão ser outros). À direita, a régua: o limiar fica no meio entre a leitura do claro e a do preto, medidas no seu chão.
Objetivo: calibrar o sensor de linha (AO no GPIO 35) nos primeiros 5 segundos, passando-o sobre o claro e o preto, e depois dizer "LINHA" ou "chao"; ler junto um sensor de obstáculo (DO no GPIO 27). No Wokwi, use um potenciômetro no lugar do AO e um botão no lugar do DO.
// IR de linha (analógico) com calibração + IR de obstáculo (digital)
const int PINO_LINHA = 35; // AO do TCRT5000, ADC1
const int PINO_OBST = 27; // DO do módulo de obstáculo
int minimo = 4095, maximo = 0, limiar = 2048;
void setup() {
Serial.begin(115200);
pinMode(PINO_OBST, INPUT_PULLUP); // segura o nível se o módulo estiver solto
Serial.println("Calibrando por 5 s: passe o sensor no claro e no preto");
unsigned long inicio = millis();
while (millis() - inicio < 5000) {
int v = analogRead(PINO_LINHA);
if (v < minimo) minimo = v;
if (v > maximo) maximo = v;
delay(5);
}
limiar = (minimo + maximo) / 2;
Serial.print("min="); Serial.print(minimo);
Serial.print(" max="); Serial.print(maximo);
Serial.print(" limiar="); Serial.println(limiar);
}
void loop() {
int v = analogRead(PINO_LINHA);
bool naLinha = (v > limiar); // preto = valor alto
bool obstaculo = (digitalRead(PINO_OBST) == LOW); // muitos módulos: LOW = detectou
Serial.print("linha="); Serial.print(v);
Serial.print(naLinha ? " LINHA" : " chao");
Serial.println(obstaculo ? " | OBSTACULO" : " | livre");
delay(100);
}
Como verificar
- ✓Depois da calibração, "max" deve ser bem maior que "min" (diferença de centenas ou mais).
- ✓Sobre a fita aparece "LINHA"; fora dela, "chao". Mão a poucos cm do sensor de obstáculo: "OBSTACULO".
- ✗min e max quase iguais? O sensor está alto demais (o ideal fica entre uns 3 e 10 mm do chão) ou você não passou sobre o preto.
Por que aprender
São os sensores mais baratos do kit e a base do seguidor de linha da Trilha 2, onde cinco deles lado a lado viram uma barra. A calibração automática que você acabou de escrever é a mesma que o projeto 2.6 usa.
Conceitos-chave
📉 Ruído e filtro de média
Nenhum sensor dá o mesmo número duas vezes. A leitura "pula" por ruído elétrico, vibração, eco perdido. Às vezes vem um valor absurdo (0 cm, 900 cm) que, se o robô levar a sério, faz ele frear do nada. Duas ferramentas simples resolvem quase tudo: descartar o que está fora da faixa possível e fazer a média móvel das últimas N leituras válidas.
🌱 Novo aqui?
A média móvel guarda as últimas N leituras numa lista circular e devolve a média delas; a cada leitura nova, a mais antiga sai. A mediana ordena os valores e pega o do meio, ótima contra picos isolados. O preço do filtro é o atraso: com N grande, a curva fica lisa mas demora a reagir.
Como ler: a linha ciano é o que o sensor entrega; a verde é o que o robô deve usar. Os círculos vermelhos são leituras impossíveis, jogadas fora antes da média. Repare que, quando o objeto se aproxima, a verde desce um pouco depois da ciano: esse é o atraso do filtro, pequeno com 5 amostras.
Objetivo: aplicar descarte + média móvel de 5 leituras ao HC-SR04 do tópico 2 e ver as duas curvas juntas no Serial Plotter.
// Descarte + média móvel no HC-SR04 (ESP32, pacote esp32 3.x)
const int PINO_TRIG = 5, PINO_ECHO = 18;
const int N = 5; // tamanho da janela
float janela[N];
int pos = 0, cheias = 0;
float medirCm() {
digitalWrite(PINO_TRIG, LOW); delayMicroseconds(2);
digitalWrite(PINO_TRIG, HIGH); delayMicroseconds(10);
digitalWrite(PINO_TRIG, LOW);
unsigned long dur = pulseIn(PINO_ECHO, HIGH, 30000);
return (dur == 0) ? -1 : dur * 0.0343 / 2.0;
}
float filtrar(float novo) {
janela[pos] = novo; pos = (pos + 1) % N; // lista circular
if (cheias < N) cheias++;
float soma = 0;
for (int i = 0; i < cheias; i++) soma += janela[i];
return soma / cheias;
}
void setup() {
Serial.begin(115200);
pinMode(PINO_TRIG, OUTPUT); pinMode(PINO_ECHO, INPUT);
}
void loop() {
float cru = medirCm();
static float filtrado = 0;
if (cru >= 2 && cru <= 400) filtrado = filtrar(cru); // descarta o impossível
Serial.print("cru:"); Serial.print(cru);
Serial.print(",filtrado:"); Serial.println(filtrado);
delay(60); // HC-SR04 pede uns 60 ms entre medidas
}
Como verificar: no Serial Plotter, "cru" tem dentes e picos (inclusive -1 quando falta eco) e "filtrado" é uma linha lisa que segue sua mão com um pequeno atraso. Troque N para 15 e veja o atraso crescer.
✓ Filtro na medida
- ✓Descartar primeiro, depois fazer a média.
- ✓Janela pequena (3 a 7) para decisão rápida.
- ✓Mediana quando os picos são raros e enormes.
✗ Filtro que atrapalha
- ✗Média de 50 leituras: o robô vê a parede tarde demais.
- ✗Incluir o -1 do "sem eco" na média.
- ✗Filtrar no código um ruído que vem de fio solto.
Por que aprender
Robô que reage a ruído freia, vira e hesita sem motivo. Com o filtro, a decisão do módulo 1.6 fica confiável. Na Trilha 2 a mesma ideia reaparece como filtro complementar da IMU e filtro na derivada do PID.
Conceitos-chave
🔗 Sensores I2C: dois fios, vários sensores
O I2C é um barramento de dois fios, SDA (dados) e SCL (relógio), compartilhado por vários dispositivos. Cada um tem um endereço de 7 bits, escrito em hexadecimal: o sensor de distância a laser VL53L0X costuma responder em 0x29, a IMU MPU-6050 em 0x68, a telinha OLED em 0x3C. No ESP32 clássico os pinos padrão são SDA = GPIO 21 e SCL = GPIO 22, e a biblioteca é a Wire, que já vem no pacote.
Como ler: as duas linhas horizontais são o barramento inteiro. Cada módulo ciano se pendura nelas com dois fios (pontos brancos) e responde só quando o ESP32 chama o seu endereço. Os resistores âmbar mantêm as linhas em 3,3 V quando ninguém fala; a maioria dos módulos já traz esses pull-ups na plaquinha. Além de SDA e SCL, cada módulo precisa de 3,3 V e GND.
Do zero ao primeiro número
Ligue quatro fios
VCC → 3,3 V, GND → GND, SDA → GPIO 21, SCL → GPIO 22.
Rode o scanner
Ele pergunta a todos os 127 endereços quem responde. Se o seu sensor aparecer, a fiação está certa.
Instale a biblioteca do sensor
Ferramentas → Gerenciar Bibliotecas. Para o VL53L0X, procure "VL53L0X" e instale a da Pololu. Abra o exemplo dela em Arquivo → Exemplos para conferir a versão.
Objetivo: listar no Serial Monitor todos os dispositivos I2C ligados. No Wokwi, adicione um "MPU6050" ligado em 21/22 e o scanner deve achar 0x68.
// Scanner I2C: quem está no barramento? (ESP32, pacote esp32 3.x)
#include <Wire.h>
void setup() {
Serial.begin(115200);
Wire.begin(); // SDA 21, SCL 22 no ESP32 clássico
delay(300);
Serial.println("Procurando dispositivos I2C...");
int achados = 0;
for (byte endereco = 1; endereco < 127; endereco++) {
Wire.beginTransmission(endereco);
if (Wire.endTransmission() == 0) { // 0 = alguém respondeu
Serial.print(" encontrado em 0x");
if (endereco < 16) Serial.print("0");
Serial.println(endereco, HEX);
achados++;
}
}
Serial.print(achados); Serial.println(" dispositivo(s).");
}
void loop() {}
Como verificar: com um VL53L0X, aparece "encontrado em 0x29" e "1 dispositivo(s)."; com o MPU-6050 do Wokwi, 0x68. Zero dispositivos = SDA e SCL trocados, falta GND ou o módulo não tem alimentação.
Objetivo: ler a distância em milímetros do VL53L0X (placa real; o Wokwi não tem essa peça), com a biblioteca VL53L0X da Pololu.
// Distância a laser VL53L0X por I2C (biblioteca VL53L0X da Pololu)
#include <Wire.h>
#include <VL53L0X.h>
VL53L0X sensor;
void setup() {
Serial.begin(115200);
Wire.begin();
sensor.setTimeout(500);
if (!sensor.init()) {
Serial.println("VL53L0X nao encontrado: confira fios e endereco");
while (true) delay(1000);
}
sensor.startContinuous(); // mede sem parar
}
void loop() {
int mm = sensor.readRangeContinuousMillimeters();
if (sensor.timeoutOccurred()) Serial.println("timeout");
else { Serial.print("distancia: "); Serial.print(mm); Serial.println(" mm"); }
delay(100);
}
Como verificar: com a mão a 10 cm, aparece perto de "distancia: 100 mm"; sem nada à frente (acima de uns 1,2 m), valores perto de 8190 indicam "fora do alcance".
Por que aprender
IMU, telas, sensores de cor, de pressão e de distância a laser usam I2C. Com dois pinos você liga vários deles, o que vai fazer falta na Trilha 2, quando o robô ganhar IMU e encoders e os pinos começarem a acabar.
Conceitos-chave
⏱️ Ler vários sensores sem travar
Um delay(500) parece inofensivo, mas durante esse meio segundo o ESP32 não faz mais nada: não lê o IR, não vê a parede, não para o motor. A solução é trocar a espera por uma pergunta: "já passou tempo suficiente desde a última vez?", usando millis(). Cada sensor ganha seu próprio relógio e o loop() gira milhares de vezes por segundo, atendendo quem estiver na hora.
🌱 Novo aqui?
Código bloqueante é o que para tudo enquanto espera (como delay). Não bloqueante confere o relógio e segue em frente. Uma tarefa periódica é um pedaço de código que roda a cada X milissegundos. A conta millis() - ultimo >= intervalo, com unsigned long, continua certa mesmo quando o contador dá a volta, depois de uns 49 dias ligado.
Como ler: em cima, cada bloco vermelho é meio segundo em que o programa está surdo e cego; o obstáculo (linha âmbar) só é notado na leitura seguinte. Embaixo, três tarefas com ritmos próprios: IR a cada 10 ms, distância a cada 60 ms, mensagem a cada 250 ms. Nenhuma espera pela outra, e o obstáculo é notado na próxima leitura de distância.
Objetivo: juntar HC-SR04, IR de linha e um LED de alerta num só programa sem nenhum delay no loop. O LED do GPIO 2 acende quando há obstáculo a menos de 20 cm. É o esqueleto do "sentir" do rover do módulo 1.6.
// Vários sensores sem travar: millis() no lugar de delay (ESP32, 3.x)
const int PINO_TRIG = 5, PINO_ECHO = 18, PINO_LINHA = 35, PINO_LED = 2;
const float PERTO_CM = 20.0;
unsigned long tIR = 0, tDist = 0, tPrint = 0;
float distanciaCm = -1;
int linha = 0;
float medirCm() {
digitalWrite(PINO_TRIG, LOW); delayMicroseconds(2);
digitalWrite(PINO_TRIG, HIGH); delayMicroseconds(10);
digitalWrite(PINO_TRIG, LOW);
unsigned long dur = pulseIn(PINO_ECHO, HIGH, 25000); // espera no máximo 25 ms
return (dur == 0) ? -1 : dur * 0.0343 / 2.0;
}
void setup() {
Serial.begin(115200);
pinMode(PINO_TRIG, OUTPUT); pinMode(PINO_ECHO, INPUT); pinMode(PINO_LED, OUTPUT);
Serial.println("Sensores rodando sem delay");
}
void loop() {
unsigned long agora = millis();
if (agora - tIR >= 10) { // IR: 100 vezes por segundo
tIR = agora;
linha = analogRead(PINO_LINHA);
}
if (agora - tDist >= 60) { // distância: ~16 vezes por segundo
tDist = agora;
float cm = medirCm();
if (cm > 0) distanciaCm = cm;
digitalWrite(PINO_LED, (distanciaCm > 0 && distanciaCm < PERTO_CM) ? HIGH : LOW);
}
if (agora - tPrint >= 250) { // relatório: 4 vezes por segundo
tPrint = agora;
Serial.print("dist="); Serial.print(distanciaCm, 1);
Serial.print(" cm | linha="); Serial.print(linha);
Serial.println(distanciaCm > 0 && distanciaCm < PERTO_CM ? " | PERTO" : "");
}
}
Como verificar
- ✓O Monitor mostra 4 linhas por segundo com "dist=" e "linha="; aproximando a mão a menos de 20 cm, aparece "PERTO" e o LED acende na hora.
- ✓Passar o sensor de linha sobre o preto muda "linha=" imediatamente, mesmo com a distância sendo medida.
- ✗Tudo lento? Sobrou algum delay() no loop ou numa função chamada por ele.
💡 O pulseIn também espera
O pulseIn bloqueia até o eco voltar, por isso o timeout de 25 ms: no pior caso o loop perde 25 ms, nunca meio segundo. Na Trilha 2 você vai separar isso de vez com tarefas do FreeRTOS e interrupções.
Por que aprender
Um delay longo deixa o robô cego e ele bate antes de perceber. Com tarefas periódicas, o rover do módulo 1.6 mede, decide e move ao mesmo tempo, e você pode acrescentar sensores sem reescrever o resto.
Conceitos-chave
✅ Resumo do módulo
Para ir além
- I2C no Arduino-ESP32 (Espressif) — Wire, pinos e velocidade
- Biblioteca VL53L0X da Pololu — exemplos e modos de medida
- HC-SR04 no Wokwi — simular o sensor de distância
- Blink Without Delay (Arduino) — o padrão do millis() explicado
- ADC no Arduino-ESP32 — leitura analógica dos sensores IR
Próximo módulo
1.5 · Motores e energia. Motor DC, servo ou passo; ponte H TB6612FNG ou DRV8833; velocidade com PWM; servo com ESP32Servo; baterias e GND comum.