Contenido detallado
🧰 Arduino IDE 2.x y el paquete esp32 3.x
A Arduino IDE 2.x es el programa donde escribes el código, lo compilas y lo envías a la placa. Por sí sola, solo reconoce las placas Arduino; para ESP32 instalas el paquete "esp32" de Espressif, que actualmente está en la generación 3.x. Esta versión cambió varias funciones, por eso muchos tutoriales de 2022 o 2023 dan errores de compilación: se escribieron para la API 2.x.
🌱 ¿Eres nuevo aquí?
IDE (entorno de desarrollo) es el editor con botones para compilar y cargar. Compilar es traducir tu texto en C++ al lenguaje de máquina del chip. Un paquete de placas (core) es el conjunto de herramientas y bibliotecas que enseña a la IDE a comunicarse con una familia de chips. API es la lista de funciones que puedes llamar, como digitalWrite(); cuando cambia, el código antiguo puede dejar de compilar.
Instalación paso a paso
Descarga la IDE 2.x
En arduino.cc/en/software, elige tu sistema (Windows, macOS o Linux AppImage). Instálala y ábrela.
Abre el Gestor de placas
Ícono de placa en la barra lateral izquierda (o Herramientas → Placa → Gestor de placas). Escribe esp32.
Instala "esp32 by Espressif Systems" 3.x
Elige la versión 3 más reciente y haz clic en Instalar. Si el paquete no aparece, ve a Archivo → Preferencias y pega en "URLs adicionales": https://espressif.github.io/arduino-esp32/package_esp32_index.json
Comprueba la versión
En el Gestor de placas, el paquete debe mostrar "INSTALLED" con un número que empiece por 3. Ese número es el que debes indicar cuando pidas ayuda en un foro.
Cómo leer: a la izquierda, el camino antiguo (línea gris discontinua) pasaba por un canal numerado que creabas y luego conectabas al pin. A la derecha, el camino actual: una llamada prepara el pin y la escritura usa el propio pin. Si un tutorial te pide elegir "canal 0" del PWM, es de la API 2.x; adapta el código a las dos funciones verdes.
✓ Señales de un tutorial actual
- ✓Usa ledcAttach(pino, freq, bits) e ledcWrite(pino, valor).
- ✓Menciona "arduino-esp32 3.x" o ESP-IDF 5.
- ✓Usa un servomotor con la biblioteca ESP32Servo, no con Servo.h de Uno.
✗ Señales de un tutorial antiguo
- ✗Funciones de PWM con un número de canal separado del pin.
- ✗Te dice que "vuelvas a la versión 2.0.x del paquete" para que compile.
- ✗Afirma que el ESP32 no tiene analogWrite (el paquete actual sí lo tiene).
Por qué aprender
Saber la versión del paquete evita la peor frustración del comienzo: copiar un código "que funcionaba" y ver diez errores en rojo. Con la regla "canal separado = API antigua", corriges cualquier tutorial en un minuto.
Conceptos clave
🔌 Placa, puerto y primera carga
Para cargar un programa, el IDE necesita dos datos: qué placa (para compilar para el chip correcto) y qué puerto (por dónde enviarlo). Para el DevKit común (WROOM-32), elige ESP32 Dev Module; para placas con ESP32-S3, ESP32S3 Dev Module o el nombre exacto de tu placa. El puerto aparece cuando conectas el cable USB.
🌱 ¿Eres nuevo aquí?
A puerto es la "dirección" del cable en la computadora: COM3, COM4 en Windows; /dev/ttyUSB0 o /dev/ttyACM0 en Linux; /dev/cu.usbserial… en macOS. Muchas placas tienen un chip convertidor USB-serial (CP2102 o CH340) que puede necesitar un controlador en Windows. El botón BOOT pone el ESP32 en modo de recepción de programa; el EN (o RST) reinicia la placa.
Cómo leer: la fila verde muestra el recorrido del programa, de izquierda a derecha. Debajo de cada etapa está el fallo que más suele ocurrir. Cuando falle la carga, lee el último mensaje del IDE y encuentra en qué cuadro estás: eso ya te indica qué probar.
Objetivo: cargar en la placa real el mismo parpadeo del LED que ejecutaste en Wokwi en el módulo 1.1 (el LED externo en GPIO 2 con 220 Ω, o solo el LED azul de la placa, si tiene uno).
// Primera carga en la placa real (ESP32, paquete esp32 3.x)
const int PINO_LED = 2;
void setup() {
Serial.begin(115200);
pinMode(PINO_LED, OUTPUT);
Serial.println("Upload OK: o ESP32 esta rodando seu programa");
}
void loop() {
digitalWrite(PINO_LED, HIGH);
delay(500);
digitalWrite(PINO_LED, LOW);
delay(500);
}
Cómo verificar
- ✓El IDE termina con "Hard resetting via RTS pin..." y el LED empieza a parpadear.
- ✓En el Monitor Serie (115200 baud) aparece "Upload OK..."; si no aparece, presiona EN para reiniciar.
- ✗En Linux, si aparece "Permission denied" en el puerto, ejecuta sudo usermod -aG dialout $USER, cierra la sesión y vuelve a iniciarla.
✓ Cuando se atasque, sigue este orden
- ✓Cambia el cable por uno de datos que hayas comprobado.
- ✓Comprueba si el puerto desaparece y vuelve a aparecer al desconectar y conectar el cable.
- ✓Mantén presionado BOOT cuando aparezca "Connecting..." y suéltalo cuando veas el porcentaje.
✗ Lo que no lo resuelve
- ✗Reinstalar todo el IDE antes de probar el cable.
- ✗Dejar que el Monitor Serie de otro programa use el mismo puerto.
- ✗Conectar en GPIO 0 o 12 algo que fuerce un nivel durante el arranque y luego culpar al IDE.
Por qué aprender
La primera carga es donde más gente se rinde, casi siempre por el cable o el puerto. Con el mapa de las cuatro etapas, el error se vuelve predecible y la solución toma unos minutos.
Conceptos clave
🧱 setup, loop, variables y funciones
Todo sketch tiene dos funciones obligatorias. setup() se ejecuta una vez cuando se enciende la placa: aquí configuras los pines y el Serial. loop() se ejecuta otra vez apenas termina, para siempre: es el "pensar" del ciclo sentir-pensar-actuar. Dentro de ellas usas variables para guardar valores, if para decidir, for para repetir y funciones propias para darle un nombre a cada parte del comportamiento.
Cómo leer: de izquierda a derecha indica el orden en el tiempo. La flecha discontinua muestra que el loop() nunca termina. A la derecha, los cuadros cian son funciones que escribes y que el loop llama: el loop queda corto y cada función hace una sola cosa, con un nombre que la explica.
| Tipo | Guarda | Ejemplo en el robot |
|---|---|---|
| int | número entero | pin, lectura del ADC |
| float | número decimal | distancia en cm, voltaje |
| bool | verdadero o falso | ¿hay un obstáculo adelante? |
| unsigned long | número entero grande, sin signo | tiempo en ms con millis() |
| const | valor que no cambia | número del pin, límites |
Objetivo: un semáforo de un solo LED, escrito con funciones y un for, para que veas lo legible que queda el loop. Ejecútalo en Wokwi con un LED en GPIO 2.
// Variables, if, for y funciones (ESP32, paquete esp32 3.x)
const int PINO_LED = 2;
int ciclo = 0; // variable global: tiene validez en todo el programa
void piscar(int vezes, int intervaloMs) {
for (int i = 0; i < vezes; i++) {
digitalWrite(PINO_LED, HIGH); delay(intervaloMs);
digitalWrite(PINO_LED, LOW); delay(intervaloMs);
}
}
bool cicloPar(int n) { // función que devuelve un valor
return (n % 2 == 0);
}
void setup() {
Serial.begin(115200);
pinMode(PINO_LED, OUTPUT);
}
void loop() {
ciclo++;
if (cicloPar(ciclo)) {
Serial.println("ciclo par: 2 parpadeos lentos");
piscar(2, 400);
} else {
Serial.println("ciclo impar: 4 parpadeos rápidos");
piscar(4, 120);
}
delay(1000);
}
Cómo verificar: el LED alterna entre 4 parpadeos rápidos y 2 lentos, con una pausa de 1 s entre los grupos; el Serial Monitor escribe "ciclo impar..." y "ciclo par..." alternadamente.
Por qué aprender
El rover del módulo 1.6 tendrá un loop de pocas líneas que llama a lerDistancia(), decidir() e mover(). El código organizado así es lo que te permite ajustar una parte sin romper las demás.
Conceptos clave
🔍 Serial Monitor para depurar
Un robot no tiene pantalla. El Serial Monitor es tu ventana al interior del robot: el programa escribe con Serial.print() y tú lo lees en la computadora. El Serial Plotter, en el mismo menú Herramientas, dibuja los números como un gráfico en vivo, lo que permite ver el ruido y las tendencias de un vistazo. Depurar es exactamente esto: imprimir lo que el programa está pensando hasta encontrar dónde se equivoca.
🌱 ¿Eres nuevo aquí?
Baud rate es la velocidad de la comunicación serial, en bits por segundo. El número de Serial.begin(115200) debe coincidir con el seleccionado en la esquina de Serial Monitor; si es diferente, aparecen símbolos sin sentido. print escribe y continúa en la misma línea; println escribe y salta de línea.
Objetivo: generar dos curvas (una onda y una rampa) en el formato que entiende Serial Plotter, nombre:valor separados por comas, y un mensaje de estado en Serial Monitor. Funciona en Wokwi sin conectar nada.
// Serial Monitor y Serial Plotter (ESP32, paquete esp32 3.x)
unsigned long amostra = 0;
void setup() {
Serial.begin(115200); // mismo valor en la esquina de Monitor/Plotter
delay(500);
Serial.println("Depuración iniciada");
}
void loop() {
float onda = 50.0 * sin(amostra * 0.1); // sube y baja entre -50 y 50
int rampa = amostra % 100; // sube de 0 a 99 y vuelve a empezar
// formato de Plotter: nombre:valor,nombre:valor y salto de línea
Serial.print("onda:"); Serial.print(onda);
Serial.print(",rampa:"); Serial.println(rampa);
amostra++;
delay(50); // 20 puntos por segundo
}
Cómo verificar
- ✓En Serial Monitor: "Depuración iniciada" y luego líneas como "onda:29.55,rampa:6".
- ✓Cierra Monitor y abre Herramientas → Serial Plotter: verás una onda sinusoidal y un diente de sierra, con las leyendas "onda" y "rampa".
- ✗¿Símbolos extraños? El baud de Monitor no está en 115200.
💡 Depuración útil
Imprime el nombre y el valor ("dist=23.4", no solo "23.4") y un mensaje en cada rama del if para saber por dónde pasó el código. Evita imprimir miles de líneas por segundo sin delay (la ventana se congela) y recuerda que Monitor y Plotter no pueden usar el puerto al mismo tiempo.
Por qué aprender
Cuando el robot haga algo inesperado, el serial te permitirá descubrir si el sensor leyó mal, si la decisión fue incorrecta o si el motor no obedeció. En la Ruta 2, el mismo hábito se convierte en telemetría por Wi-Fi.
Conceptos clave
🔘 Entradas: botón y lectura analógica
Hasta ahora, la placa solo había "hablado". Ahora escucha. Un botón con INPUT_PULLUP (visto en el módulo 1.2) se lee con digitalRead(); un potenciómetro (resistor con perilla giratoria) se lee con analogRead(), que devuelve valores de 0 a 4095. Los botones mecánicos tienen un detalle: el contacto "rebota" durante unos milisegundos y el programa puede contar varias pulsaciones como una sola.
🌱 ¿Eres nuevo aquí?
Antirrebote consiste en ignorar los cambios que duran menos de un tiempo mínimo (unos 30 ms), para contar solo las pulsaciones reales. millis() devuelve cuántos milisegundos han pasado desde que se encendió la placa; comparar dos valores de millis() permite medir intervalos sin detener el programa. El potenciómetro tiene tres patas: las de los extremos van a 3,3 V y GND; la del medio entrega el voltaje variable.
Cómo leer: a la izquierda, el botón conecta GPIO 4 a GND al presionarlo (sin resistor externo: el pull-up está dentro del chip). A la derecha, el potenciómetro recibe 3,3 V (rojo) y GND (cian) en los extremos; la flecha indica el cursor central, que va a GPIO 34. Abajo, el LED de GPIO 2 muestra el resultado. Nunca conectes el potenciómetro a 5 V: GPIO 34 recibiría más de 3,3 V al final del recorrido.
Objetivo: contar las pulsaciones del botón con antirrebote (cada pulsación invierte el LED) y mostrar la posición del potenciómetro en porcentaje. Arma el circuito como en la figura de Wokwi ("Pushbutton", "Potentiometer", "LED" y "Resistor").
// Botón con antirrebote + potenciómetro (ESP32, paquete esp32 3.x)
const int PINO_BOTAO = 4; // botón conectado a GND, pull-up interno
const int PINO_POT = 34; // ADC1, solo entrada
const int PINO_LED = 2;
const unsigned long DEBOUNCE_MS = 30;
int estadoEstavel = HIGH; // suelto
int ultimaLeitura = HIGH;
unsigned long mudouEm = 0;
int apertos = 0;
bool ledLigado = false;
unsigned long ultimoPrint = 0;
void setup() {
Serial.begin(115200);
pinMode(PINO_BOTAO, INPUT_PULLUP);
pinMode(PINO_LED, OUTPUT);
Serial.println("Presiona el boton y gira el potenciometro");
}
void loop() {
int leitura = digitalRead(PINO_BOTAO);
if (leitura != ultimaLeitura) { mudouEm = millis(); ultimaLeitura = leitura; }
if (millis() - mudouEm > DEBOUNCE_MS && leitura != estadoEstavel) {
estadoEstavel = leitura;
if (estadoEstavel == LOW) { // acaba de presionarse
apertos++;
ledLigado = !ledLigado;
digitalWrite(PINO_LED, ledLigado ? HIGH : LOW);
Serial.print("Pulsacion numero "); Serial.println(apertos);
}
}
if (millis() - ultimoPrint >= 500) { // pot cada medio segundo, sin delay
ultimoPrint = millis();
int bruto = analogRead(PINO_POT); // 0 a 4095
int pct = map(bruto, 0, 4095, 0, 100);
Serial.print("pot="); Serial.print(bruto);
Serial.print(" ("); Serial.print(pct); Serial.println("%)");
}
}
Cómo verificar
- ✓Cada pulsación invierte el LED y suma 1 en "Pulsacion numero N", nunca 2 o 3 de una vez.
- ✓Al girar el potenciómetro de un extremo al otro, "pot=" va de cerca de 0 (0%) a 4095 (100%).
- ✗¿Cuenta pulsaciones por sí solo? Comprueba que el botón esté en columnas diferentes de la protoboard y que el pin sea el 4.
Por qué aprender
Es la misma lectura que usarás con sensores reales en el módulo 1.4: digital para el sensor IR de obstáculos, analógica para el sensor de línea. Y el truco de millis() en lugar de delay() vuelve, a lo grande, en el último tema de sensores.
Conceptos clave
🎚️ PWM con la API 3.x: brillo y sonido
Un pin digital solo sabe dar 0 o 3,3 V. Para que un LED tenga brillo medio, el truco consiste en encenderlo y apagarlo muy rápido: si permanece encendido la mitad del tiempo, el ojo percibe la mitad del brillo. Esto es PWM (modulación por ancho de pulso). En el ESP32, el periférico LEDC genera el PWM y, en la API 3.x, bastan dos funciones: ledcAttach(pino, frequência, bits) e ledcWrite(pino, valor). Para el sonido, ledcWriteTone(pino, frequência) cambia la frecuencia y mantiene el pulso a la mitad.
🌱 ¿Eres nuevo aquí?
Ciclo de trabajo (duty cycle) es el porcentaje del tiempo durante el cual la señal permanece encendida. Frecuencia es cuántas veces por segundo se repite el ciclo (Hz). Resolución es en cuántos niveles divides el ciclo: con 8 bits, de 0 a 255; con 10 bits, de 0 a 1023. Un buzzer pasivo solo suena si recibe una frecuencia; el activo pita por sí solo cuando se enciende.
Cómo leer: las tres líneas tienen el mismo ritmo (frecuencia); solo cambia cuánto tiempo permanece alto cada pulso. A la derecha, el brillo que percibe el ojo y el valor de ledcWrite en 8 bits (255 = 100%). En el módulo 1.5, el mismo número se convertirá en velocidad del motor.
Objetivo: el potenciómetro del tema 5 controla el brillo del LED del GPIO 2, y el botón reproduce una escala corta en un buzzer pasivo en el GPIO 25. En Wokwi, usa la pieza «Buzzer» (es pasiva).
// PWM na API 3.x: brilho pelo potenciômetro e som no buzzer (ESP32)
const int PINO_LED = 2;
const int PINO_BUZZER = 25;
const int PINO_POT = 34;
const int PINO_BOTAO = 4;
const int NOTAS[] = {262, 294, 330, 349, 392}; // dó, ré, mi, fá, sol (Hz)
void tocarEscala() {
for (int i = 0; i < 5; i++) {
ledcWriteTone(PINO_BUZZER, NOTAS[i]); // muda a frequência, 50% de ciclo
delay(180);
}
ledcWrite(PINO_BUZZER, 0); // silêncio
}
void setup() {
Serial.begin(115200);
pinMode(PINO_BOTAO, INPUT_PULLUP);
ledcAttach(PINO_LED, 5000, 8); // LED: 5 kHz, 8 bits (0 a 255)
// canais 0-1, 2-3, 4-5, 6-7 dividem um temporizador, e ledcWriteTone muda a
// frequência do temporizador inteiro: o buzzer vai para o canal 2, longe do LED
ledcAttachChannel(PINO_BUZZER, 2000, 10, 2);
ledcWrite(PINO_BUZZER, 0);
Serial.println("PWM pronto: gire o pot, aperte o botao");
}
void loop() {
int bruto = analogRead(PINO_POT); // 0 a 4095
int brilho = map(bruto, 0, 4095, 0, 255); // 0 a 255
ledcWrite(PINO_LED, brilho); // recebe o PINO
if (digitalRead(PINO_BOTAO) == LOW) {
Serial.println("Tocando escala");
tocarEscala();
while (digitalRead(PINO_BOTAO) == LOW) delay(10); // espera soltar
}
Serial.print("brilho:"); Serial.println(brilho);
delay(50);
}
Cómo verificar
- ✓Girar el potenciómetro cambia el brillo suavemente, sin parpadeos; el Serial Plotter muestra «brilho» de 0 a 255.
- ✓Al presionar el botón, suenan cinco notas ascendentes y el Monitor muestra «Tocando escala».
- ✗Error de compilación en ledcAttach? El paquete instalado es 2.x: actualízalo a 3.x (tema 1).
💡 Frecuencia adecuada para cada uso
LED: por encima de 1 kHz (5 kHz es un buen valor estándar) para que no parpadee en cámara. Motor DC: entre 1 y 20 kHz, según el controlador. Servo: 50 Hz, pero usa la biblioteca ESP32Servo en el módulo 1.5. Cuanto mayor sea la frecuencia, menor será la resolución máxima disponible; 8 a 10 bits son suficientes para robots.
Por qué aprender
El PWM es la forma en que el ESP32 controla la velocidad de los motores mediante el puente H en el módulo 1.5 y el ángulo de los servos. La bocina del rover, en los próximos pasos del módulo 1.6, usa el mismo ledcWriteTone de este ejemplo.
Conceptos clave
✅ Resumen del módulo
Para profundizar
- Instalar Arduino-ESP32 (Espressif) — Administrador de placas y URL oficial
- LEDC (PWM) en la API 3.x — ledcAttach, ledcWrite, ledcWriteTone
- Guía de migración 2.x → 3.0 — qué cambió y cómo adaptar el código
- Serial Plotter de la IDE 2 — formato de las variables y leyendas
- Referencia del lenguaje Arduino — if, for, millis, map
Siguiente módulo
1.4 · Sensores: el robot percibe. Distancia con HC-SR04, línea y obstáculos con infrarrojos, filtro de ruido, I2C y lectura de varios sensores sin bloquearse.