Robótica com C — Bloco 1
Na Aula 42 você trabalhou com sinais digitais: HIGH ou LOW, ligado ou desligado, 5V ou 0V. Mas o mundo físico é contínuo — a temperatura não pula de 0°C para 100°C instantaneamente, um potenciômetro não vai do mínimo ao máximo de uma vez, a luz do ambiente tem infinitos níveis de intensidade.
Para ler essa realidade contínua, o Arduino Uno tem 6 pinos analógicos (A0 a A5) equipados com um conversor analógico-digital (ADC) de 10 bits. Para simular uma saída contínua, usa-se PWM em pinos específicos.
ADC de 10 bits: o conversor divide a faixa de tensão (0V a 5V) em 2¹⁰ = 1024 valores possíveis (0 a 1023). Cada unidade representa 5V ÷ 1024 ≈ 4,9 mV de resolução. Quanto maior a tensão, maior o número retornado por analogRead().
| Aspecto | Digital | Analógico |
|---|---|---|
| Valores possíveis | 2 — HIGH ou LOW | 1024 — de 0 a 1023 |
| Função de leitura | digitalRead(pino) |
analogRead(pinoA) |
| Pinos no Uno | D0 a D13 (14 pinos) | A0 a A5 (6 pinos) |
| Componentes típicos | Botão, LED, relé | Potenciômetro, LDR, sensor de temperatura, joystick |
| Tensão de 2,5V retorna | Indefinido (zona cinza) | ~512 (metade de 1023) |
analogRead() e o potenciômetroO potenciômetro é um resistor variável com três terminais: dois externos (conectados a 5V e GND) e um central (o cursor, que desliza). À medida que você gira o botão, o cursor varia a tensão no terminal central de 0V a 5V de forma linear e contínua — o sensor analógico mais simples possível.
// Lendo um potenciômetro no pino A0 // Ligação: terminal esquerdo → 5V, terminal direito → GND, cursor → A0 #define PINO_POT A0 void setup() { Serial.begin(9600); // Pinos analógicos são sempre INPUT — não precisa de pinMode() } void loop() { int valor = analogRead(PINO_POT); // 0 a 1023 Serial.print("Leitura bruta: "); Serial.println(valor); delay(200); // evita inundar o Monitor Serial }
Leitura bruta: 0
Leitura bruta: 127
Leitura bruta: 512
Leitura bruta: 890
Leitura bruta: 1023
Não precisa de pinMode(): pinos analógicos A0–A5 são configurados como entrada por padrão. Ao contrário dos pinos digitais, você pode chamar analogRead() diretamente sem nenhuma configuração prévia.
map(): remapeando intervalosO valor bruto de analogRead() vai de 0 a 1023 — mas raramente você quer trabalhar com essa faixa diretamente. Quer converter para porcentagem (0–100), para ângulo de servo (0–180), para temperatura em graus, ou para brilho de LED (0–255). A função map() faz essa conversão linear:
// Assinatura de map(): // map(valor, deMin, deMax, paraMin, paraMax) long resultado = map(512, 0, 1023, 0, 100); // resultado = 50 — metade do intervalo original vira metade do novo
É exatamente uma regra de três implementada para você. A fórmula por baixo é:
resultado = (valor - deMin) * (paraMax - paraMin) / (deMax - deMin) + paraMin;
Aplicando nos casos práticos mais comuns:
// Potenciômetro → múltiplas representações simultâneas #define PINO_POT A0 void setup() { Serial.begin(9600); } void loop() { int bruto = analogRead(PINO_POT); // 0 – 1023 int porcento = map(bruto, 0, 1023, 0, 100); // 0 – 100 % int angulo = map(bruto, 0, 1023, 0, 180); // 0 – 180° (servo) int brilho = map(bruto, 0, 1023, 0, 255); // 0 – 255 (PWM/LED) float tensao = bruto * (5.0 / 1023.0); // 0.00 – 5.00 V Serial.print("Bruto="); Serial.print(bruto); Serial.print(" %="); Serial.print(porcento); Serial.print(" ang="); Serial.print(angulo); Serial.print(" bri="); Serial.print(brilho); Serial.print(" V="); Serial.println(tensao); delay(300); }
Bruto=0 %=0 ang=0 bri=0 V=0.00
Bruto=256 %=25 ang=45 bri=64 V=1.25
Bruto=512 %=50 ang=90 bri=128 V=2.50
Bruto=768 %=75 ang=135 bri=192 V=3.75
Bruto=1023 %=100 ang=180 bri=255 V=5.00
Por que 5.0 e não 5 no cálculo de tensão? Porque em C (e no Arduino) a divisão entre dois int é inteira — 5 / 1023 resultaria em 0, não 0.004.... O sufixo .0 força a operação para float. Isso é exatamente o type casting que você aprendeu na Aula 02.
O Arduino Uno não tem um conversor digital-analógico (DAC) real — ele só produz 0V ou 5V. Para simular tensões intermediárias, usa uma técnica chamada PWM (Pulse Width Modulation): o pino alterna rapidamente entre HIGH e LOW. Se ficar HIGH por 50% do tempo, o componente recebe em média 2,5V. Se ficar HIGH por 25% do tempo, recebe em média 1,25V.
Frequência do PWM: no Arduino Uno, o PWM opera a ~490 Hz (ou 980 Hz no pino 5 e 6). Isso significa que o ciclo HIGH/LOW se repete 490 vezes por segundo — rápido demais para o olho humano perceber a intermitência em um LED, e rápido o suficiente para motores e servos responderem à média.
A função é analogWrite(pino, valor), onde o valor vai de 0 a 255 (8 bits). Apenas os pinos marcados com ~ na placa suportam PWM: no Uno são os pinos 3, 5, 6, 9, 10 e 11.
Valor em analogWrite() |
Duty cycle (% do tempo em HIGH) | Tensão média no pino | Efeito num LED |
|---|---|---|---|
| 0 | 0% | 0V | Apagado |
| 64 | 25% | ~1,25V | Brilho fraco |
| 128 | 50% | ~2,5V | Brilho médio |
| 191 | 75% | ~3,75V | Brilho forte |
| 255 | 100% | 5V | Brilho máximo |
// LED com brilho controlado pelo potenciômetro // Potenciômetro em A0; LED com resistor 220Ω no pino 9 (~) #define PINO_POT A0 #define PINO_LED 9 // pino 9 tem PWM (~) void setup() { pinMode(PINO_LED, OUTPUT); Serial.begin(9600); } void loop() { int leitura = analogRead(PINO_POT); // 0 – 1023 int brilho = map(leitura, 0, 1023, 0, 255); // converte para 0 – 255 analogWrite(PINO_LED, brilho); // aplica o PWM Serial.print("POT="); Serial.print(leitura); Serial.print(" PWM="); Serial.println(brilho); delay(50); }
POT=0 PWM=0
POT=312 PWM=77
POT=511 PWM=127
POT=765 PWM=190
POT=1023 PWM=255
Atenção: analogWrite() só funciona em pinos com ~. Se você chamar analogWrite() em um pino sem PWM (como o pino 7), o Arduino simplesmente liga ou desliga o pino — sem gradação. Verifique sempre se o pino tem o símbolo ~ na placa antes de usar.
millis()Um padrão muito útil em robótica é aumentar ou diminuir gradualmente a intensidade de um motor ou LED — o fade. Usando millis() (sem bloquear o programa), você pode fazer um fade suave enquanto o restante do sistema continua funcionando:
/* * Fade automático — LED aumenta e diminui o brilho continuamente * Usa millis() para não bloquear outros componentes */ #define PINO_LED 9 #define PASSO 5 // quanto o brilho muda por vez #define INTERVALO 20 // ms entre cada passo int brilho = 0; int direcao = PASSO; // positivo = subindo, negativo = descendo unsigned long ultimoPasso = 0; void setup() { pinMode(PINO_LED, OUTPUT); Serial.begin(9600); } void loop() { unsigned long agora = millis(); if (agora - ultimoPasso >= INTERVALO) { ultimoPasso = agora; brilho += direcao; // Inverte direção nos extremos if (brilho >= 255) { brilho = 255; direcao = -PASSO; } if (brilho <= 0) { brilho = 0; direcao = PASSO; } analogWrite(PINO_LED, brilho); Serial.println(brilho); } // Espaço livre para ler sensores, botões, etc. }
0 → 5 → 10 → ... → 250 → 255 → 250 → ... → 5 → 0 → 5 → ...
Esse é o padrão de controle de motor que você vai usar no robô. Trocar PINO_LED por um pino de motor DC e brilho por velocidade é a única mudança necessária. Aprenda bem esse padrão aqui — ele retorna nas aulas de robótica.
Sensor para leitura analógicaAssim como encapsulamos o botão numa struct na Aula 42, podemos fazer o mesmo para sensores analógicos. Projetos reais frequentemente têm dois ou três sensores analógicos — um joystick, por exemplo, tem dois potenciômetros (eixo X e eixo Y) além de um botão:
/* * Leitura de joystick analógico * Joystick: eixo X → A0, eixo Y → A1, botão → pino 7 * Mostra posição e detecta clique no Monitor Serial */ typedef struct { int pinoAnalogico; int minBruto; // valor mínimo calibrado int maxBruto; // valor máximo calibrado int centro; // valor de repouso (meio) } Eixo; // Inicializa um eixo do joystick void inicializarEixo(Eixo* e, int pino) { e->pinoAnalogico = pino; e->minBruto = 0; e->maxBruto = 1023; e->centro = 512; } // Lê e normaliza o eixo: retorna -100 a +100 (centro = 0) int lerEixo(Eixo* e) { int bruto = analogRead(e->pinoAnalogico); // Remapeia para -100 a +100 return map(bruto, e->minBruto, e->maxBruto, -100, 100); } Eixo eixoX, eixoY; // Botão do joystick (com debounce simplificado) #define PINO_BTN 7 int estadoAnteriorBtn = HIGH; void setup() { inicializarEixo(&eixoX, A0); inicializarEixo(&eixoY, A1); pinMode(PINO_BTN, INPUT_PULLUP); Serial.begin(9600); Serial.println("Joystick pronto. Mova os eixos!"); } void loop() { int x = lerEixo(&eixoX); int y = lerEixo(&eixoY); Serial.print("X="); if (x >= 0) Serial.print("+"); Serial.print(x); Serial.print(" Y="); if (y >= 0) Serial.print("+"); Serial.print(y); // Detecta clique no botão int btnAtual = digitalRead(PINO_BTN); if (btnAtual == LOW && estadoAnteriorBtn == HIGH) { Serial.print(" [CLIQUE!]"); } estadoAnteriorBtn = btnAtual; Serial.println(); delay(100); }
Joystick pronto. Mova os eixos!
X=+2 Y=-1
X=+45 Y=+12
X=+98 Y=+87
X=-72 Y=+34
X=+3 Y=-2 [CLIQUE!]
Conectando os temas: neste exemplo você usou typedef struct, ponteiros, ->, funções que recebem Eixo*, analogRead(), map(), digitalRead() com pull-up e detecção de borda — tudo junto em ~50 linhas. Esse é o estilo de código que vai aparecer no robô.
| Conceito | C puro (PC) | Arduino |
|---|---|---|
| Ler sensor analógico | Requer driver de SO, biblioteca específica ou acesso a /dev | analogRead(A0) — direto, sem configuração prévia |
| Faixa de leitura | Depende do hardware e do driver | Sempre 0 a 1023 (10 bits, 0V a 5V) |
| Saída com gradação | Depende do hardware (DAC, placa de som…) | analogWrite(pino, 0–255) via PWM em pinos ~ |
| Conversão de intervalo | Regra de três manual ou função própria | map(val, deMin, deMax, paraMin, paraMax) pronto |
| Divisão inteira vs float | Mesma regra — 5/1023 = 0, 5.0/1023 = 0.004 |
Mesma regra — idêntico ao C |
| Structs para sensores | ✅ — padrão do curso | ✅ — funciona identicamente |
Teste o que você aprendeu sobre sinais analógicos no Arduino: