Kit Carro Robô WiFi Multifuncional ESP32 AD178 - Curso Completo

Kit Carro Robô WiFi Multifuncional ESP32 AD178 — Curso Completo com Projetos Práticos

Introdução

Este curso apresenta o Kit Carro Robô WiFi Multifuncional ESP32 AD178, um robô móvel 4WD — four-wheel drive, tração nas quatro rodas — com ESP32 e placa de expansão, driver de motores L298N, 4 motores TT com redução, sensor ultrassônico HC-SR04 montado sobre servo, seguidor de linha de 3 canais IR, sensores de desvio por infravermelho, fotossensores, receptor infravermelho com controle remoto e comando sem fio por WiFi em modo AP — access point, ponto de acesso — com portal em 192.168.4.1 e aplicativo KUONGSHUN, programado na IDE do Arduino — integrated development environment, ambiente integrado de desenvolvimento — com o núcleo esp32 2.0.12. É um material educacional para quem está começando em robótica móvel, sistemas embarcados e redes WiFi embarcadas: a sequência parte da montagem mecânica do chassi em acrílico e da preparação do ambiente com driver CH340, passa por ensaios isolados de tração, desvio infravermelho, fotossensores, ultrassom, servo e linha, avança para comportamentos autônomos de seguimento de objeto, desvio de obstáculos e busca de luz e controle infravermelho, e chega aos firmwares integrados de comando Web e Web multifunção com quatro modos selecionáveis pelo navegador e pelo aplicativo, sempre com o resultado esperado de cada teste. Ao final, o leitor terá o robô montado, calibrado, navegando por linha, desviando de obstáculos, seguindo objetos, buscando luz e dirigível pelo navegador e pelo aplicativo.

O que você vai aprender:

  • Montar o chassi 4WD em acrílico com 4 motores TT, L298N, torre do ultrassom com servo e todos os sensores
  • Preparar IDE do Arduino, núcleo ESP32 2.0.12, driver CH340 e bibliotecas ESP32Servo 1.1.2 e IRremote 4.1.2
  • Validar tração 4WD, desvio IR, fotossensores, telemetria HC-SR04, centragem do servo e calibração da linha de 3 canais
  • Programar seguimento de objeto, desvio com varredura, busca de luz e controle por infravermelho com parada por timeout
  • Operar o modo Web em 192.168.4.1 e o modo Web multifunção com quatro comportamentos, velocidade e servo ajustáveis
  • Diagnosticar alimentação 18650, WiFi, upload, biblioteca e pista com matriz de sintomas e causas

Material utilizado: Kit Carro Robô WiFi Multifuncional ESP32 AD178 disponível na loja RoboBuilders em https://robobuilders.com.br.

Visão Geral do Kit e Arquitetura de Hardware

O AD178 é um robô de quatro rodas motrizes sobre chassi em acrílico em dois andares. Um único ESP32 comanda tudo: gera os dois PWMs de velocidade, os quatro sinais de sentido da ponte H dupla L298N, o PPM do servo do ultrassom, lê o eco por tempo de voo, lê os três canais digitais da linha, lê os dois sensores de desvio infravermelho e os dois fotossensores analógicos, decodifica o infravermelho do controle remoto, além de manter o rádio WiFi em modo AP com servidor HTTP. A energia vem de duas células 18650 em série com chave, alimentando em paralelo a lógica do ESP32 pela expansão e o estágio de potência do L298N. A separação mecânica em dois andares isola vibração e fiação de potência embaixo da eletrônica de sinal em cima, reduzindo ruído no eco do ultrassom e nos sensores ópticos.

A cinemática do 4WD com acionamento por lados (skid steering) relaciona as velocidades das rodas direita e esquerda com a velocidade do centro e a taxa de guinada. Com distância entre lados LL e raio de roda rr, e velocidades angulares ωR\omega_R e ωL\omega_L, o modelo direto é:

v=r2 (ωR+ωL)v = \frac{r}{2}\,(\omega_R + \omega_L) ω=rL (ωR−ωL)\omega = \frac{r}{L}\,(\omega_R - \omega_L)

onde vv é a velocidade de avanço do centro e ω\omega a taxa de guinada. Marcha à frente soma velocidades iguais nos dois lados, giro puro usa lados opostos de mesmo módulo, e curva usa módulos diferentes. Cada lado tem dois motores TT em paralelo elétrico na mesma ponte do L298N, de modo que o lado direito e o lado esquerdo funcionam como as duas rodas de um diferencial clássico. A força de tração disponível é limitada pelo atrito Fmax=μNF_{max} = \mu N, por isso piso liso com poeira reduz aderência e o robô patina em arrancadas com duty alto. O raio de curvatura instantâneo é R=v/ωR = v/\omega, e quando v=0v = 0 o robô gira em torno do próprio centro, manobra usada no desvio após ré de segurança. O código implementa as combinações como funções discretas: ambas as pontes avante para vv positivo, ambas a ré para vv negativo, pontes opostas para ω\omega puro.

Diagrama de fiação oficial do AD178 com ESP32, expansão, L298N, motores, ultrassom, servo, linha e receptor IRCarro robô AD178 montado com chassi em dois andares, 4 rodas e torre do ultrassom

Especificações técnicas da controladora ESP32

Parâmetro de EngenhariaEspecificação TécnicaObservação de Operação
Microcontrolador principalESP32 dual-core Xtensa LX6 até 240 MHzWiFi 802.11 b/g/n e Bluetooth integrados no silício
Tensão lógica de operação3,3 VDCEntradas toleram 3,3 V; divisor do eco do HC-SR04 tratado no texto
Memória Flash4 MB na controladora do kitAbriga sketch, página Web e tabelas do servidor HTTP
SRAM de dados520 KBBuffers do servidor, pilhas das tarefas WiFi e variáveis de modo
Resolução ADC12 bits (0 a 4095 com referência 3,3 V)Fotossensores nos canais analógicos
PWM de velocidadeLEDC 5 kHz 8 bits nos canais 3 e 4ledcWrite(canal, 0-255) com duty D=valor/255D = valor/255
Interface USB-serialConversor CH340C com USB Tipo-CExige driver CH340 e cabo de dados
Rede de comandoAP KUONGSHUN-AD178 senha 12345678 canal 9Portal fixo em 192.168.4.1, potência 19,5 dBm

Especificações dos módulos, atuadores e sensores

Subsistema / MóduloCircuito / ComponenteFaixa de OperaçãoFunção no Sistema
Driver de potênciaPonte H dupla L298N5 V a 35 V / 2 A por canalSentido e velocidade PWM dos 4 motores TT
Motores de tração (4x)Motor TT com redução3 V a 6 V DCTração integral com dois motores por lado em paralelo
Placa de expansãoExpansão para ESP325 V DCDistribui alimentação e sinais da controladora
TelemetriaHC-SR04 piezoelétrico2 cm a 450 cm, cone de 15 grausDistância por eco de 40 kHz
VarreduraServo 180 grausPulsos 500 a 2500 us, período 20 msPan do ultrassom: 90 graus à frente, 160 esquerda, 20 direita
Linha (3 canais)IR emissor-receptor por canalDigital com trimpot por viaPreto absorve, branco reflete; 0 é preto no código
Desvio IR (2x)Par emissor-receptor com LM3932 cm a 7 cm ajustávelNível baixo com obstáculo próximo
Fotossensores (2x)LDR com divisorSaída analógica 3,3 V a 5 VTensão cai com mais luz; busca de luz compara lados
Comando IRReceptor 38 kHz + controleProtocolo NECMarchas e parada com timeout de 200 ms
AlimentaçãoCaixa para 2 células 18650 com chave7,4 V nominal em sérieLógica e potência em paralelo

Inventário do kit

A lista abaixo consolida o guia de montagem em 13 passos do fabricante com as quantidades reais do PDF. Cada grupo indica a função eletromecânica no conjunto, para conferência antes de apertar o primeiro parafuso.

  • Placas de acrílico transparentes inferior e superior com furação do 4WD — casco em dois andares que separa potência embaixo de sinal em cima e define a frente com torre do ultrassom
  • 4 motores TT com 4 suportes metálicos e parafusos M3 por 30 de cabeça redonda com porcas M3 — atuadores de tração integral com redução e bump do suporte para o lado externo
  • 4 rodas de tração com parafusos autorroscantes M2 por 25 — conversão de torque em deslocamento nos quatro cantos
  • Módulo seguidor de linha de 3 vias com cabo Dupont XH2.54 de 5 pinos — leitura reflexiva da pista sob o bico dianteiro com trimpot de sensibilidade
  • Módulo driver L298N com 4 colunas de cobre M3 por 10 e parafusos M3 por 8 — estágio de potência das duas pontes com orientação de bornes definida no passo
  • 6 colunas de cobre M3 por 40 com parafusos M3 por 12 — vão entre andares que acomoda driver, expansão e chicotes
  • Placa controladora ESP32 com USB Tipo-C e chip serial CH340C — cérebro único com WiFi, ADC de 12 bits, LEDC para PWM e portas para todos os sensores
  • Placa de expansão para ESP32 com 4 colunas M3 por 10 e parafusos M3 por 8 — distribuição de alimentação e sinais com barras que recebem a controladora
  • 2 fotossensores LDR com parafusos M2 por 10 e porcas M2 — olhos da busca de luz na dianteira
  • 2 sensores de desvio por infravermelho com parafusos M3 por 12 e porcas M3 — vigilância de curta distância das laterais dianteiras
  • Módulo receptor infravermelho com parafusos M2 por 10 e porcas M2 e controle remoto — comando sem fio com mapa NEC e bateria CR2025 ou CR2032
  • Servo com parafusos M2 por 10 e porcas M3 — pan de um eixo que aponta o ultrassom na varredura de desvio
  • Sensor ultrassônico HC-SR04 com suporte, parafusos M1.6 por 10 com porcas M1.6, braço do servo em forma de 1 com parafusos M1.4 por 5 e parafuso do kit do servo — telemetria sobre varredura com eixo acústico solidário ao eixo do servo
  • Caixa de baterias para 2 células 18650 com chave e parafusos M3 por 8 com porcas M3 — barramento de cerca de 7,4 V nominal que alimenta lógica e potência em paralelo
  • Chicotes XH2.54, jumpers Dupont, cabo de bateria e cabo USB Tipo-C de dados — interligação de sinal e potência com distinção obrigatória entre cabo de carga e cabo de dados
  • Pacote de aplicativo KUONGSHUN em APK para Android na pasta 4_Android_Software_Installation_Package — comando remoto alternativo pelo mesmo AP do robô

Somatória: 16 grupos, totalizando mais de 120 peças individuais entre estrutura, fixadores, placas, sensores, chicotes e acessórios, que compõem um robô 4WD completo com um ESP32, driver duplo, 4 motores, 8 canais de sensoriamento, servo de varredura, comando infravermelho e comando WiFi em dois firmwares integrados.

Mapa de pinos e protocolos (consolidado dos 12 .ino reais)

A tabela abaixo foi extraída dos doze sketches do fabricante. É a referência única de fiação: qualquer fio fora dela impede o modo correspondente sem acusar erro de compilação.

SubsistemaPino ESP32DireçãoProtocoloObservação
PWM velocidade AGPIO 25 via canal LEDC 3SaídaLEDC 5 kHz 8 bitsledcWrite(ENA, 0-255) com duty D=valor/255D = valor/255
PWM velocidade BGPIO 13 via canal LEDC 4SaídaLEDC 5 kHz 8 bitsMesmo duty do canal A no cruzeiro
Sentido ponte AGPIO 27 IN1 e GPIO 26 IN2SaídaDigitalAvante LOW/HIGH, ré HIGH/LOW
Sentido ponte BGPIO 12 IN3 e GPIO 14 IN4SaídaDigitalMesma convenção do canal A
Gatilho ultrassomGPIO 19 TRIGSaídaPulso 10 usDispara trem de 8 ciclos a 40 kHz
Eco ultrassomGPIO 18 ECHOEntradapulseIn/58Largura em us dividida por 58 resulta em cm
Servo do ultrassomGPIO 4SaídaPPM 20 msESP32Servo com pulsos de 500 a 2500 us, centro 90 graus
Desvio IR esquerdoGPIO 33EntradaDigital LM393Nível baixo com obstáculo
Desvio IR direitoGPIO 32EntradaDigital LM393Nível baixo com obstáculo
Fotossensor esquerdoGPIO 35EntradaADC 12 bitsTensão cai com mais luz
Fotossensor direitoGPIO 34EntradaADC 12 bitsTensão cai com mais luz
Linha esquerdaGPIO 5EntradaDigital0 é preto, 1 é branco
Linha centroGPIO 17EntradaDigitalCanal central da decisão de marcha
Linha direitaGPIO 16EntradaDigitalCanal direito da decisão de curva
Receptor infravermelhoGPIO 15EntradaNEC 38 kHzMapa de códigos na Lição 10
Rede de comandoAP KUONGSHUN-AD178 senha 12345678RFWiFi 802.11 + HTTPPortal em 192.168.4.1, canal 9, TX 19,5 dBm

O controle de velocidade usa PWM — pulse-width modulation, modulação por largura de pulso — com período TT e largura tont_{on}. O ciclo de trabalho e a tensão média no motor são:

D=tonTD = \frac{t_{on}}{T} Vmed=D⋅(Vbat−Vdrop)V_{med} = D \cdot (V_{bat} - V_{drop})

onde DD é o duty cycle, VbatV_{bat} a tensão do pack 18650 e VdropV_{drop} a queda de cerca de 2 V2\,\text{V} na ponte L298N. No ESP32, ledcSetup(canal, 5000, 8) cria D=valor/255D = valor/255 em 5 kHz5\,\text{kHz}. O torque médio cresce com DD, mas abaixo do limiar de atrito estático o motor zumbe sem partir, por isso os exemplos usam 120120 a 255255. A potência elétrica entregue é P=Vmed⋅IP = V_{med} \cdot I, e a corrente de estol dos quatro TT somada excede o que o USB fornece, razão pela qual tração só se valida com as duas células instaladas e chave ligada. A Lei de Ohm V=R⋅IV = R \cdot I explica a queda nos enrolamentos e nos transistores da ponte, que aquecem em marcha lenta prolongada.

Montagem Mecânica

A montagem segue os 13 passos do guia do fabricante na ordem original do PDF, do suporte dos motores até as rodas. A regra de ouro é mecânica antes de energia: nenhum teste com bateria antes da fiação conferida fio a fio, e nenhum aperto final antes da validação de giro livre e curso do servo. Cada passo indica peças, cuidados e validação, com a imagem oficial correspondente. A numeração de passos do PDF não é sequencial no final (o diagrama de fiação aparece como passo 13 antes da união dos andares como passo 11): este curso preserva a numeração original do fabricante.

Passo 1.1 — Montar os suportes nos motores

