Ejercicio 04 — Convertir un porcentaje (0..100) a PWM (0..255) con map() y aplicarlo…

Dificultad: amarillo · Módulo 03 (Salidas analogicas (PWM))

Enunciado

Convertir un porcentaje (0..100) a PWM (0..255) con map() y aplicarlo al LED.

Montaje (Arduino UNO)

LED en pin 9 (~) con resistencia 220 ohm a GND

Cómo se resuelve

Visualmente este ejercicio se parece a la rampa del ej01, pero lo que enseña no es el LED: es una herramienta, map(), que vas a usar en casi todos los módulos que quedan. La idea es dejar de pensar en cuentas de 0 a 255 y volver a pensar en porcentaje, que es la unidad del problema.

El montaje es el de siempre en este módulo: LED en el pin 9 con su resistencia de 220 Ω a GND, y pinMode(LED, OUTPUT) en setup().

La pieza nueva es esta función:

void brilloPorcentaje(int pct) {
    int pwm = map(pct, 0, 100, 0, 255);   // 0..100 -> 0..255
    analogWrite(LED, pwm);
}

map(valor, deMin, deMax, aMin, aMax) reescala un número de un rango a otro conservando la proporción. El orden de los argumentos es la parte que hay que memorizar: primero el rango de origen (0, 100, el porcentaje) y después el de destino (0, 255, las cuentas de PWM). Invertirlos y escribir map(pct, 0, 255, 0, 100) compila perfectamente y devuelve valores absurdos, sin ningún aviso; es el fallo típico con esta función.

Por dentro, map hace exactamente la regla de tres que en el ejercicio 02 calculabas a mano: (pct - 0) * (255 - 0) / (100 - 0). Y la hace con enteros, truncando el resultado, así que map(50, 0, 100, 0, 255) da 127 y no 127,5; map(5, ...) da 12 en vez de 12,75. Sobre 256 niveles esa media cuenta no se ve, pero conviene saber que está ahí, porque en rangos pequeños el truncamiento sí puede notarse.

Entonces, ¿qué gana map frente a escribir 255 * pct / 100? Sobre todo dos cosas. La primera es que expresa la intención: la línea dice “reescala de este rango a este otro”, no “multiplica y divide por unos números”. La segunda es que la conversión queda encerrada en un único sitio; si mañana el LED se cambia por un servo que espera de 0 a 180, se toca ese map y nada más. Y como bonus, sirve para cualquier par de rangos sin rehacer la aritmética, incluso invertidos: map(pct, 0, 100, 255, 0) da un brillo que baja cuando el porcentaje sube. A cambio hay que aceptar el truncamiento entero y que map no recorta fuera de rango: si le pasas 150, te devuelve 382, un valor que analogWrite no espera. Cuando el dato venga de fuera y no de un bucle tuyo, ese recorte hay que hacerlo aparte con constrain().

Que la conversión viva dentro de una función es la otra mitad del ejercicio. brilloPorcentaje(int pct) te da una orden que se lee en la unidad correcta, y quien la llama ya no necesita saber que por debajo existen los 255. Eso permite que el loop() sea sólo la secuencia:

for (int pct = 0; pct <= 100; pct += 5) {
    brilloPorcentaje(pct);
    delay(100);
}
delay(500);

El bucle recorre 0, 5, 10… hasta 100: 21 pasos de 100 ms cada uno, es decir unos 2,1 segundos de rampa. Compáralo con el ej01, que subía de uno en uno con delay(8): aquí los escalones son mucho más gruesos (cada paso de 5 % son casi 13 cuentas de PWM) y más lentos, así que la rampa se ve claramente escalonada en vez de continua. Es un buen sitio para experimentar: con pct++ y delay(20) la subida dura lo mismo y se vuelve suave.

El delay(500) de después del bucle está fuera del for, y por tanto se ejecuta con el LED en el último valor escrito, el 100 %. Es medio segundo de brillo máximo antes de que el loop() vuelva a empezar y lo tire de golpe a 0. Ese apagón brusco es intencionado, para que se distinga dónde acaba una rampa y empieza la siguiente; si lo que quieres es una respiración continua, la solución es la del ej01, añadir el bucle de bajada recorriendo el porcentaje al revés.

El verdadero destino de todo esto es el módulo siguiente. Cuando el valor deje de venir de un for y venga de un potenciómetro, llegará en el rango 0..1023 de analogRead, y la línea que lo convierte en brillo será la misma de aquí cambiando dos números: map(lectura, 0, 1023, 0, 255).

Para practicar — cópialo y complétalo

