PTENES
MÓDULO 1.3 · PRÁTICA

💻 ESP32: o primeiro programa

Do simulador para a placa real. Você instala a Arduino IDE 2.x com o pacote esp32 3.x, faz o primeiro upload, aprende o mínimo de C++ para escrever programas de robô, usa o Serial Monitor como janela para dentro da placa e controla brilho e som com PWM na API nova.

6
Tópicos
Iniciante
Nível
Prática
Tipo
ESP32
Hardware (ou Wokwi)
0%
0 de 0 tópicos lidos · Seção 1 de 6

Conteúdo detalhado

1

🧰 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

1

Baixe a IDE 2.x

Em arduino.cc/en/software, escolha seu sistema (Windows, macOS ou Linux AppImage). Instale e abra.

2

Abra o Gerenciador de Placas

Ícone de placa na barra lateral esquerda (ou Ferramentas → Placa → Gerenciador de Placas). Digite esp32.

3

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

4

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.

API 2.x (antiga) canal 0freq + bits pino 2 escrever no CANAL, não no pinonão compila no pacote 3.x API 3.x (atual)ledcAttach(2, 5000, 8) pino, frequência, bits ledcWrite(2, 128)recebe o PINOo canal é escolhido sozinho tutorial com "canal" = tutorial antigo

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

Gerenciador de placas
onde se instala o pacote
Pacote esp32
Espressif, geração 3.x
Versão da API
decide se o código compila
Compilar
texto → código de máquina
2

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

1 compilar IDE + pacote2 cabo USB porta escolhida3 gravar modo BOOT4 rodar reinicia sozinho onde costuma falhar erro vermelho:placa ou API errada sem porta:cabo só de carga "Connecting...":segure o BOOT texto estranho:baud diferente

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

pisca-led.ino
// 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

Porta COM/tty
endereço do cabo
Driver USB
CP2102 ou CH340
Botão BOOT
modo de gravação
Cabo de dados
não só de carga
3

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

ligavariáveis globais setup()uma vez loop()para sempre repete lerDistancia() decidir()mover() suas funções

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.

TipoGuardaExemplo no robô
intnúmero inteiropino, leitura do ADC
floatnúmero com vírguladistância em cm, tensão
boolverdadeiro ou falsoobstáculo à frente?
unsigned longinteiro grande, sem sinaltempo em ms com millis()
constvalor que não mudanú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.

funcoes.ino
// 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

Tipo de variável
int, float, bool
const
nome fixo para um valor
if / for
decidir e repetir
Função
um nome, uma tarefa
4

🔍 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-plotter.ino
// 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

Baud rate
115200 dos dois lados
print / println
mesma linha / nova linha
Serial Plotter
gráfico ao vivo
nome:valor
legenda no gráfico
5

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

ESP32GPIO 4 GNDGPIO 343,3 V GNDGPIO 2 botãopull-up interno poten- ciômetro GND

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").

botao-e-potenciometro.ino
// 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

INPUT_PULLUP
botão sem resistor externo
analogRead
0 a 4095 no ESP32
Debounce
ignorar o quique
map()
converte faixas
6

🎚️ 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.

25% · fracoledcWrite(p, 64) 50% · médioledcWrite(p, 128)75% · forte ledcWrite(p, 191)mesmo período, largura do pulso diferente

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-brilho-e-som.ino
// 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

Ciclo de trabalho
% do tempo ligado
Frequência
pulsos por segundo
Resolução em bits
8 bits = 0 a 255
ledcWriteTone
nota no buzzer

✅ Resumo do módulo

✓
IDE 2.x + pacote esp32 3.x: tutorial com canal de PWM separado é da API antiga.
✓
Upload em 4 etapas: compilar, cabo de dados, BOOT, rodar; cada uma com sua falha típica.
✓
setup uma vez, loop para sempre, e funções com nome para cada tarefa.
✓
Serial a 115200: Monitor para texto, Plotter para curvas no formato nome:valor.
✓
Entradas: botão com INPUT_PULLUP e debounce; potenciômetro no ADC1 de 0 a 4095.
✓
PWM 3.x: ledcAttach(pino, freq, bits) e ledcWrite(pino, valor); ledcWriteTone para som.

Para ir além

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.