Fixe cada um dos 4 suportes metálicos no corpo do motor TT com dois parafusos M3 por 30 de cabeça redonda e porcas M3, com o ressalto (bump) do suporte para o lado oposto ao motor. Repita o mesmo método nos quatro motores. Validação: suporte solidário ao corpo sem folga, com o ressalto livre para o encaixe no chassi. Teoria de apoio: o suporte converte o torque de reação do motor em força no chassi pela segunda lei de Newton F=m⋅aF = m \cdot a aplicada ao par ação-reação. Suporte frouxo absorve parte do curso como vibração e rouba tração, além de desalinhar o eixo e gerar arrasto lateral permanente.

Passo 1.1 — Suportes metálicos fixados nos 4 motores TT

💡 Dica: monte os quatro conjuntos motor-suporte em lote antes de ir ao chassi. A repetição do mesmo gesto garante simetria de aperto nos quatro cantos.

Passo 1.2 — Fixar os motores na base inferior

Prenda os 4 conjuntos na placa inferior de acrílico com parafusos M3 por 12 de cabeça chata, com o ressalto dos motores voltado para fora. Validação: eixos coaxiais dois a dois e motores paralelos entre si, sem torção da placa. Teoria de apoio: eixos não paralelos decompõem a tração em componente útil longitudinal F⋅cos⁡θF \cdot \cos\theta e componente parasita lateral F⋅sin⁡θF \cdot \sin\theta, onde θ\theta é o erro angular. Mesmo poucos graus de erro cobram curva permanente para um lado que o seguidor de linha tentará corrigir com zigue-zague.

Passo 1.2 — Quatro motores TT fixados na base inferior com ressaltos para fora

⚠️ Atenção: parafuso de cabeça chata deve facear o acrílico sem sobressair. Cabeça saliente vira ponto de encalhe do chassi em soleiras e degraus da pista.

Passo 2 — Instalar o módulo seguidor de linha

Conecte antes o cabo Dupont XH2.54 de 5 vias no conector do módulo de 3 vias, fixe o módulo sob o bico da base inferior com 4 parafusos M3 por 12 e porcas M3. Validação: módulo paralelo à placa, emissores voltados ao solo, cabo passado sem tração para o vão superior. Teoria de apoio: cada canal tem LED emissor infravermelho e fototransistor receptor. O preto absorve e o branco reflete, de modo que a tensão do divisor varia com a refletância ρ\rho. O comparador de cada via decide preto ou branco contra o limiar do trimpot:

Vcomp>Vref⇒branco (1),Vcomp<Vref⇒preto (0)V_{comp} > V_{ref} \Rightarrow branco\ (1),\quad V_{comp} < V_{ref} \Rightarrow preto\ (0)

onde VrefV_{ref} é ajustado pelo trimpot. Altura excessiva dilui o contraste e exige recalibração na pista definitiva.

Passo 2 — Módulo seguidor de 3 vias sob o bico com cabo XH2.54

Passo 3 — Instalar a placa driver L298N

Rosqueie primeiro as 4 colunas de cobre M3 por 10 na placa inferior, apoie o L298N sobre elas de baixo para cima e trave com 8 parafusos M3 por 8, respeitando a orientação com bornes de motores para as laterais. Validação: dissipador sem encostar no acrílico e bornes acessíveis para a fiação dos motores. Teoria de apoio: cada ponte H tem quatro chaves em H que invertem a polaridade no motor. A queda Vdrop≈2 VV_{drop} \approx 2\,\text{V} nos Darlingtons converte-se em calor P=Vdrop⋅IP = V_{drop} \cdot I, por isso marcha lenta prolongada com duty alto aquece o dissipador e exige pausas entre ensaios.

Passo 3 — Driver L298N sobre colunas com orientação dos bornes

⛔ Perigo: nunca alimente o L298N invertido. Inversão de bateria destrói a ponte e a expansão em segundos. Confira polaridade duas vezes antes do primeiro ON.

Passo 4 — Instalar as colunas de cobre do vão

Fixe as 6 colunas duplas M3 por 40 na base inferior com parafusos M3 por 12, exatamente nos furos indicados, sem trocar posição. Elas definem o vão entre andares que acomoda driver, expansão e chicotes. Validação: seis topos no mesmo plano, sem coluna bamba. Teoria de apoio: o vão adequado garante convecção ao redor do L298N e raio de curvatura suave aos chicotes, evitando fadiga por dobra fechada que rompe cobre com a vibração da marcha dos quatro motores.

Passo 4 — Seis colunas de cobre do vão entre andares

Passo 5 — Montar controladora ESP32 e expansão na placa superior

Distinga frente e verso da placa superior de acrílico, rosqueie as 4 colunas M3 por 10, apoie a expansão e trave com 8 parafusos M3 por 8, e encaixe a controladora ESP32 nos barramentos da expansão até o fundo sem forçar pinos. Validação: placas paralelas, USB Tipo-C acessível pela lateral para upload, sem pinos tortos. Teoria de apoio: a expansão é um distribuidor passivo de alimentação e sinal. O contato intermitente em barra de pinos gera reset espúrio por queda de VccV_{cc} durante pico de corrente dos quatro motores, sintoma clássico de placa mal assentada que se confunde com defeito de firmware.

Passo 5 — Controladora ESP32 e expansão na placa superior

Passo 6.1 — Montar os fotossensores

Fixe os 2 fotossensores na dianteira com parafusos M2 por 10 de cabeça redonda e porcas M2, respeitando a orientação com as janelas sensíveis para frente e para cima. Validação: sensores simétricos em relação ao eixo longitudinal, sem sombra do casco sobre as janelas. Teoria de apoio: o LDR varia a resistência com a iluminância EE aproximadamente por R∝E−γR \propto E^{-\gamma}, e o divisor converte em tensão que cai com mais luz. Sombra assimétrica do casco cria offset permanente entre lados que o firmware interpreta como fonte lateral inexistente.

Passo 6.1 — Dois fotossensores na dianteira

Passo 6.2 — Montar os sensores de desvio IR

Fixe os 2 sensores de desvio infravermelho com parafusos M3 por 12 de cabeça chata e porcas M3, emissores voltados para frente e para fora. Validação: sensores simétricos, sem inclinação para o solo. Teoria de apoio: o par emissor-receptor com LM393 detecta reflexão próxima e entrega nível baixo com obstáculo entre 2 cm e 7 cm. Apontar para o solo gera falso positivo permanente com o reflexo do piso claro, que o desvio interpreta como parede e trava a marcha à frente.

Passo 6.2 — Dois sensores de desvio IR na dianteira

Passo 6.3 — Montar o receptor infravermelho

Fixe o módulo receptor com 2 parafusos M2 por 10 e porcas M2, com o olho receptor para frente e sem obstrução do casco. Validação: olho desobstruído em 180 graus frontais. Teoria de apoio: o receptor usa fotodiodo com filtro de 38 kHz e decodificador NEC. Obstrução parcial reduz o alcance de metros para centímetros e gera quadros corrompidos que o firmware descarta, sintoma que se confunde com pilha fraca do controle.

Passo 6.3 — Receptor infravermelho na dianteira

Passo 7 — Montar o servo

Fixe o servo na furação da placa com 2 parafusos M2 por 10 e porcas M3, com a face do eixo voltada para frente e o fio passando pelo furo redondo inferior. Validação: servo perpendicular, sem folga rotacional na caixa. Teoria de apoio: o servo será o pan do ultrassom. O centro mecânico a 90 graus deve coincidir com a frente geométrica, e qualquer desalinhamento aqui vira erro sistemático de ±10∘\pm 10^{\circ} na varredura de desvio, que o firmware não compensa.

Passo 7 — Servo do ultrassom com fio pelo furo inferior

Passo 8.1 — Fixar o ultrassom no suporte

Prenda o HC-SR04 no suporte com 4 parafusos M1.6 por 10 e porcas M1.6, cápsulas para frente e sem tocar os pinos. Validação: sensor firme, transdutores livres, pinos acessíveis para jumpers. Teoria de apoio: o ultrassom mede por tempo de voo com d=vsom⋅t/2d = v_{som} \cdot t/2. O suporte rígido garante que o eixo acústico acompanhe o ângulo comandado, e folga espalha o cone de 15∘15^{\circ} e degrada a comparação direita-esquerda do desvio.

Passo 8.1 — HC-SR04 no suporte do servo

Passo 8.2 — Fixar o braço do servo

Prenda o braço em forma de 1 no suporte com 2 parafusos autorroscantes M1.4 por 5, no furo indicado do suporte. Validação: braço solidário ao suporte sem jogo. Teoria de apoio: o braço é a alavanca entre eixo do servo e carga do ultrassom. O torque exigido é τ=F⋅d\tau = F \cdot d, e folga aqui vira histerese angular que desloca a leitura lateral em centímetros no alvo a 30 cm.

Passo 8.2 — Braço em forma de 1 no suporte

Passo 8.3 — Unir suporte ao eixo do servo

Encaixe o braço no eixo do servo com o parafuso pequeno do kit do servo, com a frente positiva para frente. Validação: conjunto gira solidário ao eixo sem escorregar e sem cabo tracionando. Antes de apertar o definitivo, centralize o servo em 90 graus com o sketch da Lição 5, conforme o ritual de correção do tutorial: se a varredura ficar assimétrica, volte o servo a 90 graus pelo reset da placa, solte o braço com o cabo desplugado e reencaixe alinhado.

Passo 8.3 — Torre do ultrassom unida ao eixo do servo

⚠️ Atenção: não force o eixo do servo com a mão para alinhar. O trem de engrenagens interno quebra dentes com torque manual. Centralize sempre pelo firmware da Lição 5.

Passo 9 — Montar a caixa de baterias

Fixe a caixa de 2 células 18650 com 4 parafusos M3 por 8 e porcas M3, respeitando a orientação com fios para trás. Não instale as células ainda. Validação: caixa firme, fios livres até o L298N e a expansão. Teoria de apoio: duas células em série somam tensão Vpack=V1+V2≈7,4 VV_{pack} = V_1 + V_2 \approx 7{,}4\,\text{V} nominal e mantêm a capacidade em ampère-hora. Chave desligada durante a fiação evita curto acidental de ferramenta sobre os bornes.

Passo 9 — Caixa 18650 com chave na placa

⚠️ Atenção: use apenas células 18650 íntegras, sem amassados ou capa rasgada, e sempre em par com cargas semelhantes. Célula danificada é risco de incêndio.

Passo 13 — Diagrama de fiação (ordem original do PDF)

Com a chave desligada e sem baterias, execute o diagrama oficial: motores aos bornes do L298N com a posição relativa da frente do carro para a esquerda, enables e sentidos à expansão conforme o mapa de pinos, TRIG e ECHO do ultrassom, sinal do servo, três canais da linha, dois desvios IR, dois fotossensores, sinal do receptor IR e cabo de bateria em paralelo na expansão e no L298N. Observe cor e posição de cada pino. Validação: cada fio puxado de leve sem soltar, polaridade da bateria conferida, USB Tipo-C de dados separado do cabo de carga. Teoria de apoio: a malha de potência carrega ampères com queda V=R⋅IV = R \cdot I nos contatos, enquanto a malha de sinal carrega microampères sensível a ruído. Trançar sinal longe dos fios de motor reduz diafonia no eco do ultrassom.

Passo 13 — Diagrama oficial de fiação de sinal e potência

⛔ Perigo: confira duas vezes o paralelo da bateria na expansão e no L298N antes de inserir as células. Curto no barramento funde trilhas e pode inflar células 18650.

Passo 11 — Unir as bases superior e inferior

Apoie a placa superior sobre as seis colunas de 40 mm, passe os chicotes pelo vão sem prensar e trave com 6 parafusos M3 por 12. Validação: vão uniforme, nenhum fio esmagado, torre do ultrassom livre acima do casco. Teoria de apoio: a compressão de chicote contra coluna cria falha intermitente por vibração, o defeito mais difícil de diagnosticar porque some com o robô parado na bancada e reaparece em marcha.

Passo 11 — União dos andares sobre as colunas

Passo 12 — Montar as rodas

Encaixe as 4 rodas nos eixos dos TT e trave cada uma com parafuso autorroscante M2 por 25. Validação: rodas coplanares, sem bamboleio, giro livre sem roçar no acrílico. Teoria de apoio: excentricidade de roda injeta oscilação periódica com frequência f=v/(2πr)f = v/(2\pi r), que o seguidor de linha interpreta como erro de pista e corrige com zigue-zague. Roda bem centrada é pré-requisito de linha lisa. Inspeção final: chassi completo nos dois andares com torre do ultrassom ao centro.

Passo 12 — Quatro rodas nos eixos TTRobô AD178 montado com 4 rodas e conferência final

Preparação do Ambiente

A preparação instala a cadeia completa de gravação antes do primeiro upload: IDE do Arduino, driver do conversor serial CH340, núcleo ESP32 e bibliotecas do kit. Nenhuma das 12 lições compila sem as quatro camadas, e a maioria dos erros de porta e de biblioteca nasce aqui. Reserve uma hora calma, com cabo Tipo-C de dados conhecido e internet estável para o gerenciador de placas. A ordem abaixo espelha as três pastas de 2_Programming_Preparation do fabricante: instalação da IDE e CH340, plugin ESP32 e bibliotecas.

IDE do Arduino e driver CH340

Baixe a IDE no site oficial do Arduino e instale a versão estável para Windows ou macOS conforme o guia do fabricante. Conecte a ESP32 com cabo Tipo-C de dados e observe se o sistema cria uma porta serial. Se aparecer apenas COM1 no Windows ou nenhum dispositivo USB com VID 0x1A86 no macOS, instale manualmente o pacote CH340 da pasta de recursos do kit e reinicie. Validação: Gerenciador de Dispositivos mostra a porta sem triângulo amarelo, e a IDE lista a COM correspondente em Ferramentas e Porta. Teoria de apoio: o CH340 converte USB em UART TTL para o bootloader do ESP32. Cabo de carga sem fios de dados alimenta a placa sem criar porta, o erro mais comum do primeiro upload, que se resolve trocando o cabo e não o driver.

Download da IDE do Arduino no site oficialInstalação manual do driver CH340 no Windows

⚠️ Atenção: use cabo Tipo-C de dados, não cabo de carga. Sem fios D+ e D-, a placa acende mas a porta nunca aparece.

Núcleo ESP32 2.0.12 na IDE

