Conteúdo detalhado
🧰 Arduino IDE 2.x e o pacote esp32 3.x
A Arduino IDE 2.x é o programa onde você escreve o código, compila e manda para a placa. Sozinha ela só conhece as placas Arduino; para o ESP32 você instala o pacote "esp32" da Espressif, que hoje está na geração 3.x. Essa versão mudou várias funções, e é por isso que muito tutorial de 2022 ou 2023 dá erro de compilação: ele foi escrito para a API 2.x.
🌱 Novo aqui?
IDE (ambiente de desenvolvimento) é o editor com botões de compilar e enviar. Compilar é traduzir o seu texto em C++ para a linguagem de máquina do chip. Um pacote de placas (core) é o conjunto de ferramentas e bibliotecas que ensina a IDE a falar com uma família de chips. API é a lista de funções que você pode chamar, como digitalWrite(); quando ela muda, código antigo pode parar de compilar.
Instalação passo a passo
Baixe a IDE 2.x
Em arduino.cc/en/software, escolha seu sistema (Windows, macOS ou Linux AppImage). Instale e abra.
Abra o Gerenciador de Placas
Ícone de placa na barra lateral esquerda (ou Ferramentas → Placa → Gerenciador de Placas). Digite esp32.
Instale "esp32 by Espressif Systems" 3.x
Escolha a versão 3 mais recente e clique em Instalar. Se o pacote não aparecer, vá em Arquivo → Preferências e cole em "URLs adicionais": https://espressif.github.io/arduino-esp32/package_esp32_index.json
Confira a versão
No Gerenciador de Placas, o pacote deve mostrar "INSTALLED" com número começando por 3. Esse número é o que você informa quando pedir ajuda em fórum.
Como ler: à esquerda, o caminho antigo (tracejado cinza) passava por um canal numerado que você criava e depois ligava ao pino. À direita, o caminho atual: uma chamada prepara o pino e a escrita usa o próprio pino. Se um tutorial pedir para escolher "canal 0" do PWM, ele é da API 2.x; traduza para as duas funções verdes.
✓ Sinais de tutorial atual
- ✓Usa ledcAttach(pino, freq, bits) e ledcWrite(pino, valor).
- ✓Cita "arduino-esp32 3.x" ou ESP-IDF 5.
- ✓Servo com a biblioteca ESP32Servo, não com Servo.h do Uno.
✗ Sinais de tutorial antigo
- ✗Funções de PWM com um número de canal separado do pino.
- ✗Manda "voltar para a versão 2.0.x do pacote" para compilar.
- ✗Afirma que o ESP32 não tem analogWrite (o pacote atual tem).
Por que aprender
Saber a versão do pacote poupa a pior frustração do começo: copiar um código "que funcionava" e ver dez erros vermelhos. Com a regra "canal separado = API antiga", você corrige qualquer tutorial em um minuto.
Conceitos-chave
🔌 Placa, porta e primeiro upload
Para enviar um programa, a IDE precisa de duas informações: qual placa (para compilar para o chip certo) e qual porta (por onde mandar). Para o DevKit comum (WROOM-32), escolha ESP32 Dev Module; para placas com ESP32-S3, ESP32S3 Dev Module ou o nome exato da sua placa. A porta aparece quando você liga o cabo USB.
🌱 Novo aqui?
A porta é o "endereço" do cabo no computador: COM3, COM4 no Windows; /dev/ttyUSB0 ou /dev/ttyACM0 no Linux; /dev/cu.usbserial… no macOS. Muitas placas têm um chip conversor USB-serial (CP2102 ou CH340) que pode precisar de driver no Windows. O botão BOOT coloca o ESP32 no modo de receber programa; o EN (ou RST) reinicia a placa.
Como ler: a fileira verde é o caminho do programa, da esquerda para a direita. Embaixo de cada etapa está a falha que mais aparece nela. Quando o upload der errado, leia a última mensagem da IDE e ache em qual caixa você está: isso já diz o que testar.
Objetivo: mandar para a placa real o mesmo pisca-LED que você rodou no Wokwi no módulo 1.1 (o LED externo no GPIO 2 com 220 Ω, ou só o LED azul da placa, se ela tiver).
// Primeiro upload na placa real (ESP32, pacote esp32 3.x)
const int PINO_LED = 2;
void setup() {
Serial.begin(115200);
pinMode(PINO_LED, OUTPUT);
Serial.println("Upload OK: o ESP32 esta rodando seu programa");
}
void loop() {
digitalWrite(PINO_LED, HIGH);
delay(500);
digitalWrite(PINO_LED, LOW);
delay(500);
}
Como verificar
- ✓A IDE termina com "Hard resetting via RTS pin..." e o LED começa a piscar.
- ✓No Serial Monitor (115200 baud) aparece "Upload OK..."; se não aparecer, aperte EN para reiniciar.
- ✗Linux com "Permission denied" na porta: rode sudo usermod -aG dialout $USER, saia e entre de novo na sessão.
✓ Quando travar, nesta ordem
- ✓Troque o cabo por um de dados comprovado.
- ✓Veja se a porta some e volta ao desligar o cabo.
- ✓Segure BOOT quando aparecer "Connecting..." e solte ao ver a porcentagem.
✗ O que não resolve
- ✗Reinstalar a IDE inteira antes de testar o cabo.
- ✗Deixar o Serial Monitor de outro programa usando a mesma porta.
- ✗Ligar no GPIO 0 ou 12 algo que force nível na partida e depois culpar a IDE.
Por que aprender
O primeiro upload é onde mais gente desiste, quase sempre por cabo ou porta. Com o mapa das quatro etapas, o erro fica previsível e a solução leva minutos.
Conceitos-chave
🧱 setup, loop, variáveis e funções
Todo sketch tem duas funções obrigatórias. setup() roda uma vez quando a placa liga: é onde você configura pinos e o Serial. loop() roda de novo assim que termina, para sempre: é o "pensar" do ciclo sentir-pensar-agir. Dentro delas você usa variáveis para guardar valores, if para decidir, for para repetir e funções próprias para dar nome a cada pedaço do comportamento.
Como ler: da esquerda para a direita é a ordem no tempo. A seta tracejada mostra que o loop() nunca acaba. À direita, as caixas ciano são funções que você escreve e o loop chama: o loop fica curto e cada função faz uma coisa só, com nome que explica.
| Tipo | Guarda | Exemplo no robô |
|---|---|---|
| int | número inteiro | pino, leitura do ADC |
| float | número com vírgula | distância em cm, tensão |
| bool | verdadeiro ou falso | obstáculo à frente? |
| unsigned long | inteiro grande, sem sinal | tempo em ms com millis() |
| const | valor que não muda | número do pino, limites |
Objetivo: um semáforo de um LED só, escrito com funções e um for, para você ver como o loop fica legível. Rode no Wokwi com um LED no GPIO 2.
// Variáveis, if, for e funções (ESP32, pacote esp32 3.x)
const int PINO_LED = 2;
int ciclo = 0; // variável global: vale em todo o programa
void piscar(int vezes, int intervaloMs) {
for (int i = 0; i < vezes; i++) {
digitalWrite(PINO_LED, HIGH); delay(intervaloMs);
digitalWrite(PINO_LED, LOW); delay(intervaloMs);
}
}
bool cicloPar(int n) { // função que devolve um valor
return (n % 2 == 0);
}
void setup() {
Serial.begin(115200);
pinMode(PINO_LED, OUTPUT);
}
void loop() {
ciclo++;
if (cicloPar(ciclo)) {
Serial.println("ciclo par: 2 piscadas lentas");
piscar(2, 400);
} else {
Serial.println("ciclo impar: 4 piscadas rapidas");
piscar(4, 120);
}
delay(1000);
}
Como verificar: o LED alterna entre 4 piscadas rápidas e 2 lentas, com uma pausa de 1 s entre os grupos; o Serial Monitor escreve "ciclo impar..." e "ciclo par..." alternados.
Por que aprender
O rover do módulo 1.6 vai ter um loop de poucas linhas chamando lerDistancia(), decidir() e mover(). Código organizado assim é o que deixa você ajustar uma parte sem quebrar as outras.
Conceitos-chave
🔍 Serial Monitor para depurar
Robô não tem tela. O Serial Monitor é sua janela para dentro dele: o programa escreve com Serial.print() e você lê no computador. O Serial Plotter, no mesmo menu Ferramentas, desenha os números como gráfico ao vivo, o que deixa ruído e tendências visíveis num relance. Depurar é exatamente isso: imprimir o que o programa está pensando até achar onde ele pensa errado.
🌱 Novo aqui?
Baud rate é a velocidade da conversa serial, em bits por segundo. O número em Serial.begin(115200) tem que ser igual ao escolhido no canto do Serial Monitor; se for diferente, aparecem símbolos sem sentido. print escreve e continua na mesma linha; println escreve e pula linha.
Objetivo: gerar duas curvas (uma onda e uma rampa) no formato que o Serial Plotter entende, nome:valor separados por vírgula, e uma mensagem de estado no Serial Monitor. Funciona no Wokwi sem ligar nada.
// Serial Monitor e Serial Plotter (ESP32, pacote esp32 3.x)
unsigned long amostra = 0;
void setup() {
Serial.begin(115200); // mesmo valor no canto do Monitor/Plotter
delay(500);
Serial.println("Depuracao iniciada");
}
void loop() {
float onda = 50.0 * sin(amostra * 0.1); // sobe e desce entre -50 e 50
int rampa = amostra % 100; // sobe de 0 a 99 e recomeça
// formato do Plotter: nome:valor,nome:valor e quebra de linha
Serial.print("onda:"); Serial.print(onda);
Serial.print(",rampa:"); Serial.println(rampa);
amostra++;
delay(50); // 20 pontos por segundo
}
Como verificar
- ✓No Serial Monitor: "Depuracao iniciada" e depois linhas como "onda:29.55,rampa:6".
- ✓Feche o Monitor e abra Ferramentas → Serial Plotter: uma senoide e um dente de serra, com legenda "onda" e "rampa".
- ✗Símbolos estranhos? O baud do Monitor não está em 115200.
💡 Depuração que ajuda
Imprima nome e valor ("dist=23.4", não só "23.4") e uma mensagem em cada ramo do if para saber por onde o código passou. Evite milhares de linhas por segundo sem delay (a janela trava) e lembre que Monitor e Plotter não podem usar a porta ao mesmo tempo.
Por que aprender
Quando o robô fizer algo inesperado, o serial é como você vai descobrir se o sensor leu errado, se a decisão foi errada ou se o motor não obedeceu. Na Trilha 2 o mesmo hábito vira telemetria por Wi-Fi.
Conceitos-chave
🔘 Entradas: botão e leitura analógica
Até aqui a placa só "falou". Agora ela escuta. Um botão com INPUT_PULLUP (visto no módulo 1.2) é lido com digitalRead(); um potenciômetro (resistor com botão giratório) é lido com analogRead(), que devolve de 0 a 4095. Botões mecânicos têm um detalhe: o contato "quica" por alguns milissegundos e o programa pode contar vários apertos em um.
🌱 Novo aqui?
Debounce é ignorar mudanças que duram menos que um tempo mínimo (uns 30 ms), para contar só apertos reais. millis() devolve quantos milissegundos se passaram desde que a placa ligou; comparar dois valores de millis() mede intervalos sem parar o programa. O potenciômetro tem três pernas: as das pontas vão em 3,3 V e GND, a do meio entrega a tensão variável.
Como ler: à esquerda, o botão liga o GPIO 4 ao GND quando apertado (sem resistor externo: o pull-up está dentro do chip). À direita, o potenciômetro recebe 3,3 V (vermelho) e GND (ciano) nas pontas; a seta é o cursor do meio, que vai ao GPIO 34. Embaixo, o LED do GPIO 2 mostra o resultado. Nunca ligue o potenciômetro em 5 V: o GPIO 34 receberia mais de 3,3 V no fim do curso.
Objetivo: contar apertos do botão com debounce (cada aperto inverte o LED) e mostrar a posição do potenciômetro em porcentagem. Monte igual à figura no Wokwi ("Pushbutton", "Potentiometer", "LED" e "Resistor").
// Botão com debounce + potenciômetro (ESP32, pacote esp32 3.x)
const int PINO_BOTAO = 4; // botão até o GND, pull-up interno
const int PINO_POT = 34; // ADC1, só entrada
const int PINO_LED = 2;
const unsigned long DEBOUNCE_MS = 30;
int estadoEstavel = HIGH; // solto
int ultimaLeitura = HIGH;
unsigned long mudouEm = 0;
int apertos = 0;
bool ledLigado = false;
unsigned long ultimoPrint = 0;
void setup() {
Serial.begin(115200);
pinMode(PINO_BOTAO, INPUT_PULLUP);
pinMode(PINO_LED, OUTPUT);
Serial.println("Aperte o botao e gire o potenciometro");
}
void loop() {
int leitura = digitalRead(PINO_BOTAO);
if (leitura != ultimaLeitura) { mudouEm = millis(); ultimaLeitura = leitura; }
if (millis() - mudouEm > DEBOUNCE_MS && leitura != estadoEstavel) {
estadoEstavel = leitura;
if (estadoEstavel == LOW) { // acabou de ser apertado
apertos++;
ledLigado = !ledLigado;
digitalWrite(PINO_LED, ledLigado ? HIGH : LOW);
Serial.print("Aperto numero "); Serial.println(apertos);
}
}
if (millis() - ultimoPrint >= 500) { // pot a cada meio segundo, sem delay
ultimoPrint = millis();
int bruto = analogRead(PINO_POT); // 0 a 4095
int pct = map(bruto, 0, 4095, 0, 100);
Serial.print("pot="); Serial.print(bruto);
Serial.print(" ("); Serial.print(pct); Serial.println("%)");
}
}
Como verificar
- ✓Cada aperto inverte o LED e soma 1 em "Aperto numero N", nunca 2 ou 3 de uma vez.
- ✓Girando o potenciômetro de ponta a ponta, "pot=" vai de perto de 0 (0%) a 4095 (100%).
- ✗Aperto contando sozinho? Confira se o botão está em colunas diferentes da protoboard e se o pino é o 4.
Por que aprender
É a mesma leitura que você fará com sensores de verdade no módulo 1.4: digital para o sensor IR de obstáculo, analógica para o sensor de linha. E o truque do millis() no lugar do delay() volta, em grande, no último tópico de sensores.
Conceitos-chave
🎚️ PWM na API 3.x: brilho e som
Um pino digital só sabe dar 0 ou 3,3 V. Para um LED ter meio brilho, o truque é ligar e desligar muito rápido: se ele fica ligado metade do tempo, o olho vê metade do brilho. Isso é PWM (modulação por largura de pulso). No ESP32 quem gera o PWM é o periférico LEDC, e na API 3.x bastam duas funções: ledcAttach(pino, frequência, bits) e ledcWrite(pino, valor). Para som, ledcWriteTone(pino, frequência) muda a frequência e mantém o pulso pela metade.
🌱 Novo aqui?
Ciclo de trabalho (duty cycle) é a porcentagem do tempo em que o sinal fica ligado. Frequência é quantas vezes por segundo o ciclo se repete (Hz). Resolução é em quantos degraus você divide o ciclo: com 8 bits, de 0 a 255; com 10 bits, de 0 a 1023. Um buzzer passivo só toca se receber uma frequência; o ativo apita sozinho quando ligado.
Como ler: as três linhas têm o mesmo ritmo (frequência); muda só quanto tempo cada pulso fica no alto. À direita, o brilho que o olho percebe e o valor de ledcWrite em 8 bits (255 = 100%). No módulo 1.5, o mesmo número vai virar velocidade de motor.
Objetivo: o potenciômetro do tópico 5 controla o brilho do LED do GPIO 2, e o botão toca uma escala curta num buzzer passivo no GPIO 25. No Wokwi, use a peça "Buzzer" (ela é passiva).
// PWM na API 3.x: brilho pelo potenciômetro e som no buzzer (ESP32)
const int PINO_LED = 2;
const int PINO_BUZZER = 25;
const int PINO_POT = 34;
const int PINO_BOTAO = 4;
const int NOTAS[] = {262, 294, 330, 349, 392}; // dó, ré, mi, fá, sol (Hz)
void tocarEscala() {
for (int i = 0; i < 5; i++) {
ledcWriteTone(PINO_BUZZER, NOTAS[i]); // muda a frequência, 50% de ciclo
delay(180);
}
ledcWrite(PINO_BUZZER, 0); // silêncio
}
void setup() {
Serial.begin(115200);
pinMode(PINO_BOTAO, INPUT_PULLUP);
ledcAttach(PINO_LED, 5000, 8); // LED: 5 kHz, 8 bits (0 a 255)
// canais 0-1, 2-3, 4-5, 6-7 dividem um temporizador, e ledcWriteTone muda a
// frequência do temporizador inteiro: o buzzer vai para o canal 2, longe do LED
ledcAttachChannel(PINO_BUZZER, 2000, 10, 2);
ledcWrite(PINO_BUZZER, 0);
Serial.println("PWM pronto: gire o pot, aperte o botao");
}
void loop() {
int bruto = analogRead(PINO_POT); // 0 a 4095
int brilho = map(bruto, 0, 4095, 0, 255); // 0 a 255
ledcWrite(PINO_LED, brilho); // recebe o PINO
if (digitalRead(PINO_BOTAO) == LOW) {
Serial.println("Tocando escala");
tocarEscala();
while (digitalRead(PINO_BOTAO) == LOW) delay(10); // espera soltar
}
Serial.print("brilho:"); Serial.println(brilho);
delay(50);
}
Como verificar
- ✓Girar o potenciômetro muda o brilho suavemente, sem piscar; o Serial Plotter mostra "brilho" de 0 a 255.
- ✓Apertar o botão toca cinco notas subindo e o Monitor mostra "Tocando escala".
- ✗Erro de compilação em ledcAttach? O pacote instalado é 2.x: atualize para 3.x (tópico 1).
💡 Frequência certa para cada uso
LED: acima de 1 kHz (5 kHz é um bom padrão) para não piscar em câmera. Motor DC: entre 1 e 20 kHz, conforme o driver. Servo: 50 Hz, mas use a biblioteca ESP32Servo no módulo 1.5. Quanto maior a frequência, menor a resolução máxima disponível; 8 a 10 bits sobram para robôs.
Por que aprender
O PWM é como o ESP32 controla a velocidade dos motores através da ponte H no módulo 1.5 e o ângulo dos servos. A buzina do rover, nos próximos passos do módulo 1.6, é o mesmo ledcWriteTone deste exemplo.
Conceitos-chave
✅ Resumo do módulo
Para ir além
- Instalar o Arduino-ESP32 (Espressif) — Gerenciador de Placas e URL oficial
- LEDC (PWM) na API 3.x — ledcAttach, ledcWrite, ledcWriteTone
- Guia de migração 2.x → 3.0 — o que mudou e como traduzir
- Serial Plotter da IDE 2 — formato das variáveis e legendas
- Referência da linguagem Arduino — if, for, millis, map
Próximo módulo
1.4 · Sensores: o robô percebe. Distância com HC-SR04, linha e obstáculo com infravermelho, filtro de ruído, I2C e leitura de vários sensores sem travar.