Aula 43 — Entradas e Saídas Analógicas

Robótica com C — Bloco 1

Digital vs Analógico — o mundo não é só 0 e 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ômetro

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

A função map(): remapeando intervalos

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

PWM — a saída "analógica" do Arduino

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.

Fade automático com 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.

Exemplo completo: struct Sensor para leitura analógica

Assim 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ô.

C vs Arduino — Sinais Analógicos

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

Quiz — Aula 43

Teste o que você aprendeu sobre sinais analógicos no Arduino:

1. Qual é a faixa de valores retornada por analogRead() no Arduino Uno?

2. Um potenciômetro está na metade do curso. Qual valor aproximado analogRead(A0) retorna?

3. O que faz map(valor, 0, 1023, 0, 180)?

4. O que é PWM (Pulse Width Modulation)?

5. Qual é a faixa de valores do parâmetro de analogWrite(pino, valor)?

6. Por que bruto * (5 / 1023) retorna sempre 0 no Arduino?

7. Quais pinos do Arduino Uno suportam analogWrite() (PWM)?

8. No exemplo do joystick, por que a função lerEixo() recebe Eixo* em vez de Eixo?