Abra Arquivo e Preferências e adicione as URLs do gerenciador nas URLs Adicionais para Gerenciadores de Placas, conforme o PDF do fabricante (http://arduino.esp8266.com/stable/package_esp8266com_index.json e https://www.arduino.me/package_esp32_index.json). Em Ferramentas, Placa e Gerenciador de Placas, busque esp32 e instale a versão 2.0.12, sem aceitar a latest automaticamente. Selecione ESP32 Dev Module em Ferramentas e Placa. Validação: o menu mostra ESP32 Dev Module e o exemplo WiFiScan compila sem erro. Teoria de apoio: o núcleo entrega compilador Xtensa, bootloader, WiFi, LEDC e ADC. A versão 2.0.12 preserva a API ledcSetup e ledcAttachPin usada nos 12 sketches do kit. O núcleo 3.x removeu essas funções e quebra 100 por cento dos códigos do AD178 com erro de compilação, por isso fixar a versão é inegociável e o primeiro item do troubleshooting.

URL do gerenciador ESP32 nas preferênciasInstalação do núcleo ESP32 2.0.12 e seleção do Dev Module

⛔ Perigo: núcleo ESP32 3.x não compila os sketches do kit. Se a compilação acusar ledcSetup was not declared, volte o núcleo para 2.0.12 no Gerenciador de Placas.

Tabela-mestre de bibliotecas do curso

Todas as dependências externas do curso estão consolidadas abaixo com versão pinada e duas vias de instalação. Instale uma vez e confira antes de cada lição pelo quadro da lição. As versões são as do pacote do fabricante, pasta 3. Add library files/libraries.

Biblioteca (#include)Buscar no GerenciadorVersão usada no cursoDownload ZIP (espelho oficial)Lições
ESP32Servo por Kevin HarringtonESP32Servo1.1.2ESP32Servo no GitHub madhephaestus/ESP32Servo5, 7, 8, 11, 12
IRremote por shirriff, z3t0, ArminJoIRremote4.1.2IRremote no GitHub Arduino-IRremote/Arduino-IRremote10, 12
WiFi do núcleo ESP32embutida no núcleo 2.0.12, nada a instalar2.0.12acompanha o pacote ESP32 da Espressif11, 12
esp_http_server do IDFembutida no núcleo, nada a instalar2.0.12acompanha o pacote ESP3211, 12

Via Gerenciador, recomendada: Sketch, Incluir Biblioteca, Gerenciar Bibliotecas, buscar o nome exato, selecionar a versão pinada no seletor, Instalar e reiniciar a IDE. Via ZIP: copiar a pasta libraries do kit para o caminho do Sketchbook (Arquivo, Preferências, ver o caminho sem alterá-lo) ou Sketch, Incluir Biblioteca, Adicionar Biblioteca .ZIP, e reiniciar a IDE. Conferência: Arquivo, Exemplos, ESP32Servo, Sweep deve abrir e compilar para ESP32 Dev Module sem No such file or directory, e Arquivo, Exemplos, IRremote, SimpleReceiver deve abrir e compilar. Erro típico ESP32Servo.h: No such file or directory indica pasta libraries/ESP32Servo ausente ou IDE aberta durante a cópia sem reiniciar. O código IR do kit usa a API moderna da 4.x (irrecv.decode(), irrecv.decodedIRData.decodedRawData, irrecv.resume()): nunca instale a série 2.x nestas lições, pois a API antiga (decode(&results), results.value) não compila com estes sketches.

Cópia da pasta libraries e conferência dos exemplos

💡 Dica: copie a pasta libraries do kit para a pasta de sketches sem alterar o caminho do Sketchbook, ou instale pelas versões pinadas acima. Nunca misture as duas vias com versões diferentes.

Ferramentas Oficiais Pinadas Deste Curso

Ferramenta / PacoteVersão usada no cursoOnde obter (oficial)Observação de bancada
Arduino IDE1.8.19 (série 1.8)Site oficial do ArduinoTelas idênticas às do tutorial do fabricante
Núcleo ESP322.0.12Gerenciador de Placas com a URL do PDFPreserva ledcSetup/ledcAttachPin dos 12 sketches
Driver USB-serial CH3403.8Pacote CH340 da pasta do kit ou wch.cnSem ele a COM não aparece; cabo precisa ser de dados
ESP32Servo1.1.2Gerenciador ou pasta libraries do kitServo do ultrassom e modos com varredura
IRremote4.1.2Gerenciador ou pasta libraries do kitAPI decodedIRData da Lição 10 e 12
APP de pilotagemKUONGSHUN.apk (pasta 4_Android_Software_Installation_Package)APK dentro do pacote do kitAndroid; iOS usa o app KUONGSHUN da loja

Primeiro upload e Monitor Serial

Com a placa conectada, selecione ESP32 Dev Module e a COM correta, abra o sketch da lição em 3_Code_and_Tutorial, clique em Verificar e depois em Carregar, e aguarde a barra de progresso com pontos e o aviso de sucesso. O ritual é idêntico nas 12 lições: abrir o programa pelo caminho indicado, conectar com Tipo-C de dados, selecionar placa e porta, carregar, desconectar o cabo, colocar o carro no chão, instalar as 2 baterias 18650 (versão sem bateria exige par próprio) e ligar a chave. Abra o Monitor Serial com o baud rate do sketch, 9600 em todas as lições, com retorno de linha adequado. Validação: mensagens de telemetria aparecem sem lixo de caracteres, o que confirma cristal, baud e aterramento íntegros. Teoria de apoio: o upload usa o bootloader ROM via RTS e DTR para reset automático. Falha de conexão com Failed to connect pede BOOT segurado na placa, cabo de dados e porta correta, nessa ordem, antes de suspeitar de driver.

Seleção da placa ESP32 Dev Module e da porta COMUpload com barra de progresso e aviso de sucesso

💡 Dica: grave a Lição 1 antes de qualquer sensor. Ela prova que IDE mais núcleo mais driver mais cabo mais porta mais ponte H estão íntegros. Só então passe aos sensores com o robô suspenso e a chave em OFF durante o upload.

Lições na Ordem do Tutorial do Fabricante

As doze lições abaixo seguem a ordem do PDF ESP32 WiFi Multifunctional Robot Car V2.0.24.7.17, sem reordenar nem fundir. Cada lição mantém o padrão interno: teoria do fenômeno com equações, componentes, conexões, bibliotecas, firmware comentado linha a linha, gravação com resultado esperado e avisos. Grave uma lição por vez e valide antes de avançar, porque os modos integrados 8, 9 e 12 pressupõem tração, ultrassom, servo, linha, desvio, fotossensores e infravermelho já funcionais isoladamente.

Lição 1 — Colocar o Carro em Movimento

A primeira lição valida a cadeia completa de potência: ESP32, L298N, quatro TT e baterias. O sketch aciona as cinco combinações de ponte em sequência de dois segundos: avante, ré, esquerda, direita e parada, em ciclo. É o ensaio de fumaça do robô: se ele anda nas quatro direções, a fiação de potência e os sentidos estão corretos. A teoria central é a ponte H: cada lado tem dois terminais comandados por IN1/IN2 e IN3/IN4, com os enables em PWM liberando corrente. A tabela de verdade implementada é avante LOW/HIGH em ambas as pontes, ré HIGH/LOW, esquerda com pontes opostas num sentido e direita com o oposto, parada com duty zero nos dois enables. A velocidade usa carSpeed 150, logo o duty é:

D=150255≈0,59D = \frac{150}{255} \approx 0{,}59 Vmed=0,59⋅(Vbat−Vdrop)≈0,59⋅5,4 V≈3,2 VV_{med} = 0{,}59 \cdot (V_{bat} - V_{drop}) \approx 0{,}59 \cdot 5{,}4\,\text{V} \approx 3{,}2\,\text{V}

onde VmedV_{med} é a tensão média em cada motor. O período de dois segundos por estado deriva de delay(2000), tempo suficiente para observar o sentido sem o robô sair da mesa se elevado no suporte. A mensagem de cada estado no Monitor Serial a 9600 bps confirma a máquina de estados mesmo sem ver o robô andar. A cinemática diferencial prevê que avante soma vR=vLv_R = v_L, ré soma negativa, e giros usam lados opostos para ω\omega puro com R=0R = 0. Se um lado gira invertido, inverta os dois fios dele no borne do L298N com a bateria removida, nunca no código, para preservar o mapa de sentidos das lições integradas.

Componente necessárioFunção no ensaio
Chassi montado com 4 TT e rodasPlanta de tração integral sob teste
ESP32 com expansãoGeração dos sentidos e PWMs
Driver L298NEstágio de potência das duas pontes
2 células 18650 com chaveBarramento de potência da tração
Cabo Tipo-C de dadosUpload do sketch de marcha
Lição 1 — Abertura do programa de marcha no caminho do kitLição 1 — Carro em marcha avante, ré, giros e parada em ciclo
Conexão validadaPinoSentido
Enable A (PWM)GPIO 25 via LEDC 3Saída PWM 150 de 255
IN1/IN2GPIO 27/26Saída digital de sentido do lado A
IN3/IN4GPIO 12/14Saída digital de sentido do lado B
Enable B (PWM)GPIO 13 via LEDC 4Saída PWM 150 de 255

📦 Bibliotecas desta lição: nenhuma — o código usa digitalWrite(), ledcWrite() do núcleo e delay() nativos, sem biblioteca externa.

//www.kuongshun.com — Lição 1 marcha automática (pasta 1_Auto_Move), comentários didáticos em português

//define L298n module IO Pin — define os pinos do módulo L298N
#define ENA 3            // cria constante do canal LEDC 3 (velocidade do lado A)
#define ENA_PIN 25       // cria constante do pino GPIO 25 (saída PWM do lado A)
#define IN1 27           // cria constante do pino GPIO 27 (sentido 1 do lado A)
#define IN2 26           // cria constante do pino GPIO 26 (sentido 2 do lado A)
#define IN3 12           // cria constante do pino GPIO 12 (sentido 1 do lado B)
#define IN4 14           // cria constante do pino GPIO 14 (sentido 2 do lado B)
#define ENB 4            // cria constante do canal LEDC 4 (velocidade do lado B)
#define ENB_PIN 13       // cria constante do pino GPIO 13 (saída PWM do lado B)
#define carSpeed 150     // cria constante de velocidade (duty 150 de 255)

void Forward(){          // define a rotina de marcha avante
  ledcWrite(ENA, carSpeed); // aplica PWM 150 no canal A (libera ponte A)
  ledcWrite(ENB, carSpeed); // aplica PWM 150 no canal B (libera ponte B)
  digitalWrite(IN1, LOW);   // coloca IN1 em nível baixo (sentido avante A)
  digitalWrite(IN2, HIGH);  // coloca IN2 em nível alto (sentido avante A)
  digitalWrite(IN3, LOW);   // coloca IN3 em nível baixo (sentido avante B)
  digitalWrite(IN4, HIGH);  // coloca IN4 em nível alto (sentido avante B)
  Serial.println("Forward"); // envia mensagem ao monitor serial
}

void Back() {            // define a rotina de marcha ré
  ledcWrite(ENA, carSpeed); // aplica PWM 150 no canal A
  ledcWrite(ENB, carSpeed); // aplica PWM 150 no canal B
  digitalWrite(IN1, HIGH);  // inverte o lado A para ré
  digitalWrite(IN2, LOW);   // inverte o lado A para ré
  digitalWrite(IN3, HIGH);  // inverte o lado B para ré
  digitalWrite(IN4, LOW);   // inverte o lado B para ré
  Serial.println("Back");   // envia mensagem ao monitor serial
}

void Right() {           // define a rotina de giro à direita
  ledcWrite(ENA, carSpeed); // aplica PWM 150 no canal A
  ledcWrite(ENB, carSpeed); // aplica PWM 150 no canal B
  digitalWrite(IN1, HIGH);  // lado A para ré (gira sobre o centro)
  digitalWrite(IN2, LOW);   // lado A para ré (gira sobre o centro)
  digitalWrite(IN3, LOW);   // lado B avante (gira sobre o centro)
  digitalWrite(IN4, HIGH);  // lado B avante (gira sobre o centro)
  Serial.println("Right");  // envia mensagem ao monitor serial
}

void Left() {            // define a rotina de giro à esquerda
  ledcWrite(ENA, carSpeed); // aplica PWM 150 no canal A
  ledcWrite(ENB, carSpeed); // aplica PWM 150 no canal B
  digitalWrite(IN1, LOW);   // lado A avante (gira sobre o centro)
  digitalWrite(IN2, HIGH);  // lado A avante (gira sobre o centro)
  digitalWrite(IN3, HIGH);  // lado B para ré (gira sobre o centro)
  digitalWrite(IN4, LOW);   // lado B para ré (gira sobre o centro)
  Serial.println("Left");   // envia mensagem ao monitor serial
}

void Stop() {            // define a rotina de parada
  ledcWrite(ENA, 0);     // zera o PWM do canal A (trava a ponte A)
  ledcWrite(ENB, 0);     // zera o PWM do canal B (trava a ponte B)
  Serial.println("Stop!"); // envia mensagem ao monitor serial
}

//before execute loop() function, — antes do loop, o setup executa uma vez
//setup() function will execute first and only execute once
void setup() {           // define a rotina de configuração inicial
  Serial.begin(9600);    // inicializa o monitor serial em 9600 bps
  pinMode(IN1, OUTPUT);  // configura IN1 como saída digital
  pinMode(IN2, OUTPUT);  // configura IN2 como saída digital
  pinMode(IN3, OUTPUT);  // configura IN3 como saída digital
  pinMode(IN4, OUTPUT);  // configura IN4 como saída digital
  pinMode(ENA_PIN, OUTPUT); // configura o pino do PWM A como saída
  pinMode(ENB_PIN, OUTPUT); // configura o pino do PWM B como saída
  ledcSetup(ENA,5000,8); // cria o canal LEDC 3 com 5 kHz e 8 bits
  ledcSetup(ENB,5000,8); // cria o canal LEDC 4 com 5 kHz e 8 bits
  ledcAttachPin(ENA_PIN,ENA); // vincula o GPIO 25 ao canal 3
  ledcAttachPin(ENB_PIN,ENB); // vincula o GPIO 13 ao canal 4
  Stop();                // parte parado por segurança
}

//Repeat execution — execução repetida do ciclo de marcha
void loop() {            // define o laço principal do ensaio
  Forward();  //go forward — avança por 2 segundos
  delay(2000);//delay 2000 ms — aguarda 2 segundos
  Back();     //go back — recua por 2 segundos
  delay(2000); // aguarda 2 segundos
  Left();     //turning left — gira à esquerda por 2 segundos
  delay(2000); // aguarda 2 segundos
  Right();    //turning right — gira à direita por 2 segundos
  delay(2000); // aguarda 2 segundos
  Stop();     //Stop — para por 2 segundos
  delay(2000); // aguarda 2 segundos
}

Gravação e operação: abra 3_Code_and_Tutorial/1_Auto_Move/1_Auto_Move.ino, selecione ESP32 Dev Module e a COM, carregue, desconecte o USB, ponha o carro no chão com as 2 baterias e ligue a chave. Resultado esperado: avante 2 s, ré 2 s, esquerda 2 s, direita 2 s, parada 2 s, em ciclo, com as mensagens no Serial quando conectado. Se um lado não gira, confira o borne desse lado no L298N e o enable correspondente antes de suspeitar do código.

Lição 2 — Sensor de Desvio por Infravermelho

A segunda lição valida os dois sensores de desvio IR como entradas digitais, antes de usá-los para desviar. O sketch lê GPIO 33 e GPIO 32 e imprime os valores a cada segundo. A teoria é a reflexão ativa com comparador LM393: o tubo emissor irradia infravermelho numa frequência portadora, o tubo receptor capta o retorno de superfícies próximas, e o comparador decide obstáculo contra o limiar do potenciômetro. Com obstáculo, a saída vai a nível baixo e o LED verde da plaquinha acende; sem obstáculo, fica em nível alto. A distância de detecção varia com a tensão de alimentação e a refletância do alvo:

d5V≈7 cm>d3,3V≈3,5 cmd_{5V} \approx 7\,\text{cm} > d_{3{,}3V} \approx 3{,}5\,\text{cm}

onde dd é o alcance máximo. Girar o potenciômetro no sentido horário aumenta o alcance e no anti-horário diminui, pela variação de VrefV_{ref} do comparador. Alvo preto absorve e rende alcance mínimo, alvo branco rende máximo, alvo pequeno rende menos que alvo grande. O ângulo de detecção é de 35 graus, então o feixe enxerga cone largo e não ponto.

Componente necessárioFunção no ensaio
2 sensores de desvio IR montadosEntradas digitais sob teste
ESP32 com expansãoLeitura dos GPIO 33 e 32
Cabo Tipo-C de dadosUpload e Monitor Serial
Obstáculo de teste (mão ou caixa)Alvo reflexivo para provocar nível baixo
Lição 2 — Upload do programa do sensor de desvioLição 2 — Princípio do par emissor-receptor infravermelho
Conexão validadaPinoSentido
Desvio esquerdo OUTGPIO 33Entrada digital
Desvio direito OUTGPIO 32Entrada digital
VCC dos módulos3,3 V ou 5 VAlimentação (alcance muda)
GND dos módulosGNDReferência comum

📦 Bibliotecas desta lição: nenhuma — o código usa digitalRead() e Serial nativos, sem biblioteca externa.

//www.kuongshun.com — Lição 2 sensor de desvio IR (pasta 2_Infrared_Obstacle_Avoidance_Sensor), comentários didáticos

#define IRPIN_L 33 // cria constante do pino do desvio esquerdo (GPIO 33)
#define IRPIN_R 32 // cria constante do pino do desvio direito (GPIO 32)
void Obstacle_Avoidance() { // define a rotina de leitura dos desvios
  int Right_IR_Value = 1; // cria variável do lado direito (1 é livre)
  int Left_IR_Value = 1;  // cria variável do lado esquerdo (1 é livre)
  Left_IR_Value = digitalRead(IRPIN_L);  // lê o nível do sensor esquerdo
  Right_IR_Value = digitalRead(IRPIN_R); // lê o nível do sensor direito
  Serial.print("Left_IR_Value:");  // rotula a telemetria da esquerda
  Serial.println(Left_IR_Value);   // imprime 0 com obstáculo ou 1 livre
  Serial.print("Right_IR_Value:"); // rotula a telemetria da direita
  Serial.println(Right_IR_Value);  // imprime 0 com obstáculo ou 1 livre
  Serial.println("");              // imprime linha em branco separadora
  delay(1000);                     // aguarda 1 segundo entre leituras
}

void setup(){            // define a rotina de configuração inicial
  Serial.begin(9600);    // inicializa o monitor serial em 9600 bps
  pinMode(IRPIN_L, INPUT); // configura o pino esquerdo como entrada
  pinMode(IRPIN_R, INPUT); // configura o pino direito como entrada
}

void loop(){             // define o laço principal do ensaio
  Obstacle_Avoidance();  // executa a leitura dos dois sensores

}

Gravação e operação: abra 3_Code_and_Tutorial/2_Infrared_Obstacle_Avoidance_Sensor/2_Infrared_Obstacle_Avoidance_Sensor.ino, carregue, abra o Monitor Serial em 9600 bps. Resultado esperado: com nada à frente, ambos imprimem 1; aproximando a mão de um lado, esse lado vira 0 e o LED verde da plaquinha acende. Se um canal fica sempre 0, gire o potenciômetro dele no anti-horário até voltar a 1 no livre e confira se o sensor não aponta para o solo.

💡 Dica: calibre os dois lados contra o mesmo alvo branco a 5 cm. Sensores com limiares diferentes viram curva permanente no desvio da Lição 8.

Lição 3 — Sinal de Busca de Luz

A terceira lição valida os dois fotossensores como entradas analógicas. O sketch lê GPIO 35 e GPIO 34 e imprime os valores a cada segundo. O título do PDF indica busca de luz e o corpo do passo a passo do fabricante reaproveita o texto do ultrassom: este curso segue o código real, que é de fotossensores, e o ensaio real, que é ver a tensão variar com a luz. A teoria é o divisor com LDR: o fotossensor é mais sensível à luz ambiente, e quanto menor a claridade menor a tensão de saída, ou seja, a tensão de saída cai quando a luz aumenta. A leitura do ESP32 converte pela referência de 12 bits:

Vin=ADC4095⋅3,3 VV_{in} = \frac{ADC}{4095} \cdot 3{,}3\,\text{V}

onde ADCADC é a contagem de 0 a 4095. Cobrir o sensor com o dedo eleva a contagem, apontar lanterna derruba, e a diferença entre lados indica a direção da fonte. Este ensaio prepara a Lição 9, que transforma a diferença em esterçamento proporcional.

Componente necessárioFunção no ensaio
2 fotossensores montadosEntradas analógicas sob teste
ESP32 com expansãoLeitura ADC dos GPIO 35 e 34
Lanterna ou celular com flashFonte de luz para provocar variação
Ambiente nem claro nem escuroPonto de operação no meio da escala
Lição 3 — Upload do programa dos fotossensoresLição 3 — Características do módulo fotossensível
Conexão validadaPinoSentido
Fotossensor esquerdo SGPIO 35Entrada analógica ADC1
Fotossensor direito SGPIO 34Entrada analógica ADC1
Intermediário dos módulos5 V ou 3,3 VAlimentação do divisor
GND dos módulosGNDReferência comum

📦 Bibliotecas desta lição: nenhuma — o código usa analogRead() nativo do ADC, sem biblioteca externa.

//www.kuongshun.com — Lição 3 fotossensores (pasta 3_Light_Seeking_Signal), comentários didáticos

volatile float Left_photosensitive;  // cria variável da luz esquerda (contagem ADC)
volatile float Right_photosensitive; // cria variável da luz direita (contagem ADC)
#define L_light_PIN 35 // cria constante do pino do fotossensor esquerdo
#define R_light_PIN 34 // cria constante do pino do fotossensor direito

void Light_Seeking() { // define a rotina de leitura dos fotossensores
  Left_photosensitive = analogRead(L_light_PIN);   // lê o ADC esquerdo (0 a 4095)
  Right_photosensitive = analogRead(R_light_PIN);  // lê o ADC direito (0 a 4095)
  Serial.print("Left_photosensitive:");  // rotula a telemetria da esquerda
  Serial.println(Left_photosensitive);   // imprime a contagem da esquerda
  Serial.print("Right_photosensitive:"); // rotula a telemetria da direita
  Serial.println(Right_photosensitive);  // imprime a contagem da direita
  Serial.println("");                    // imprime linha em branco separadora
  delay(1000);                           // aguarda 1 segundo entre leituras
}

void setup(){ // define a rotina de configuração inicial
  Left_photosensitive = 0;  // zera a variável da esquerda
  Right_photosensitive = 0; // zera a variável da direita
  Serial.begin(9600);       // inicializa o monitor serial em 9600 bps
  pinMode(L_light_PIN, INPUT); // configura o pino esquerdo como entrada
  pinMode(R_light_PIN, INPUT); // configura o pino direito como entrada
}

void loop(){ // define o laço principal do ensaio
  Light_Seeking(); // executa a leitura dos dois fotossensores

}

Gravação e operação: abra 3_Code_and_Tutorial/3_Light_Seeking_Signal/3_Light_Seeking_Signal.ino, carregue, abra o Monitor em 9600 bps num ambiente de meia-luz. Resultado esperado: valores intermediários nos dois lados, subindo ao cobrir com a mão e caindo com lanterna direta, com o lado iluminado sempre menor que o sombreado. Se um lado não varia, confira o pino S dele e se a janela não está tampada pelo chicote.

Lição 4 — Módulo Sensor Ultrassônico

A quarta lição valida a telemetria de distância por tempo de voo. O sketch dispara o TRIG, mede a largura do pulso de ECHO e converte em centímetros. A teoria é o sonar: o TRIG recebe pulso alto de pelo menos 10 us, o módulo emite trem de 8 ciclos a 40 kHz, e o ECHO sobe pelo tempo de ida e volta da onda. A distância deriva da velocidade do som:

d=t⋅vsom2=tus58 cmd = \frac{t \cdot v_{som}}{2} = \frac{t_{us}}{58}\,\text{cm}

onde tust_{us} é a largura do pulso em microssegundos e 58 é o divisor para centímetros (148 para polegadas). O alcance vai de 2 cm a 450 cm com precisão de até 0,2 cm, ângulo de 15 graus, corrente estática abaixo de 2 mA. O período de medição deve passar de 60 ms para o eco anterior não contaminar o disparo seguinte, e a função respeita isso com os delays internos. Superfícies inclinadas espalham o eco e encurtam a leitura, e objetos pequenos somem do cone a longa distância.

Componente necessárioFunção no ensaio
HC-SR04 na torre do servoTransdutor sob teste, parado a 90 graus
ESP32 com expansãoTRIG no GPIO 19, ECHO no GPIO 18
Obstáculo plano (livro ou parede)Alvo de eco estável
Trena ou réguaConferência da leitura contra distância real
Lição 4 — Upload do programa do ultrassomLição 4 — Diagrama de temporização do disparo e do eco
Conexão validadaPinoSentido
TRIG do HC-SR04GPIO 19Saída de disparo 10 us
ECHO do HC-SR04GPIO 18Entrada de largura de pulso
VCC do HC-SR045 VAlimentação do transdutor
GND do HC-SR04GNDReferência comum

📦 Bibliotecas desta lição: nenhuma — o código usa pulseIn() nativo, sem biblioteca externa.

//www.kuongshun.com — Lição 4 ultrassom (pasta 4_Ultrasonic), comentários didáticos

#define Echo 18 // cria constante do pino de eco (GPIO 18, entrada)
#define Trig 19 // cria constante do pino de disparo (GPIO 19, saída)
float checkdistance() { // define a rotina de medição em centímetros
  digitalWrite(Trig, LOW);   // garante TRIG baixo antes do disparo
  delayMicroseconds(2);      // aguarda 2 us de repouso
  digitalWrite(Trig, HIGH);  // sobe o TRIG para disparar
  delayMicroseconds(10);     // mantém 10 us de pulso de disparo
  digitalWrite(Trig, LOW);   // desce o TRIG e libera o trem de 40 kHz
  float Fdistance = pulseIn(Echo, HIGH); // mede a largura do eco em us
  Fdistance= Fdistance / 58;  // converte microssegundos em centímetros
  delay(10);     // aguarda 10 ms contra reverberação
  return (int)Fdistance; // retorna a distância como inteiro
}

void Ultrasonic_Sensor_Module() // define a rotina de telemetria no Serial
{
    int Distance = 0; // cria variável de distância zerada
    Distance = checkdistance(); // executa a medição real
    Serial.print("Distance:"); // rotula a telemetria
    Serial.print(Distance);    // imprime o valor em cm
    Serial.println("CM");      // imprime a unidade
    delay(100); // aguarda 100 ms até a próxima medição
}

void setup() // define a rotina de configuração inicial
{
    Serial.begin(9600);  // inicializa o monitor serial em 9600 bps
    pinMode(Trig, OUTPUT); // configura o disparo como saída
    pinMode(Echo, INPUT);  // configura o eco como entrada
}
void loop() // define o laço principal do ensaio
{
    Ultrasonic_Sensor_Module(); // executa a telemetria contínua
}

Gravação e operação: abra 3_Code_and_Tutorial/4_Ultrasonic/4_Ultrasonic.ino, carregue, abra o Monitor em 9600 bps com o sensor parado à frente. Resultado esperado: leituras estáveis em cm que acompanham a trena ao mover um livro de 10 cm a 100 cm. Se trava em zero, confira ECHO no 18; se trava alto, confira TRIG no 19 e o 5 V do módulo.

⚠️ Atenção: nunca confie numa única leitura para desviar. O eco falha em cortina, tecido e quina viva. As lições de navegação filtram por limiares e varredura justamente por isso.

Lição 5 — Servo de Varredura

A quinta lição valida e corrige o ângulo do servo que carrega o ultrassom. O sketch centraliza a 90 graus por um segundo e depois varre de 0 a 180 e de volta, em ciclo. É também o ritual de correção mecânica: se a varredura ficar assimétrica, dê reset na ESP32 para voltar a 90 graus, desplugue o cabo para o servo parar, solte o braço e reencaixe alinhado. A teoria é o PPM de 20 ms: o pulso alto de 0,5 ms a 2,5 ms define o ângulo, com centro em 1,5 ms:

0,5 ms⇒0∘,1,5 ms⇒90∘,2,5 ms⇒180∘0{,}5\,\text{ms} \Rightarrow 0^{\circ},\quad 1{,}5\,\text{ms} \Rightarrow 90^{\circ},\quad 2{,}5\,\text{ms} \Rightarrow 180^{\circ}

onde a correspondência intermediária é linear: 1,0 ms vale 45 graus e 2,0 ms vale 135 graus. O servo tem três fios: marrom GND, vermelho 5 V e laranja sinal. O comparador interno confronta o comando com o potenciômetro do eixo e move o motor até zerar a diferença, quando para. O programa permite mudar ângulo inicial e faixa de giro editando o write(90) e os limites dos laços.

Componente necessárioFunção no ensaio
Servo com torre do ultrassomAtuador sob teste e correção
ESP32 com expansãoPPM no GPIO 4
Chave Philips pequenaSoltar e reencaixar o braço se assimétrico
Lição 5 — Upload do programa de ângulo do servoLição 5 — Tabela de pulso contra ângulo do SG90
Conexão validadaPinoSentido
Sinal do servo (laranja)GPIO 4Saída PPM 20 ms
VCC do servo (vermelho)5 VAlimentação do motor interno
GND do servo (marrom)GNDReferência comum

📦 Bibliotecas desta lição: ESP32Servo por Kevin Harrington (versão 1.1.2).

  • Via Gerenciador (recomendado): Sketch > Incluir Biblioteca > Gerenciar Bibliotecas… → buscar ESP32Servo → selecionar 1.1.2 → Instalar → reiniciar a IDE.
  • Via ZIP: pasta libraries do kit copiada para o Sketchbook → reiniciar a IDE; espelho oficial em madhephaestus/ESP32Servo.
  • Conferência: Arquivo > Exemplos > ESP32Servo > Sweep deve abrir e compilar para ESP32 Dev Module sem No such file or directory.
//www.kuongshun.com — Lição 5 ângulo do servo (pasta 5_Servo_Angle), comentários didáticos

#include <ESP32Servo.h> // inclui a biblioteca do servo (instalada pelo quadro acima)
#define servo_PIN 4 // cria constante do pino de sinal do servo (GPIO 4)
Servo myservo; // cria o objeto do servo que recebe os comandos
void setup() // define a rotina de configuração inicial
{
    myservo.attach(servo_PIN,500,2500); // vincula o servo ao pino com pulsos de 500 a 2500 us
    myservo.write(90); // comanda 90 graus (frente) para referência mecânica
    delay(1000); // aguarda 1 segundo parado no centro
}

void loop() // define o laço principal da varredura
{
  for(int i=0;i<180;i++){ // percorre os ângulos de 0 a 179
    myservo.write(i); // comanda o ângulo atual da subida
    delay(10); // aguarda 10 ms entre graus (velocidade da varredura)
  }
  delay(1000); // aguarda 1 segundo no extremo
  for(int i=180;i>0;i--){ // percorre os ângulos de 180 a 1
    myservo.write(i); // comanda o ângulo atual da descida
    delay(10); // aguarda 10 ms entre graus
  }
  delay(1000); // aguarda 1 segundo antes de repetir
}

Gravação e operação: abra 3_Code_and_Tutorial/5_Servo_Angle/5_Servo_Angle.ino, carregue com USB, desconecte, ligue com baterias. Resultado esperado: torre vai a 90 graus, para 1 s e varre simétrica de lado a lado. Se assimétrica, execute o ritual de reset, desplugue, solte o braço e reencaixe à frente, e repita até simétrica.

Lição 6 — Seguidor de Linha de 3 Canais

A sexta lição fecha a primeira malha autônoma: o carro anda sobre a linha preta com os três canais. O sketch lê GPIO 5, 17 e 16 e aplica a lógica do fabricante: só o centro no preto avança devagar, borda no preto esterça para esse lado, tudo branco para, tudo preto avança pleno. A teoria é a mesma reflexão da Lição 2 por via, agora com três pontos que estimam posição e erro lateral. A tabela de decisão implementa um controlador bang-bang com marchas reduzidas nas curvas suaves:

e=R−L,e>0⇒direita, e<0⇒esquerda, e=0⇒frentee = R - L,\quad e > 0 \Rightarrow \text{direita},\ e < 0 \Rightarrow \text{esquerda},\ e = 0 \Rightarrow \text{frente}

onde LL e RR são os canais laterais e o centro ancora a marcha. As marchas usam carSpeed 200 no cruzeiro, 60 por cento no avante lento e 50 a 60 por cento na roda interna das curvas, o que reduz o raio sem parar a roda externa. A pista exige faixa preta sobre fundo claro com curvas suaves: raio fechado demais joga o carro para fora porque a correção chega atrasada à velocidade de cruzeiro.

Componente necessárioFunção no ensaio
Módulo de 3 vias calibradoMalha de posição sobre a pista
Chassi completo com bateriasPlanta que executa a decisão
Pista com fita preta em fundo claroReferência de contraste
Chave de calibração dos trimpotsAjuste via a via contra preto e branco
Lição 6 — Upload do programa seguidor de linhaLição 6 — Carro sobre a pista de linha preta
Conexão validadaPinoSentido
Linha esquerdaGPIO 5Entrada digital (0 preto)
Linha centroGPIO 17Entrada digital (0 preto)
Linha direitaGPIO 16Entrada digital (0 preto)
Tração25/27/26 e 12/14/13Saídas da Lição 1 com carSpeed 200

📦 Bibliotecas desta lição: nenhuma — o código usa digitalRead(), ledcWrite() e Serial nativos, sem biblioteca externa.

//www.kuongshun.com — Lição 6 seguidor de linha (pasta 6_Line_Tracing_Car; trechos de decisão; marchas seguem a Lição 1)

//Line Tracking IO define — define os pinos do seguidor de linha
#define LEFT_LINE_TRACJING          5  // cria constante da via esquerda (GPIO 5)
#define CENTER_LINE_TRACJING        17 // cria constante da via central (GPIO 17)
#define right_LINE_TRACJING         16 // cria constante da via direita (GPIO 16)
int Left_Tra_Value;   // cria variável da leitura esquerda
int Center_Tra_Value; // cria variável da leitura central
int Right_Tra_Value;  // cria variável da leitura direita (0 é preto)

// ... marchas Forward, Forward_slow, Back, Left, Left_single, Left_small,
// ... Right, Right_single, Right_small, Stop — mesma ponte da Lição 1,
// ... com carSpeed 200 e frações 0.6/0.5/0.7 na roda interna das curvas.

void setup() { // define a rotina de configuração inicial
  Serial.begin(9600); // inicializa o monitor serial em 9600 bps
  pinMode(LEFT_LINE_TRACJING, INPUT);   // configura a via esquerda como entrada
  pinMode(CENTER_LINE_TRACJING, INPUT); // configura a via central como entrada
  pinMode(right_LINE_TRACJING, INPUT);  // configura a via direita como entrada
  pinMode(IN1, OUTPUT); //before useing io pin, pin mode must be set first
  pinMode(IN2, OUTPUT); // configura IN2 como saída (ver Lição 1)
  pinMode(IN3, OUTPUT); // configura IN3 como saída (ver Lição 1)
  pinMode(IN4, OUTPUT); // configura IN4 como saída (ver Lição 1)
  pinMode(ENA_PIN, OUTPUT); // configura o PWM A como saída
  pinMode(ENB_PIN, OUTPUT); // configura o PWM B como saída
  ledcSetup(ENA,5000,8); // cria o canal LEDC 3 com 5 kHz e 8 bits
  ledcSetup(ENB,5000,8); // cria o canal LEDC 4 com 5 kHz e 8 bits
  ledcAttachPin(ENA_PIN,ENA); // vincula o GPIO 25 ao canal 3
  ledcAttachPin(ENB_PIN,ENB); // vincula o GPIO 13 ao canal 4
}

void loop() { // define o laço da malha de linha
  Left_Tra_Value = digitalRead(LEFT_LINE_TRACJING);     // lê a via esquerda
  Center_Tra_Value = digitalRead(CENTER_LINE_TRACJING); // lê a via central
  Right_Tra_Value = digitalRead(right_LINE_TRACJING);   // lê a via direita
  Serial.println("l,c,r:"); // rotula a telemetria das três vias
  Serial.println(Left_Tra_Value);   // imprime a esquerda
  Serial.println(Center_Tra_Value); // imprime o centro
  Serial.println(Right_Tra_Value);  // imprime a direita
  if (Left_Tra_Value ==1 && Center_Tra_Value ==0 && Right_Tra_Value ==1)
  { // se só o centro vê preto, o carro está centrado
    Forward_slow(); // avança devagar segurando o centro
  }
  else if (Left_Tra_Value ==1 && Center_Tra_Value ==1 && Right_Tra_Value ==0)
  { // se centro e direita veem preto, a curva é à direita
    Right_small(); // curva suave à direita sem parar a esquerda
  }
  else if (Left_Tra_Value ==1 && Center_Tra_Value ==0 && Right_Tra_Value ==0)
  { // se a direita saiu da linha, a curva é fechada
    Right_single(); // gira à direita sobre a roda esquerda
  }
  else if (Left_Tra_Value ==0 && Center_Tra_Value ==0 && Right_Tra_Value ==1)
  { // se a esquerda saiu da linha, espelha o caso anterior
    Left_single(); // gira à esquerda sobre a roda direita
  }
  else if (Left_Tra_Value ==0 && Center_Tra_Value ==1 && Right_Tra_Value ==1)
  { // se centro e esquerda veem preto, a curva é à esquerda
    Left_small(); // curva suave à esquerda sem parar a direita
  }
  else if (Left_Tra_Value ==1 && Center_Tra_Value ==1 && Right_Tra_Value ==1)
  { // se tudo vê branco, o carro saiu da pista
    Stop(); // para em vez de caçar a linha às cegas
  }
  else if (Left_Tra_Value ==0 && Center_Tra_Value ==0 && Right_Tra_Value ==0)
  { // se tudo vê preto, é cruzamento ou largada
    Forward(); // avança pleno atravessando a marca
  }
}

Gravação e operação: calibre via a via (preto acende o LED da via, branco apaga; no branco total os três apagam, no preto total os três acendem), desenhe pista com curvas suaves, carregue 3_Code_and_Tutorial/6_Line_Tracing_Car/6_Line_Tracing_Car.ino, solte no centro da linha e ligue. Resultado esperado: persegue a fita com correções curtas, sem oscilar para fora nas curvas. Se oscila, reduza carSpeed para 150 e recalibre a altura do módulo.

💡 Dica: a dica do fabricante vale ouro: além de seguir linha, a mesma decisão confina o carro numa área cercada de fita, bastando inverter o teste para fugir do preto em vez de persegui-lo.

Lição 7 — Carro Seguidor de Objeto

A sétima lição transforma a régua do ultrassom em comportamento: o carro mantém distância do objeto à frente, aproximando quando longe e recuando quando perto demais. O sketch fixa a torre a 90 graus e aplica faixas: abaixo de 15 cm recua devagar, 15 a 20 cm para, 20 a 25 cm avança devagar, 25 a 50 cm avança pleno, fora disso para. A teoria é o controle por zonas com histerese implícita nas faixas de parada, que evita oscilação de liga-desliga no limiar:

u(d)={reˊ lenta,d<15parado,15≤d≤20avante lento,20<d≤25avante,25<d≤50parado,d>50u(d) = \begin{cases} \text{ré lenta}, & d < 15 \\ \text{parado}, & 15 \le d \le 20 \\ \text{avante lento}, & 20 < d \le 25 \\ \text{avante}, & 25 < d \le 50 \\ \text{parado}, & d > 50 \end{cases}

onde dd é a distância em cm e uu a marcha. A zona morta de 15 a 20 cm absorve o ruído do eco sem tremer os motores, e o teto de 50 cm evita perseguir a parede do fundo da sala. O objeto ideal é plano e claro, que devolve eco forte no cone de 15 graus.

Componente necessárioFunção no ensaio
HC-SR04 na torre a 90 grausRégua do comportamento
Tração 4WD calibradaExecução das marchas por faixa
Anteparo móvel (livro ou caixa)Alvo que se aproxima e afasta
Lição 7 — Upload do programa seguidor de objetoLição 7 — Carro mantendo distância do anteparo
Conexão validadaPinoSentido
TRIG / ECHOGPIO 19 / 18Saída e entrada do sonar
ServoGPIO 4 travado a 90 grausTorre fixa à frente
Tração25/27/26 e 12/14/13carSpeed 150 com lentas a 60 e 80 por cento

📦 Bibliotecas desta lição: ESP32Servo por Kevin Harrington (versão 1.1.2, mesma da Lição 5, nada a baixar).

  • Conferência: exemplo Sweep compila; se a torre não trava a 90 graus, volte à Lição 5 antes de culpar o sonar.
//www.kuongshun.com — Lição 7 seguidor de objeto (pasta 7_Object_Following_Car; estrutura; marchas seguem a Lição 1)

#include <ESP32Servo.h>  //servo library — inclui o servo (quadro da Lição 5)
Servo myservo;      // create servo object to control servo — cria o objeto da torre

#define Echo 18  // cria constante do eco (GPIO 18)
#define Trig 19  // cria constante do disparo (GPIO 19)
#define servoPIN 4 // cria constante do sinal do servo (GPIO 4)
// ... ENA/ENA_PIN/IN1/IN2/IN3/IN4/ENB/ENB_PIN e carSpeed 150 como na Lição 1
int rightDistance = 0, leftDistance = 0, middleDistance = 0; // cria reservas de varredura
int UT_distance = 0; // cria variável da distância atual em cm

// ... Forward, Back, Left, Right, Forward_slow (0.6), Back_slow (0.8), Stop — Lição 1

//Ultrasonic distance measurement Sub function — sub-rotina de medição
int Distance_detection() { // define a medição em cm
  digitalWrite(Trig, LOW);   // garante TRIG baixo
  delayMicroseconds(2);      // repouso de 2 us
  digitalWrite(Trig, HIGH);  // dispara
  delayMicroseconds(10);     // pulso de 10 us
  digitalWrite(Trig, LOW);   // libera o trem de 40 kHz
  float Fdistance = pulseIn(Echo, HIGH); // mede o eco em us
  Fdistance= Fdistance / 58;      // converte para cm
  delay(10); // pausa contra reverberação
  return (int)Fdistance; // retorna inteiro
}

void setup() { // define a configuração inicial
  myservo.attach(servoPIN,500,2500); // vincula o servo com 500 a 2500 us
  myservo.write(90); // trava a torre à frente

  Serial.begin(9600);     // serial em 9600 bps
  pinMode(Echo, INPUT);    // eco como entrada
  pinMode(Trig, OUTPUT);  // disparo como saída
  pinMode(IN1, OUTPUT); // sentidos como saída (ver Lição 1)
  pinMode(IN2, OUTPUT); // sentidos como saída
  pinMode(IN3, OUTPUT); // sentidos como saída
  pinMode(IN4, OUTPUT); // sentidos como saída
  pinMode(ENA_PIN, OUTPUT); // PWM A como saída
  pinMode(ENB_PIN, OUTPUT); // PWM B como saída
  ledcSetup(ENA,5000,8); // LEDC 3 a 5 kHz 8 bits
  ledcSetup(ENB,5000,8); // LEDC 4 a 5 kHz 8 bits
  ledcAttachPin(ENA_PIN,ENA); // GPIO 25 no canal 3
  ledcAttachPin(ENB_PIN,ENB); // GPIO 13 no canal 4
  Stop(); // parte parado
}

void loop() { // define o laço do seguimento
  myservo.write(90); // mantém a torre à frente
  UT_distance = Distance_detection(); // mede a distância atual
  Serial.println(UT_distance); // imprime para acompanhar as faixas
  if (UT_distance < 15) { // se colado no objeto
    Back_slow(); // recua devagar abrindo espaço
  } else if (15 <= UT_distance && UT_distance <= 20) { // se na zona ideal
    Stop(); // para e segura a distância
  } else if (20 <= UT_distance && UT_distance <= 25) { // se abrindo perto
    Forward_slow(); // avança devagar fechando o vão
  } else if (25 <= UT_distance && UT_distance <= 50) { // se abrindo longe
    Forward(); // avança pleno buscando o alvo
  } else { // se sem alvo no cone
    Stop(); // para em vez de disparar pela sala
  }
}

Gravação e operação: carregue 3_Code_and_Tutorial/7_Object_Following_Car/7_Object_Following_Car.ino, solte no chão livre e apresente o anteparo. Resultado esperado: longe avança, perto recua, na faixa para e segura cerca de 15 a 20 cm. Se treme parado, o eco está ruidoso: afaste de cortinas e teste contra parede lisa.

Lição 8 — Carro de Desvio de Obstáculos

A oitava lição funde os dois sentidos de curta distância: ultrassom com varredura para longe e desvios IR para perto. O sketch avança enquanto a frente está livre, para e mede os lados ao encontrar obstáculo, vira para o lado mais espaçoso e usa os IR como vigilância imediata que esterça antes do sonar confirmar. A teoria é a fusão complementar: o sonar enxerga longe em cone estreito e os IR enxergam perto em cone largo, de modo que um cobre o ponto cego do outro. Os limiares do fabricante são:

Dmix=8 cm,Dmid=30 cm,Dmax=900 cmD_{mix} = 8\,\text{cm},\quad D_{mid} = 30\,\text{cm},\quad D_{max} = 900\,\text{cm}

onde abaixo de DmidD_{mid} há obstáculo à frente, abaixo de DmixD_{mix} há emergência que pede ré de 300 ms, e acima de DmaxD_{max} a leitura é inválida. A varredura aponta a torre a 160 graus para medir a esquerda, volta a 90 e vai a 20 para a direita, sempre com pausas para o eco assentar. O tempo de giro é inverso à velocidade pela expressão 130000/carSpeed em ms, de modo que reduzir carSpeed alonga o giro na mesma proporção. Com carSpeed 200, o giro dura cerca de 650 ms.

Componente necessárioFunção no ensaio
HC-SR04 na torre móvelSonar com varredura lateral
2 desvios IR calibradosVigilância de emergência próxima
Servo centrado (Lição 5)Apontamento fiel das laterais
Arena com caixas espaçadasCenário de decisão direita-esquerda
Lição 8 — Upload do programa de desvio de obstáculosLição 8 — Carro medindo laterais diante do obstáculo
Conexão validadaPinoSentido
TRIG / ECHOGPIO 19 / 18Sonar da varredura
ServoGPIO 4 (160/90/20 graus)Apontamento da medição
IR esquerdo / direitoGPIO 33 / 32Vigilância próxima
Tração25/27/26 e 12/14/13carSpeed 200

📦 Bibliotecas desta lição: ESP32Servo por Kevin Harrington (versão 1.1.2, mesma da Lição 5, nada a baixar).

  • Conferência: torre varre simétrica na Lição 5; se a comparação direita-esquerda sai invertida, confira o braço do servo antes do código.
//www.kuongshun.com — Lição 8 desvio com ultrassom e IR (pasta 8_Ultrasonic_Infrared_Obstacle_Avoidance_Car; estrutura e decisão)

#include <ESP32Servo.h> // inclui o servo da torre (quadro da Lição 5)
Servo myservo; // cria o objeto da torre
#define Echo 18 // eco do sonar (GPIO 18)
#define Trig 19 // disparo do sonar (GPIO 19)
#define servoPIN 4 // sinal do servo (GPIO 4)
#define IRPIN_L 33 // desvio esquerdo (GPIO 33)
#define IRPIN_R 32 // desvio direito (GPIO 32)
// ... ENA/ENA_PIN/IN1/IN2/IN3/IN4/ENB/ENB_PIN e carSpeed 200 como na Lição 1

volatile int D_mix; // cria o limiar de emergência (8 cm)
volatile int D_mid; // cria o limiar de obstáculo (30 cm)
volatile int D_max; // cria o teto de validade (900 cm)
volatile int Front_Distance; // cria a distância à frente
volatile int Left_Distance;  // cria a distância à esquerda
volatile int Right_Distance; // cria a distância à direita
volatile int Right_IR_Value; // cria o estado do IR direito
volatile int Left_IR_Value;  // cria o estado do IR esquerdo

// ... Forward, Back, Left, Right, Stop — mesma ponte da Lição 1 com carSpeed 200

void Ultrasonic_obstacle_avoidance() { // define o desvio pelo sonar
  delay(200); // assenta a mecânica antes de medir
  Front_Distance = checkdistance(); // mede a frente
  delay(100); // pausa contra reverberação
  if (Front_Distance <= D_mid) { // se há obstáculo à frente
    Stop();//stop — para antes de decidir
    if (Front_Distance <= D_mix || Left_IR_Value == 0 && Right_IR_Value == 0) {
      Back();//back — emergência: ré curta de segurança
      delay(300); // ré por 300 ms
      Stop();//stop — para antes de varrer
    }
    Detect_Left_and_Right__distance(); // varre e mede os dois lados
    if ((D_mix < Left_Distance && Left_Distance < D_max ) && (D_mix < Right_Distance && Right_Distance < D_max)) {
      if (Right_Distance > Left_Distance) { // se a direita tem mais espaço
        Right();// vira à direita
        delay(130000/carSpeed); // giro proporcional a 1/carSpeed
      } else { // se a esquerda tem mais espaço
        Left();// vira à esquerda
        delay(130000/carSpeed); // mesmo giro espelhado
      }
    } else if (D_mix < Left_Distance && Left_Distance < D_max || D_mix < Right_Distance && Right_Distance < D_max) {
      if (D_mix < Left_Distance && Left_Distance < D_max) { // se só a esquerda é válida
        Left();// vira à esquerda
        delay(130000/carSpeed); // giro proporcional
      } else if (D_mix < Right_Distance && Right_Distance < D_max) { // se só a direita é válida
        Right();// vira à direita
        delay(130000/carSpeed); // giro proporcional
      }
    } else if (Right_Distance < D_mix && Left_Distance < D_mix) { // se beco sem saída
      Back();// ré de escape
      delay(510); // ré por 510 ms
      Stop();// para antes do próximo ciclo
    }
  }
  Forward(); // frente livre: avança
}

void Detect_Left_and_Right__distance() { // define a varredura lateral
  Serial.print("Front_Distance"); // rotula a frente
  Serial.println(Front_Distance); // imprime a frente
  myservo.write(160); // aponta a torre à esquerda
  delay(700); // aguarda o servo assentar
  Left_Distance = checkdistance(); // mede a esquerda
  delay(200); // pausa contra reverberação
  Serial.print("Left_Distance:"); // rotula a esquerda
  Serial.println(Left_Distance); // imprime a esquerda
  delay(100); // pausa curta
  myservo.write(90); // recentra a torre
  delay(400); // aguarda recentrar
  myservo.write(20); // aponta a torre à direita
  delay(500); // aguarda o servo assentar
  Right_Distance = checkdistance(); // mede a direita
  delay(300); // pausa contra reverberação
  Serial.print("Right_Distance"); // rotula a direita
  Serial.println(Right_Distance); // imprime a direita
  delay(100); // pausa curta
  myservo.write(90); // recentra a torre
  delay(500); // aguarda recentrar
  delay(100); // pausa final
}

void Obstacle_Avoidance_Main() { // define o laço de fusão dos sensores
  Left_IR_Value = digitalRead(IRPIN_L); // lê o IR esquerdo
  Right_IR_Value = digitalRead(IRPIN_R); // lê o IR direito
  Infrared_Obstacle_Avoidance(); // executa a vigilância próxima primeiro
  Ultrasonic_obstacle_avoidance(); // executa o sonar com varredura depois
}

void Infrared_Obstacle_Avoidance() { // define o desvio imediato pelos IR
  if (Left_IR_Value == 0 && Right_IR_Value == 1) { // obstáculo só à esquerda
    Right();// esterça à direita fugindo do contato
  } else if (Left_IR_Value == 1 && Right_IR_Value == 0) { // obstáculo só à direita
    Left();// esterça à esquerda fugindo do contato
  } else { // livre ou parede total (o sonar decide)
    Forward();// mantém avante
  }
}

void setup(){ // define a configuração inicial
  myservo.attach(servoPIN,500,2500); // vincula o servo com 500 a 2500 us
  Serial.begin(9600);     // serial em 9600 bps
  pinMode(Echo, INPUT); // eco como entrada
  pinMode(IRPIN_L, INPUT); // IR esquerdo como entrada
  pinMode(IRPIN_R, INPUT); // IR direito como entrada
  pinMode(Trig, OUTPUT); // disparo como saída
  pinMode(IN1, OUTPUT); // sentidos como saída (ver Lição 1)
  pinMode(IN2, OUTPUT); // sentidos como saída
  pinMode(IN3, OUTPUT); // sentidos como saída
  pinMode(IN4, OUTPUT); // sentidos como saída
  pinMode(ENA_PIN, OUTPUT); // PWM A como saída
  pinMode(ENB_PIN, OUTPUT); // PWM B como saída
  ledcSetup(ENA,5000,8); // LEDC 3 a 5 kHz 8 bits
  ledcSetup(ENB,5000,8); // LEDC 4 a 5 kHz 8 bits
  ledcAttachPin(ENA_PIN,ENA); // GPIO 25 no canal 3
  ledcAttachPin(ENB_PIN,ENB); // GPIO 13 no canal 4
  myservo.write(90); // torre à frente
  Stop(); // parte parado
  D_mix = 8; // emergência abaixo de 8 cm
  D_mid = 30; // obstáculo abaixo de 30 cm
  D_max = 900; // leitura acima de 900 cm é inválida
  Front_Distance = 0; // zera a frente
  Left_Distance = 0; // zera a esquerda
  Right_Distance = 0; // zera a direita
  Right_IR_Value = 1; // IR direito começa livre
  Left_IR_Value = 1; // IR esquerdo começa livre
}

void loop(){ // define o laço do desvio
  Obstacle_Avoidance_Main(); // executa IR imediato mais sonar varrido
}

Gravação e operação: carregue 3_Code_and_Tutorial/8_Ultrasonic_Infrared_Obstacle_Avoidance_Car/8_Ultrasonic_Infrared_Obstacle_Avoidance_Car.ino, solte na arena com caixas espaçadas. Resultado esperado: avança, para diante da caixa, varre a torre para os lados, vira para o lado mais livre e segue; encostando a mão num IR, esterça na hora. Se gira sempre para o mesmo lado, confira a simetria do servo na Lição 5 e a calibração dos IR na Lição 2.

Lição 9 — Carro Buscador de Luz

A nona lição fecha a malha dos fotossensores em navegação: o carro anda para a luz e esterça para o lado mais claro com velocidade proporcional à diferença. O sketch normaliza cada ADC dividindo por 35, para com luz forte nos dois lados e calcula o ângulo de busca pela razão entre lados. A teoria é o esterçamento diferencial proporcional: a diferença de iluminância vira diferença de duty entre pontes, o que curva a trajetória para a fonte sem giros bruscos. O ângulo interno é:

Lightseeking_Degree=min⁡(L,R)max⁡(L,R)⋅90∘ espelhado quando R≥LLightseeking\_Degree = \frac{\min(L,R)}{\max(L,R)} \cdot 90^{\circ}\ \text{espelhado quando } R \ge L f=graus fora do centro90,PWMinterna=carSpeed⋅0,5⋅ff = \frac{graus\ \text{fora do centro}}{90},\quad PWM_{interna} = carSpeed \cdot 0{,}5 \cdot f

onde ff é a fração de fechamento da curva e a roda interna recebe metade do duty vezes ff. Com Lightseeking_DegreeLightseeking\_Degree igual a 90 graus o carro avança pleno; abaixo de 90 fecha para um lado, acima fecha para o outro; com ambos acima de 40 unidades o carro para, pois já chegou na fonte. O carSpeed aqui é 250, o mais alto do curso, para a curva responder mesmo com a penalidade de 50 por cento na roda interna.

Componente necessárioFunção no ensaio
2 fotossensores validados (Lição 3)Olhos da malha de busca
Tração com carSpeed 250Autoridade de curva com roda penalizada
Ambiente de meia-luz com fonte móvelCenário de gradiente controlável
LanternaSol artificial da prova
Lição 9 — Upload do programa buscador de luzLição 9 — Carro partindo em direção à lanterna
Conexão validadaPinoSentido
Fotossensor esquerdoGPIO 35 (ADC/35)Entrada da razão de luz
Fotossensor direitoGPIO 34 (ADC/35)Entrada da razão de luz
Tração25/27/26 e 12/14/13carSpeed 250 com penalidade proporcional

📦 Bibliotecas desta lição: nenhuma — o código usa analogRead(), ledcWrite() e digitalWrite() nativos, sem biblioteca externa.

//www.kuongshun.com — Lição 9 buscador de luz (pasta 9_Light_Seeking_Robot_Car; malha de busca; marchas seguem a Lição 1)

volatile float Left_photosensitive;  // cria a luz esquerda normalizada
volatile float Right_photosensitive; // cria a luz direita normalizada
volatile int Lightseeking_Degree; // cria o ângulo interno de busca (0 a 180)
volatile float f; // cria a fração de fechamento da curva (0 a 1)
volatile int speed_value; // cria reserva de velocidade
#define L_light_PIN 35 // fotossensor esquerdo (GPIO 35)
#define R_light_PIN 34 // fotossensor direito (GPIO 34)
// ... ENA/ENA_PIN/IN1/IN2/IN3/IN4/ENB/ENB_PIN e carSpeed 250 como na Lição 1

// ... Forward, Back, Left, Right, Stop — mesma ponte da Lição 1 com carSpeed 250

void Light_Seeking() { // define a malha de busca de luz
  Left_photosensitive = analogRead(L_light_PIN) / 35; // normaliza a esquerda
  Right_photosensitive = analogRead(R_light_PIN) / 35; // normaliza a direita
  Serial.print("Left_photosensitive:"); // rotula a esquerda
  Serial.println(Left_photosensitive); // imprime a esquerda
  Serial.print("Right_photosensitive:"); // rotula a direita
  Serial.println(Right_photosensitive); // imprime a direita
  Serial.println(""); // separa as amostras
  if (Left_photosensitive > 40 && Right_photosensitive > 40) { // se luz forte nos dois
    Stop(); // chegou na fonte: para
  } else { // se há gradiente para explorar
    if (Left_photosensitive > Right_photosensitive) { // se a esquerda está mais escura
      Lightseeking_Degree = ((float)(Right_photosensitive / Left_photosensitive)) * 90; // ângulo abaixo de 90
    } else if (Left_photosensitive <= Right_photosensitive) { // se a direita está mais escura ou igual
      Lightseeking_Degree = 180 - ((float)(Left_photosensitive / Right_photosensitive)) * 90; // ângulo acima de 90
    }
    if (Lightseeking_Degree < 90) { // se a fonte pende para um lado
      f = ((float)(Lightseeking_Degree)) / 90; // calcula a fração de curva
      ledcWrite(ENA, carSpeed* 0.5*f); // penaliza a ponte A proporcionalmente
      ledcWrite(ENB, carSpeed); // mantém a ponte B plena
      digitalWrite(IN1, LOW); // avante nas duas pontes
      digitalWrite(IN2, HIGH); // avante nas duas pontes
      digitalWrite(IN3, LOW); // avante nas duas pontes
      digitalWrite(IN4, HIGH); // avante nas duas pontes
    }
    if (Lightseeking_Degree > 90) { // se a fonte pende para o outro lado
      f = ((float)(180 - Lightseeking_Degree)) / 90; // fração espelhada
      ledcWrite(ENA, carSpeed); // mantém a ponte A plena
      ledcWrite(ENB, carSpeed* 0.5*f); // penaliza a ponte B proporcionalmente
      digitalWrite(IN1, LOW); // avante nas duas pontes
      digitalWrite(IN2, HIGH); // avante nas duas pontes
      digitalWrite(IN3, LOW); // avante nas duas pontes
      digitalWrite(IN4, HIGH); // avante nas duas pontes
    }
    if (Lightseeking_Degree == 90) { // se o gradiente é nulo
      f = ((float)(Lightseeking_Degree)) / 90; // fração unitária
      Forward(); // avança pleno reto à fonte
    }
  }
}

void setup(){ // define a configuração inicial
  Left_photosensitive = 0; // zera a esquerda
  Right_photosensitive = 0; // zera a direita
  Lightseeking_Degree = 0; // zera o ângulo
  f = 0; // zera a fração
  Serial.begin(9600); // serial em 9600 bps
  pinMode(L_light_PIN, INPUT); // esquerda como entrada
  pinMode(R_light_PIN, INPUT); // direita como entrada
  pinMode(IN1, OUTPUT); // sentidos como saída (ver Lição 1)
  pinMode(IN2, OUTPUT); // sentidos como saída
  pinMode(IN3, OUTPUT); // sentidos como saída
  pinMode(IN4, OUTPUT); // sentidos como saída
  pinMode(ENA_PIN, OUTPUT); // PWM A como saída
  pinMode(ENB_PIN, OUTPUT); // PWM B como saída
  ledcSetup(ENA,5000,8); // LEDC 3 a 5 kHz 8 bits
  ledcSetup(ENB,5000,8); // LEDC 4 a 5 kHz 8 bits
  ledcAttachPin(ENA_PIN,ENA); // GPIO 25 no canal 3
  ledcAttachPin(ENB_PIN,ENB); // GPIO 13 no canal 4
}

void loop(){ // define o laço da busca
  Light_Seeking(); // executa a malha contra a luz
}

Gravação e operação: carregue 3_Code_and_Tutorial/9_Light_Seeking_Robot_Car/9_Light_Seeking_Robot_Car.ino, solte no chão em meia-luz e aponte a lanterna aos fotossensores. Resultado esperado: parte para frente ao sentir luz e vira para o lado mais forte, parando ao chegar perto da fonte. Se anda em círculos no escuro uniforme, é o comportamento correto sem gradiente: apresente a lanterna para ancorar a decisão.

Lição 10 — Carro com Controle Remoto Infravermelho

A décima lição devolve o comando à mão: o controle remoto dirige o carro e soltar o botão para. O sketch decodifica o protocolo NEC a 38 kHz, traduz setas em marchas e impõe parada por timeout de 200 ms sem comando novo. A teoria é o quadro NEC com repetição: cada tecla envia endereço mais comando mais inverso, e segurar a tecla gera quadros de repetição sinalizados por flag. O firmware guarda o último código válido e o reutiliza na repetição, o que mantém a marcha com o dedo pressionado sem retraduzir. O homem-morto é temporal:

tagora−tuˊltimo>200 ms⇒Stop()t_{agora} - t_{último} > 200\,\text{ms} \Rightarrow Stop()

onde tuˊltimot_{último} é atualizado a cada quadro decodificado. O mapa do controle do kit, em hexadecimal de 32 bits, é UP 0xB946FF00, DOWN 0xEA15FF00, LEFT 0xBB44FF00, RIGHT 0xBC43FF00, OK 0xBF40FF00, dígitos 0 a 9, * e #. As teclas fora das setas e OK apenas imprimem o nome no Serial, sem mover.

Componente necessárioFunção no ensaio
Receptor IR com olho desobstruídoEntrada do comando no GPIO 15
Controle remoto com CR2025/CR2032Transmissor NEC das marchas
Tração com carSpeed 200Execução das setas
Área livre de 2 m à frentePista do teste com parada ao soltar
Lição 10 — Upload do programa de controle remotoLição 10 — Controle remoto comandando marchas
Conexão validadaPinoSentido
OUT do receptor IRGPIO 15Entrada NEC 38 kHz
Tração25/27/26 e 12/14/13carSpeed 200
Timeout do homem-morto200 ms em softwareParada sem comando

📦 Bibliotecas desta lição: IRremote por shirriff, z3t0, ArminJo (versão 4.1.2).

  • Via Gerenciador (recomendado): Sketch > Incluir Biblioteca > Gerenciar Bibliotecas… → buscar IRremote → selecionar 4.1.2 → Instalar → reiniciar a IDE.
  • Via ZIP: pasta libraries do kit copiada para o Sketchbook → reiniciar a IDE; espelho oficial em Arduino-IRremote/Arduino-IRremote.
  • Conferência: Arquivo > Exemplos > IRremote > SimpleReceiver deve abrir e compilar para ESP32 Dev Module. Nunca instale a série 2.x aqui: a API decode(&results) não existe neste sketch.
//www.kuongshun.com — Lição 10 controle remoto IR (pasta 10_Infrared_Remote_Control_Car), comentários didáticos

#include <IRremote.h> // inclui o decodificador NEC (instalado pelo quadro acima)

int receiver = 15; // cria variável do pino do receptor (GPIO 15)
/*define channel enable output pins — define os pinos da ponte (ver Lição 1)*/
#define ENA 3      // canal LEDC 3 da velocidade A
#define ENA_PIN 25 // GPIO 25 do PWM A
#define IN1 27     // GPIO 27 do sentido A
#define IN2 26     // GPIO 26 do sentido A
#define IN3 12     // GPIO 12 do sentido B
#define IN4 14     // GPIO 14 do sentido B
#define ENB 4      // canal LEDC 4 da velocidade B
#define ENB_PIN 13 // GPIO 13 do PWM B
#define carSpeed 200	// initial speed of car >=0 to <=255 — velocidade das setas

IRrecv irrecv(receiver); // cria o objeto receptor no GPIO 15
uint32_t last_decodedRawData = 0; // guarda o último código para repetição
unsigned long lastCommandTime; // Record the time of the last received command — hora do último quadro
const unsigned long commandTimeout = 200; // Set the timeout period (milliseconds) — 200 ms de homem-morto

void translateIR() // takes action based on IR code received — traduz o quadro em marcha
{
  if (irrecv.decodedIRData.flags)// Check if it is a repeat IR code — se é repetição de tecla segurada
  {
    irrecv.decodedIRData.decodedRawData = last_decodedRawData;    //set the current decodedRawData to the last decodedRawData — reaproveita o último código
    Serial.println("REPEAT!"); // avisa a repetição no monitor
  } else//output the IR code on the serial monitor — se quadro novo, imprime
  {
    Serial.print("IR code:0x"); // rotula o código
    Serial.println(irrecv.decodedIRData.decodedRawData, HEX); // imprime em hexadecimal
  }
  switch (irrecv.decodedIRData.decodedRawData)//map the IR code to the remote key — casa código com tecla
  {
    case 0xB946FF00: Serial.println("UP"); Forward();break;    // seta cima: avante
    case 0xEA15FF00: Serial.println("DOWN"); Back();break;     // seta baixo: ré
    case 0xBB44FF00: Serial.println("LEFT"); Left();break;     // seta esquerda: gira
    case 0xBC43FF00: Serial.println("RIGHT"); Right();   break;// seta direita: gira
    case 0xBF40FF00: Serial.println("OK"); Stop();   break;    // OK: para
    case 0xAD52FF00: Serial.println("0");    break; // dígito 0: só imprime
    case 0xE916FF00: Serial.println("1");    break; // dígito 1: só imprime
    case 0xE619FF00: Serial.println("2");    break; // dígito 2: só imprime
    case 0xF20DFF00: Serial.println("3");    break; // dígito 3: só imprime
    case 0xF30CFF00: Serial.println("4");    break; // dígito 4: só imprime
    case 0xE718FF00: Serial.println("5");    break; // dígito 5: só imprime
    case 0xA15EFF00: Serial.println("6");    break; // dígito 6: só imprime
    case 0xF708FF00: Serial.println("7");    break; // dígito 7: só imprime
    case 0xE31CFF00: Serial.println("8");    break; // dígito 8: só imprime
    case 0xA55AFF00: Serial.println("9");    break; // dígito 9: só imprime
    case 0xBD42FF00: Serial.println("*");    break; // asterisco: só imprime
    case 0xB54AFF00: Serial.println("#");    break; // cerquilha: só imprime
    default:
    Serial.println(" other button   "); // tecla fora do mapa: avisa
  }// End Case — fim da tradução
  last_decodedRawData = irrecv.decodedIRData.decodedRawData;//store the last decodedRawData — guarda para a repetição
  //delay(500); // Do not get immediate repeat — atraso desativado para resposta contínua
} //END translateIR — fim da tradução

// ... Forward, Back, Left, Right, Stop — mesma ponte da Lição 1 com carSpeed 200

void setup() { // define a configuração inicial
  Serial.begin(9600); // serial em 9600 bps
  pinMode(IN1,OUTPUT); // sentidos como saída (ver Lição 1)
  pinMode(IN2,OUTPUT); // sentidos como saída
  pinMode(IN3,OUTPUT); // sentidos como saída
  pinMode(IN4,OUTPUT); // sentidos como saída
  pinMode(ENA_PIN,OUTPUT); // PWM A como saída
  pinMode(ENB_PIN,OUTPUT); // PWM B como saída
  pinMode(13, OUTPUT); // configura o GPIO 13 também como saída digital
  ledcSetup(ENA,5000,8); // LEDC 3 a 5 kHz 8 bits
  ledcSetup(ENB,5000,8); // LEDC 4 a 5 kHz 8 bits
  ledcAttachPin(ENA_PIN,ENA); // GPIO 25 no canal 3
  ledcAttachPin(ENB_PIN,ENB); // GPIO 13 no canal 4
  Stop(); // parte parado
  irrecv.enableIRIn();// Start the receiver — liga o receptor IR
}

void loop() { // define o laço do comando
  if (irrecv.decode()) // have we received an IR signal? — se há quadro novo
  {
    lastCommandTime = millis(); // Update the last command time — carimba a hora
    translateIR(); // traduz em marcha
    irrecv.resume(); // receive the next value — libera o próximo quadro
  }
  if (millis() - lastCommandTime > commandTimeout) { // se passa 200 ms sem quadro
    Stop(); // If no new IR signal within 100 milliseconds, stop the robot — para (homem-morto)
  }
}

Gravação e operação: instale a CR2025 ou CR2032 no controle (versão sem bateria exige par próprio), carregue 3_Code_and_Tutorial/10_Infrared_Remote_Control_Car/10_Infrared_Remote_Control_Car.ino, solte no chão e mire no receptor. Resultado esperado: segurar seta move, soltar para em 200 ms, OK para na hora, Monitor mostra código e nome da tecla. Se nada responde, confira pilha, mira e GPIO 15 antes da biblioteca.

⚠️ Atenção: luz solar direta e lâmpada fluorescente saturam o receptor de 38 kHz. Teste de IR sempre à sombra, ou o alcance cai de metros para centímetros.

Lição 11 — Carro Web por WiFi

A décima primeira lição corta o fio do USB e põe o comando no navegador do celular. O ESP32 sobe em modo AP com SSID KUONGSHUN-AD178 e senha 12345678, anuncia o IP 192.168.4.1 e serve uma página com botões de marcha que disparam requisições com parâmetros de comando. A teoria é o WiFi em modo AP com servidor HTTP embarcado: o ESP32 é o roteador da sua própria célula sem internet, o celular associa e mantém a conexão mesmo com aviso de sem internet, e cada toque gera um GET /control?var=car&val=N que o handler converte em marcha. O canal 9 fixo reduz disputa com roteadores domésticos nos canais 1 e 6, e a potência de 19,5 dBm maximiza alcance indoor ao custo de consumo que exige bateria cheia. A latência de toque a roda é a soma do HTTP de ida com a atuação da ponte, na casa de centenas de milissegundos, e soltar o botão envia o comando de parada, o mesmo homem-morto do infravermelho agora sobre rádio. A rede do modo é:

rede: IP=192.168.4.1,canal=9,SSID=KUONGSHUN-AD178\text{rede: } IP = 192.168.4.1,\quad \text{canal} = 9,\quad SSID = KUONGSHUN\text{-}AD178
Componente necessárioFunção no ensaio
ESP32 com WiFi íntegroRoteador e servidor da célula
Tração com carSpeed 255Marcha plena do modo Web
Celular ou tablet com WiFiNavegador do portal 192.168.4.1
2 baterias 18650 cheiasCorrente do rádio mais motores
Lição 11 — Upload do programa do carro WebLição 11 — Conexão no hotspot KUONGSHUN-AD178
Lição 11 — Página de comando em 192.168.4.1 com botões de marcha
Rede e portalValor fixo do firmware
SSID do APKUONGSHUN-AD178
Senha12345678
Portal192.168.4.1
Comando avante / ré / parar / esquerda / direita/control?var=car&val=1/2/3/4/5

📦 Bibliotecas desta lição: ESP32Servo por Kevin Harrington (versão 1.1.2, incluída pelo app_httpd.cpp), WiFi e esp_http_server embutidas no núcleo 2.0.12, nada a instalar além do quadro da Lição 5.

  • Conferência: o sketch compila para ESP32 Dev Module sem WiFi.h: No such file or directory, o que prova o núcleo 2.0.12 ativo.
//www.kuongshun.com — Lição 11 carro Web (pasta 11_WIFI_Web_Car; .ino comentado)

#include <WiFi.h> // inclui o rádio WiFi do núcleo (sem instalar nada)

const char* ssid = "KUONGSHUN-AD178"; // cria o nome da rede do robô
const char* password = "12345678";    // cria a senha da rede do robô

void startServer(); // declara o servidor HTTP definido no app_httpd.cpp
void Car_init();    // declara a inicialização da ponte definida no app_httpd.cpp

void setup() { // define a configuração da célula WiFi
  Serial.begin(9600); // serial em 9600 bps para o IP
  WiFi.setTxPower(WIFI_POWER_19_5dBm); // fixa a potência máxima de transmissão
  WiFi.mode(WIFI_AP); // coloca o rádio em modo ponto de acesso
  WiFi.softAP(ssid, password, 9); // anuncia SSID e senha no canal 9
  IPAddress myIP = WiFi.softAPIP(); // lê o IP do próprio AP
  Serial.print("AP IP address: "); // rotula o IP
  Serial.println(myIP); //probar 192.168.4.1 — imprime 192.168.4.1
  Car_init(); // inicializa sentidos e PWMs da ponte
  startServer(); // sobe o servidor HTTP do portal
}

void loop() { // define o laço (vazio de propósito)
  // o servidor HTTP atende por interrupção de tarefa: nada aqui
}

O app_httpd.cpp da Lição 11 completa o modo com a ponte a carSpeed 255, o handler e a página. Leitura guiada dos trechos que importam, com comentários didáticos:

// app_httpd.cpp da Lição 11 — trechos de engenharia (nomes originais preservados)

void Car_init(){ // inicializa a ponte H antes do servidor
  pinMode(IN1,OUTPUT); // sentidos como saída (ver Lição 1)
  pinMode(IN2,OUTPUT);
  pinMode(IN3,OUTPUT);
  pinMode(IN4,OUTPUT);
  pinMode(ENA_PIN,OUTPUT); // PWMs como saída
  pinMode(ENB_PIN,OUTPUT);
  ledcSetup(ENA,5000,8); // LEDC 3 a 5 kHz 8 bits
  ledcSetup(ENB,5000,8); // LEDC 4 a 5 kHz 8 bits
  ledcAttachPin(ENA_PIN,ENA); // GPIO 25 no canal 3
  ledcAttachPin(ENB_PIN,ENB); // GPIO 13 no canal 4
}

// o cmd_handler lê var e val da URL e despacha:
// var=car val=1 avante, val=2 ré, val=3 parada, val=4 esquerda, val=5 direita
// cada toque do botão gera fetch('/control?var=car&val=N') e soltar gera val=3

void startServer() // sobe o HTTP com as rotas / e /control
{
    httpd_config_t config = HTTPD_DEFAULT_CONFIG(); // configuração padrão do IDF
    // rota / entrega a INDEX_HTML com os botões Forward/TurnLeft/TurnRight/Backward
    // rota /control entrega o cmd_handler que executa a marcha
}

Gravação e operação: carregue a pasta 3_Code_and_Tutorial/11_WIFI_Web_Car inteira (o .ino mais o app_httpd.cpp juntos), desconecte, ligue com baterias e aguarde o LED do ESP32. No celular, conecte no WiFi KUONGSHUN-AD178 com 12345678 mantendo sem internet, abra o navegador em 192.168.4.1. Resultado esperado: a página mostra os quatro botões, pressionar move e soltar para. Se a rede não aparece, confira bateria e alimentação da ESP32 e do L298N, pois rádio sem corrente não anuncia SSID. Se abre a página mas não anda, recarregue este sketch, pois outro modo com servidor pode estar gravado. Se o celular alterna para dados móveis, desative dados durante o ensaio.

Lição 12 — Carro Web Multifunção

A décima segunda lição integra tudo num portal só: marchas por toque, quatro modos autônomos, ajuste do servo e ajuste da velocidade, mais o controle IR em paralelo. O ESP32 mantém o mesmo AP e IP, mas o handler aceita variáveis além de car: model troca o func_mode, e os controles deslizantes definem velocidade e ângulo. O loop() do .ino atende o IR e executa os quatro modos em sequência, de modo que o modo ativo assume a tração até o portal trocar. Os modos reaproveitam as malhas das lições isoladas: linha da 6, desvio da 8, seguimento da 7 e busca de luz da 9. O mapa de funções da página do fabricante é: segurar setas move, botões de modo entram em busca de luz, desvio, seguimento e linha, um slider ajusta o ângulo do servo da frente e outro a velocidade. A variável central é:

func_mode∈{0 livre, 1 luz, 2 desvio, 3 seguimento, 4 linha}func\_mode \in \{0\ \text{livre},\ 1\ \text{luz},\ 2\ \text{desvio},\ 3\ \text{seguimento},\ 4\ \text{linha}\}

onde cada valor seleciona a malha que assume a ponte no loop(). Os botões de marcha continuam homem-morto por soltura, e os modos autônomos rodam até a troca, sem timeout de rádio: supervisione com o dedo no botão de parada.

Componente necessárioFunção no ensaio
Robô completo calibradoExecução dos quatro modos pelo portal
Todos os sensores validadosEntradas das malhas integradas
Celular no AP do robôPortal 192.168.4.1 multifunção
Controle IR com bateriaComando paralelo ao portal
Lição 12 — Upload do programa multifunçãoLição 12 — Conexão no hotspot para o modo multifunção
Lição 12 — Página multifunção com setas, modos, servo e velocidade
Recurso do portalComando e efeito
Setas (segurar)Marchas com parada ao soltar, como na Lição 11
Modo busca de luzAssume a malha da Lição 9
Modo desvioAssume a malha da Lição 8 com varredura
Modo seguimentoAssume a malha da Lição 7 por faixas
Modo linhaAssume a malha da Lição 6 de 3 vias
Controle deslizante do servoÂngulo da torre no modo livre
Controle deslizante de velocidadecarSpeed em tempo real

📦 Bibliotecas desta lição: IRremote 4.1.2 e ESP32Servo 1.1.2 (mesmas das Lições 10 e 5, nada a baixar), WiFi e esp_http_server embutidas no núcleo 2.0.12.

  • Conferência: compila para ESP32 Dev Module sem erro de IRremote nem de ESP32Servo; se o IR não responde só aqui mas responde na Lição 10, confira o GPIO 15 e recarregue a pasta inteira.
//www.kuongshun.com — Lição 12 Web multifunção (pasta 12_WIFI_Web_Multi_Function_Car; .ino comentado)

#include <WiFi.h> // inclui o rádio do núcleo
#include <IRremote.h> // inclui o NEC (quadro da Lição 10)

const char* ssid = "KUONGSHUN-AD178"; // nome da rede do robô
const char* password = "12345678"; // senha da rede do robô

int receiver = 15; // pino do receptor IR (GPIO 15)
IRrecv irrecv(receiver); // objeto receptor no GPIO 15
uint32_t last_decodedRawData = 0; // último código para repetição

void startServer(); // servidor HTTP do app_httpd.cpp
void Car_init(); // ponte do app_httpd.cpp
void Line_Tracing_mode(); // modo linha do app_httpd.cpp
void Obstacles_Avoidance_mode(); // modo desvio do app_httpd.cpp
void Object_Following_mode(); // modo seguimento do app_httpd.cpp
void Light_Seeking_mode(); // modo luz do app_httpd.cpp
void Forward(); // marchas do app_httpd.cpp
void Back();
void Left();
void Right();
void Stop();

void getIRData() // takes action based on IR code received — IR paralelo ao portal
{
  if (irrecv.decode()) // have we received an IR signal? — se há quadro
  {
    if (irrecv.decodedIRData.flags)// Check if it is a repeat IR code — se repetição
    {
      irrecv.decodedIRData.decodedRawData = last_decodedRawData; // reaproveita o último
      Serial.println("REPEAT!"); // avisa
    } else//output the IR code on the serial monitor — se novo, imprime
    {
      Serial.print("IR code:0x"); // rotula
      Serial.println(irrecv.decodedIRData.decodedRawData, HEX); // imprime
    }
    switch (irrecv.decodedIRData.decodedRawData)//map the IR code to the remote key
    { // mesmo mapa da Lição 10: setas movem, OK para, resto só imprime
      case 0xB946FF00: Serial.println("UP"); Forward();break;
      case 0xEA15FF00: Serial.println("DOWN"); Back();break;
      case 0xBB44FF00: Serial.println("LEFT"); Left();break;
      case 0xBC43FF00: Serial.println("RIGHT"); Right();   break;
      case 0xBF40FF00: Serial.println("OK"); Stop();   break;
      case 0xAD52FF00: Serial.println("0");    break;
      case 0xE916FF00: Serial.println("1");    break;
      case 0xE619FF00: Serial.println("2");    break;
      case 0xF20DFF00: Serial.println("3");    break;
      case 0xF30CFF00: Serial.println("4");    break;
      case 0xE718FF00: Serial.println("5");    break;
      case 0xA15EFF00: Serial.println("6");    break;
      case 0xF708FF00: Serial.println("7");    break;
      case 0xE31CFF00: Serial.println("8");    break;
      case 0xA55AFF00: Serial.println("9");    break;
      case 0xBD42FF00: Serial.println("*");    break;
      case 0xB54AFF00: Serial.println("#");    break;
      default:
        Serial.println(" other button   ");
    }// End Case
    last_decodedRawData = irrecv.decodedIRData.decodedRawData;//store the last decodedRawData
    delay(100); // Do not get immediate repeat — 100 ms contra repetição imediata
  }
  irrecv.resume(); // receive the next value — libera o próximo quadro
} //END translateIR

void setup() { // define a configuração do modo integrado
  float Left_photosensitive; // reserva da luz esquerda
  float Right_photosensitive; // reserva da luz direita
  Serial.begin(9600); // serial em 9600 bps
  WiFi.setTxPower(WIFI_POWER_19_5dBm); // potência máxima do rádio
  WiFi.mode(WIFI_AP); // ponto de acesso
  WiFi.softAP(ssid, password, 9); // SSID e senha no canal 9
  IPAddress myIP = WiFi.softAPIP(); // IP do AP
  Serial.print("AP IP address: "); // rotula
  Serial.println(myIP); //probar 192.168.4.1 — imprime 192.168.4.1
  Car_init(); // ponte do app_httpd.cpp
  startServer(); // servidor do app_httpd.cpp
  irrecv.enableIRIn();// Start the receiver — liga o IR paralelo
}

void loop() { // define o laço integrado
  getIRData(); // atende o controle remoto
  Object_Following_mode(); // executa o modo ativo de seguimento
  Obstacles_Avoidance_mode(); // executa o modo ativo de desvio
  Line_Tracing_mode(); // executa o modo ativo de linha
  Light_Seeking_mode(); // executa o modo ativo de luz
}

O app_httpd.cpp da Lição 12 concentra as quatro malhas e o portal estendido. Leitura guiada das peças que importam:

// app_httpd.cpp da Lição 12 — peças de engenharia (nomes originais preservados)

// sensores e atuadores reunidos num arquivo só:
#define Echo 18 // sonar (GPIO 18)      // entrada do eco
#define Trig 19 // sonar (GPIO 19)      // saída do disparo
#define IRPIN_L 33 // desvio esquerdo   // entrada digital
#define IRPIN_R 32 // desvio direito    // entrada digital
#define L_light_PIN 35 // fotossensor E // entrada ADC
#define R_light_PIN 34 // fotossensor D // entrada ADC
#define servoPIN 4 // servo da torre    // saída PPM
#define LEFT_LINE_TRACJING 5 // linha E // entrada digital
#define CENTER_LINE_TRACJING 17 // linha C
#define right_LINE_TRACJING 16 // linha D

// estado global do portal:
int func_mode = 0; // 0 livre, 1 luz, 2 desvio, 3 seguimento, 4 linha
int carSpeed = 200; // velocidade ajustável pelo controle deslizante
int Servo = 90; // ângulo ajustável pelo controle deslizante

// Line_Tracing_mode, Obstacles_Avoidance_mode, Object_Following_mode e
// Light_Seeking_mode executam a malha correspondente somente quando func_mode
// casa com o modo; fora do modo retornam sem tocar a ponte. O cmd_handler
// estende o da Lição 11: além de var=car val=1..5, aceita var=model para
// trocar func_mode e variáveis de servo e velocidade dos controles deslizantes.

Gravação e operação: carregue a pasta 3_Code_and_Tutorial/12_WIFI_Web_Multi_Function_Car inteira, conecte no AP, abra 192.168.4.1 e teste em sequência com espaço livre: setas movem com parada ao soltar, cada modo assume o comportamento da lição isolada, o controle deslizante do servo move a torre no modo livre e o controle deslizante de velocidade altera visivelmente o passo. Se um modo parece morto, confira se outro ficou ativo e volte ao livre antes de concluir defeito. Se a página carrega mas os sliders não atuam, recarregue sem cache do navegador.

Aplicativo KUONGSHUN (Android APK)

A pasta 4_Android_Software_Installation_Package traz o KUONGSHUN.apk, comando alternativo ao navegador nos modos 11 e 12. No Android, copie o APK da pasta do kit para o celular e instale permitindo a fonte do kit; no iOS, busque KUONGSHUN na loja e instale. Operação: ligue o robô com baterias no modo 11 ou 12, conecte o WiFi em KUONGSHUN-AD178, abra o APP, selecione o carro ESP32 e toque em conectar. Resultado esperado: o direcional move com parada ao soltar e os botões assumem marchas e modos como no portal. Se o APP mostra WiFi conectado mas robô desconectado, reassocie no hotspot do robô e toque em conectar de novo, sem reinstalar. Se o direcional anda invertido, confira IN1/IN2 e IN3/IN4 nos bornes antes de culpar o APP. Nunca baixe APK de sites aleatórios: use o da documentação do kit.

⚠️ Atenção: o APP exige o sketch 11 ou 12 gravado, pois é o firmware que sobe o AP e atende os comandos. Com sketch de sensor isolado gravado, o APP conecta no WiFi mas não encontra o robô.

Solução de Problemas

SintomaCausa raizAção corretiva
Porta COM não aparece na IDEDriver CH340 ausente ou cabo só de cargaInstalar o CH340 da pasta do kit, trocar por cabo Tipo-C de dados, reiniciar
Failed to connect no uploadBOOT não segurado, porta ou cabo erradosSegurar BOOT na placa, conferir COM, trocar cabo, repetir
ledcSetup was not declaredNúcleo ESP32 3.x instaladoVoltar o núcleo para 2.0.12 no Gerenciador de Placas
ESP32Servo.h: No such fileBiblioteca fora do Sketchbook ou IDE sem reiniciarCopiar libraries do kit, reiniciar a IDE, conferir libraries/ESP32Servo
IRremote.h: No such fileBiblioteca ausente ou série erradaInstalar IRremote 4.1.2, nunca a 2.x, reiniciar a IDE
Carro não anda na Lição 1Bateria fraca ou borne soltoMedir pack acima de 7 V, reapertar bornes, chave ON
Um lado gira invertidoPar do lado trocado no borneCom bateria removida, inverter os 2 fios desse lado no L298N
Motor zumbe sem partirDuty abaixo do atrito estáticoSubir carSpeed para 150 ou mais, testar com bateria cheia
L298N esquenta muitoDuty alto parado ou marcha lenta longaPausar entre ensaios, reduzir velocidade, ventilar o dissipador
IR de desvio sempre 0Trimpot alto ou mira no soloGirar potenciômetro no anti-horário, nivelar o sensor à frente
Fotossensor não variaJanela tampada ou pino trocadoLiberar a janela, conferir GPIO 35 e 34, testar com lanterna
Ultrassom trava em zeroECHO fora do GPIO 18Refazer o jumper do ECHO, conferir 5 V do módulo
Ultrassom trava altoTRIG fora do GPIO 19Refazer o jumper do TRIG, afastar de cortinas e quinas
Servo assimétricoBraço fora do centro mecânicoReset para 90 graus, desplugue, solte o braço e reencaixe à frente
Linha oscila e saiVelocidade alta ou trimpot descalibradoBaixar carSpeed, recalibrar via a via na pista definitiva
Linha ignora a fitaContraste fraco ou altura excessivaFita preta fosca em fundo claro, curvas suaves, módulo baixo
Seguidor treme paradoEco ruidoso ou alvo pequenoTestar contra parede lisa, manter alvo dentro de 50 cm
Desvio gira sempre igualServo descentralizado ou IR descalibradoRefazer Lições 5 e 2 antes do código
IR remoto sem respostaPilha fraca, mira ou GPIO 15Trocar CR2025/CR2032, mirar à sombra, conferir o pino
Alcance IR de centímetrosSol direto ou fluorescente no receptorTestar à sombra, limpar o olho do receptor
SSID não apareceBateria fraca ou sketch sem APBateria cheia, regravar Lição 11 ou 12, conferir alimentação
192.168.4.1 não abreDados móveis roubando a rota ou modo erradoDesativar dados, manter sem internet, regravar o sketch Web
Página abre mas não andaOutro sketch com servidor gravadoRegravar a Lição 11 ou 12 inteira com o app_httpd.cpp
Controle deslizante não atuaCache do navegador ou modo erradoRecarregar sem cache, voltar ao modo livre
Modo parece mortoOutro func_mode ativoVoltar ao livre pelo portal antes de concluir defeito
APP não encontra o robôSketch sem AP ou WiFi de casa ativoGravar 11 ou 12, reassociar em KUONGSHUN-AD178, reconectar no APP

Solução de problemas

Olá! Sou o assistente técnico da RoboBuilders. Descreva seu problema com o Kit Carro Robô WiFi Multifuncional ESP32 AD178 - Curso Completo e eu ajudarei a encontrar a solução.

O assistente usa IA e pode errar. Confirme informações críticas no datasheet oficial do fabricante.