Pega este esqueleto y completa los TODO. Es la mejor forma de aprender: inténtalo antes de mirar la solución.

/*
 * Curso de Arduino — Módulo 03: Salidas analógicas (PWM)
 * Ejercicio 04 — PRÁCTICA (rellena los TODO)
 * Enunciado: Convertir un porcentaje (0..100) a PWM (0..255) con map() y aplicarlo al LED.
 * Dificultad: amarillo
 * Placa: Arduino UNO    Montaje: LED en pin 9 (~) con resistencia 220 ohm a GND
 * Correr: Arduino IDE / Wokwi (wokwi/ej04) / arduino-cli (ver README)
 *
 * Pista: map(valor, deMin, deMax, aMin, aMax). Aquí: map(pct, 0,100, 0,255).
 */
const int LED = 9;   // pin PWM (~)
 
// Aplica un brillo expresado en porcentaje (0..100).
void brilloPorcentaje(int pct) {
    // TODO: int pwm = map(pct, 0, 100, 0, 255);
    // TODO: analogWrite(LED, pwm);
}
 
void setup() {
    // TODO: configura LED como salida
}
 
void loop() {
    // TODO: bucle for que recorra pct de 0 a 100 (paso 5)
    //       dentro: brilloPorcentaje(pct) y delay(100)
}

Solución — cópiala y ejecútala

/*
 * Curso de Arduino — Módulo 03: Salidas analógicas (PWM)
 * Ejercicio 04 — MODELO (resuelto)
 * Enunciado: Convertir un porcentaje (0..100) a PWM (0..255) con map() y aplicarlo al LED.
 * Dificultad: amarillo
 * Placa: Arduino UNO    Montaje: LED en pin 9 (~) con resistencia 220 ohm a GND
 * Correr: Arduino IDE / Wokwi (wokwi/ej04) / arduino-cli (ver README)
 *
 * Idea: map(valor, deMin, deMax, aMin, aMax) reescala un rango a otro.
 *       map(50, 0,100, 0,255) -> 127. Así razonamos en % y no en "cuentas" de 0..255.
 */
const int LED = 9;   // pin PWM (~)
 
// Aplica un brillo expresado en porcentaje (0..100).
void brilloPorcentaje(int pct) {
    int pwm = map(pct, 0, 100, 0, 255);   // 0..100 -> 0..255
    analogWrite(LED, pwm);
}
 
void setup() {
    pinMode(LED, OUTPUT);
}
 
void loop() {
    // recorre 0..100 % subiendo y aplica el brillo
    for (int pct = 0; pct <= 100; pct += 5) {
        brilloPorcentaje(pct);
        delay(100);
    }
    delay(500);
}

Cómo ejecutar la simulación

Sin instalar nada, en el navegador

  1. Abre wokwi.com y crea un proyecto nuevo de la placa que pide el ejercicio.
  2. Pega la solución (o tu intento) en la pestaña sketch.ino.
  3. Abre la pestaña diagram.json, borra lo que haya y pega el circuito de aquí abajo.
  4. Dale al play. Verás el circuito funcionando: LEDs, pantallas y monitor serie.
Circuito del ejercicio (diagram.json)
{
  "version": 1,
  "author": "ESIBot",
  "editor": "wokwi",
  "parts": [
    { "type": "wokwi-arduino-uno", "id": "uno", "top": 0, "left": 0, "attrs": {} },
    { "type": "wokwi-led", "id": "led", "top": -50, "left": 150, "attrs": { "color": "red" } },
    { "type": "wokwi-resistor", "id": "r1", "top": 100, "left": 150, "attrs": { "value": "220" } }
  ],
  "connections": [
    [ "uno:9", "r1:1", "green", [] ],
    [ "r1:2", "led:A", "green", [] ],
    [ "led:C", "uno:GND.1", "black", [] ]
  ],
  "dependencies": {}
}

En VS Code, con el repo del curso

El paquete de simulación de este ejercicio está en practica/03-salidas-analogicas-pwm/wokwi/ej04/. Con la extensión Wokwi instalada y la licencia activada (lo cuenta el onboarding del curso):

make sim EJ=03-salidas-analogicas-pwm/ej04

y después, en VS Code, Ctrl+Shift+PWokwi: Start Simulator. La extensión no compila: simula un firmware ya compilado, y de compilarlo se encarga ese make.

Cómo usarlo

Con placa: abre el .ino en el Arduino IDE, elige placa y puerto y pulsa Subir. Sin placa: simulalo en Wokwi con cualquiera de las dos vias de arriba. Antes de mirar la solucion, intenta completar tu el esqueleto: es la mejor forma de aprender.

Conexiones