GUÍA DE INGENIERÍA Y DESARROLLO
CURSO PRÁCTICO Y
COMPLETO DE ARDUINO
De Cero a Desarrollador Senior de Hardware y Prototipado
Un manual estructurado en 10 módulos condensados. Diseñado para aprender
arquitectura de microcontroladores, electrónica aplicada, programación
avanzada C/C++, interrupciones, sensores, actuadores y buenas prácticas de
ingeniería de software.
Arquitectura: ATmega328P / Arduino Uno R3 & R4
Enfoque: Producción, Código Limpio, Interrupciones y Protocolos Bus (I2C, SPI)
Nivel: Principiante a Avanzado
Formato: Manual de Referencia Técnica
Tutorial de Arduino - Guía de Desarrollo Página 1 de 19
MÓDULO 1: INTRODUCCIÓN Y ARQUITECTURA DE
HARDWARE
Arduino es una plataforma de desarrollo de código abierto basada en hardware y software flexible y fácil de
usar. En su núcleo más extendido (Arduino Uno R3), encontramos el microcontrolador de 8 bits
ATmega328P de Microchip (Atmel).
1.1 Esquema de la Placa Arduino Uno R3
Para programar con eficiencia, es imprescindible comprender la anatomía física y funcional del hardware:
Componente Función Técnica Especificaciones / Detalle
Unidad Central de Procesamiento ATmega328P @ 16 MHz (Clock), arquitectura AVR
Microcontrolador
(MCU) de 8 bits.
Almacenamiento del Firmware
Memoria Flash 32 KB (0.5 KB reservados para el Bootloader).
(Programa)
SRAM Memoria Volátil de Trabajo 2 KB para variables dinámicas y pila de ejecución.
EEPROM Memoria No Volátil de Datos 1 KB para datos persistentes entre reinicios.
14 pines (D0 a D13). Soporta corriente máxima de
Pines E/S Digitales Entrada y Salida Lógica de 5V
40mA por pin.
6 pines identificados con el símbolo '~' (D3, D5,
Pines PWM Modulación por Ancho de Pulso
D6, D9, D10, D11).
6 entradas (A0 a A5). Resolución de 0 a 1023 (0V
Pines Analógicos Entradas ADC de 10 bits
a 5V).
Alimentación (VIN/ USB 5V regulado, o Jack DC 7-12V (Regulador
Suministro Eléctrico
USB) interno a 5V/3.3V).
1.2 Mapa de Distribución de Pines (Pinout)
Clasificación Operativa de Pines
• Digital IO D0 - D13: Operan a 0V (LOW) y 5V (HIGH). Los pines D0 (RX) y D1 (TX) están
compartidos con el puerto serie USB.
Tutorial de Arduino - Guía de Desarrollo Página 2 de 19
• PWM ~D3, ~D5, ~D6, ~D9, ~D10, ~D11: Emiten ondas cuadradas a frecuencias de ~490Hz (o
980Hz en D5/D6) para simular salidas analógicas.
• Analog IN A0 - A5: Conectados al Conversor Analógico-Digital (ADC). Convierten un rango de
voltaje continuo en un valor entero entre 0 y 1023.
• Power 5V, 3.3V, GND, VIN: Líneas de alimentación para sensores y periféricos externos.
¡Atención de Ingeniería! Exceder los 40 mA por pin GPIO o 200 mA totales entre todos los pines destruirá
permanentemente el microcontrolador. Utiliza siempre resistencias limitadoras de corriente para LEDs (mínimo
220Ω) y transistores para cargas inductivas (motores, relés).
Tutorial de Arduino - Guía de Desarrollo Página 3 de 19
MÓDULO 2: FUNDAMENTOS DE PROGRAMACIÓN
C/C++
El lenguaje de Arduino es esencialmente C/C++ estructurado con una biblioteca base que oculta la
complejidad del acceso directo a los registros del microcontrolador.
2.1 Estructura Mínima de un Sketch
Todo programa en Arduino requiere obligatoriamente de dos funciones principales:
void setup() {
// Se ejecuta UNA SOLA VEZ al energizar o reiniciar la placa.
// Ideal para configuración de pines, inicialización de librerías y serie.
}
void loop() {
// Se ejecuta en un BUCLE INFINITO secuencial tras finalizar setup().
// Contiene la lógica principal de control del sistema.
}
2.2 Tipos de Datos y Optimización de Memoria
Debido a la limitación de 2 KB de SRAM en el ATmega328P, elegir el tipo de dato adecuado es fundamental
para prevenir desbordamientos de memoria (Stack/Heap Overflow):
Tamaño
Tipo de Dato Rango de Valores Uso Recomendado
(Bytes)
bool / boolean 1 Byte true (1) o false (0) Banderas lógicas y estados de pines.
Lecturas de 8 bits, números de pines,
byte / uint8_t 1 Byte 0 a 255
contadores pequeños.
Contadores, mediciones genéricas,
int 2 Bytes -32,768 a 32,767
variables enteras.
Valores analógicos ADC, contadores
unsigned int 2 Bytes 0 a 65,535
positivos.
-2,147,483,648 a Matemáticas de gran precisión, tiempo
long 4 Bytes
2,147,483,647 acumulado.
Variables de tiempo con millis() y
unsigned long 4 Bytes 0 a 4,294,967,295
micros() .
Tutorial de Arduino - Guía de Desarrollo Página 4 de 19
Tamaño
Tipo de Dato Rango de Valores Uso Recomendado
(Bytes)
-3.4028235E+38 a Cálculos científicos y sensores con
float 4 Bytes
3.4028235E+38 decimales (precisión 6 dígitos).
Consejo Senior: Macro F() y Memoria FLASH
Las cadenas de texto impresas por el puerto serie como [Link]("Error del sistema")
consumen la valiosa memoria SRAM. Utiliza la macro F() para forzar la lectura directamente desde la memoria
FLASH:
[Link](F("Texto guardado solo en Flash"));
Tutorial de Arduino - Guía de Desarrollo Página 5 de 19
MÓDULO 3: ENTRADAS Y SALIDAS DIGITALES
(GPIO)
El control de entradas y salidas digitales permite interactuar con pulsadores, interruptores, relés, indicadores
LED y sensores discretos.
3.1 Funciones Nativas GPIO
• pinMode(pin, modo) : Configura el pin objetivo como INPUT , OUTPUT o INPUT_PULLUP .
• digitalWrite(pin, valor) : Establece el estado lógico en HIGH (5V) o LOW (0V).
• digitalRead(pin) : Lee el estado eléctrico del pin, devolviendo HIGH o LOW .
3.2 Resistencias Pull-Up y Pull-Down
Cuando un pin configurado como entrada no está conectado a nada (estado flotante), captará ruido
electromagnético ambiental, provocando lecturas erráticas. Para solucionar esto se emplean resistencias
físicas de referenciación o la resistencia interna del microcontrolador.
Uso del Pull-Up Interno
Arduino incluye resistencias Pull-Up internas de 20kΩ direccionables por software. Esto elimina la
necesidad de colocar resistencias externas al conectar pulsadores a GND.
3.3 Proyecto Práctico: Control de LED mediante Pulsador con Anti-Rebote
(Debounce)
Los pulsadores mecánicos generan un "ruido de rebote" (oscilaciones rápidas) durante los primeros
milisegundos de conmutación. Este código elimina el problema mediante software:
// Definición de pines y constantes
const byte PIN_BTN = 2;
const byte PIN_LED = 13;
const unsigned long TIEMPO_DEBOUNCE = 50; // ms
// Variables de estado
bool estadoLed = LOW;
bool ultimoEstadoBoton = HIGH;
unsigned long ultimoTiempoConmutacion = 0;
void setup() {
pinMode(PIN_BTN, INPUT_PULLUP); // Activa Pull-Up interna (Pulsado = LOW)
pinMode(PIN_LED, OUTPUT);
Tutorial de Arduino - Guía de Desarrollo Página 6 de 19
digitalWrite(PIN_LED, estadoLed);
}
void loop() {
bool lecturaActual = digitalRead(PIN_BTN);
if (lecturaActual != ultimoEstadoBoton) {
ultimoTiempoConmutacion = millis();
}
if ((millis() - ultimoTiempoConmutacion) > TIEMPO_DEBOUNCE) {
// Confirmamos cambio de estado estable
static bool estadoConfirmadoBoton = HIGH;
if (lecturaActual != estadoConfirmadoBoton) {
estadoConfirmadoBoton = lecturaActual;
if (estadoConfirmadoBoton == LOW) { // Transición a presionado
estadoLed = !estadoLed;
digitalWrite(PIN_LED, estadoLed);
}
}
}
ultimoEstadoBoton = lecturaActual;
}
Tutorial de Arduino - Guía de Desarrollo Página 7 de 19
MÓDULO 4: SEÑALES ANALÓGICAS Y PWM
Los microcontroladores digitales no pueden emitir un voltaje analógico variable de forma directa. Sin
embargo, pueden leer valores analógicos variables usando su ADC y simular salidas variables usando PWM.
4.1 Entrada Analógica (ADC - Analog to Digital Converter)
El conversor analógico a digital del ATmega328P posee una resolución de 10 bits. Esto significa que
transforma una señal de entrada de 0V a 5V en un entero proporcional comprendido entre 0 y 1023 (2^10
= 1024 niveles).
Ecuación de Conversión de Voltaje
Voltaje_Medido = ( Lectura_ADC × V_REF ) / 1023.0
Por defecto V_REF = 5.0V . Se puede modificar utilizando la función analogReference(EXTERNAL)
introduciendo un voltaje de referencia exacto por el pin AREF .
4.2 Salida Analógica Regulada (PWM - Pulse Width Modulation)
El PWM varía la cantidad de energía entregada modulando el ciclo de trabajo (Duty Cycle) de una señal
cuadrada constante. Un ciclo del 0% equivale a 0V continuos; 50% equivale a 2.5V promedio; y 100%
entrega 5V continuos.
La función nativa es analogWrite(pin, valor) , donde el argumento valor oscila entre 0 (0% duty) y
255 (100% duty) debido al timer de 8 bits.
4.3 Proyecto Práctico: Control de Brillo LED por Potenciómetro
const byte PIN_POTENCIOMETRO = A0; // Pin de entrada ADC
const byte PIN_LED_PWM = 9; // Pin PWM ~D9
void setup() {
pinMode(PIN_LED_PWM, OUTPUT);
[Link](115200); // Inicializa puerto serie a 115200 baudios
}
void loop() {
int lecturaRAW = analogRead(PIN_POTENCIOMETRO); // 0 a 1023
// Mapeo lineal de escala 10-bits a escala PWM de 8-bits
int valorPWM = map(lecturaRAW, 0, 1023, 0, 255);
analogWrite(PIN_LED_PWM, valorPWM);
Tutorial de Arduino - Guía de Desarrollo Página 8 de 19
// Monitoreo por consola
float voltaje = (lecturaRAW * 5.0) / 1023.0;
[Link](F("ADC: "));
[Link](lecturaRAW);
[Link](F(" | Voltaje: "));
[Link](voltaje, 2);
[Link](F("V | PWM: "));
[Link](valorPWM);
delay(50);
}
Tutorial de Arduino - Guía de Desarrollo Página 9 de 19
MÓDULO 5: COMUNICACIÓN SERIE Y BUS DE
DATOS
La transferencia de datos entre el microcontrolador y sensores avanzados, pantallas u otras placas requiere
de protocolos de comunicación estandarizados.
5.1 UART (Universal Asynchronous Receiver-Transmitter)
Es la comunicación serie por defecto de Arduino. Es asíncrona (no requiere línea de reloj compartida) y utiliza
dos líneas dedicadas: TX (Transmitir) y RX (Recibir). Las velocidades estándar se miden en Baudios (bits
por segundo), siendo habituales 9600 y 115200.
5.2 Comparativa de Protocolos: I2C vs SPI vs UART
SPI (Serial Peripheral
Característica UART / Serial I2C (Inter-Integrated Circuit)
Interface)
2 (SDA, SCL) + Resistencias
Líneas / Pines 2 (TX, RX) 4 (MISO, MOSI, SCK, SS/CS)
Pull-Up
Muy Rápida (Hasta >10
Velocidad Lenta (~115.2 kbps) Media (100 kbps - 400 kbps)
Mbps)
Punto a Punto (2 Bus Multimaestro/Multiesclavo Bus Maestro / Múltiples
Topología
dispositivos) (por Direcciones) Esclavos (por línea SS)
Pines Arduino D11 (MOSI), D12 (MISO),
D0 (RX), D1 (TX) A4 (SDA), A5 (SCL)
Uno D13 (SCK)
Debugging PC, Display LCD 16x2, OLED, RTC Pantallas TFT, Tarjetas
Uso Típico
Bluetooth HC-05 DS3231, BME280 MicroSD, Lectores RFID
5.3 Ejemplo de Uso Bus I2C con Librería Wire
#include <Wire.h>
void setup() {
[Link](); // Inicializa como Maestro I2C
[Link](9600);
[Link](F("Escaneando Bus I2C..."));
for (byte address = 1; address < 127; address++) {
[Link](address);
byte error = [Link]();
Tutorial de Arduino - Guía de Desarrollo Página 10 de 19
if (error == 0) {
[Link](F("Dispositivo I2C detectado en direccion 0x"));
if (address < 16) [Link]("0");
[Link](address, HEX);
}
}
}
Tutorial de Arduino - Guía de Desarrollo Página 11 de 19
MÓDULO 6: GESTIÓN DEL TIEMPO Y MULTITAREA
CONCURRENTE
El uso de la función delay(ms) bloquea por completo la ejecución del procesador durante el tiempo
indicado. En aplicaciones profesionales de ingeniería, el uso de código bloqueante está prohibido ya que
impide responder a eventos en tiempo real.
6.1 El Concepto de millis()
La función millis() devuelve el número de milisegundos transcurridos desde que la placa Arduino
encendió el programa actual. Dado que el contador se almacena en un entero sin signo de 32 bits
( unsigned long ), la función se desborda y vuelve a cero aproximadamente cada 49.7 días.
Estructura No Bloqueante (Máquina de Estados Temporal)
Al calcular la diferencia de tiempo transcurrido millis() - tiempoAnterior >= intervalo ,
prevenimos errores incluso cuando el contador principal se desborda a cero.
6.2 Código Comparativo: Multitarea Simultánea
A continuación se presenta un programa que ejecuta dos tareas totalmente independientes al mismo tiempo:
parpadear un LED cada 500ms y leer un sensor cada 2000ms sin pausar el hilo de ejecución.
const byte PIN_LED = 13;
// Tareas con intervalos independientes
const unsigned long INTERVALO_LED = 500;
const unsigned long INTERVALO_SENSOR = 2000;
unsigned long ultimoTiempoLED = 0;
unsigned long ultimoTiempoSensor = 0;
bool estadoLed = LOW;
void setup() {
pinMode(PIN_LED, OUTPUT);
[Link](9600);
}
void loop() {
unsigned long tiempoActual = millis();
// Tarea 1: Control y Parpadeo de LED
if (tiempoActual - ultimoTiempoLED >= INTERVALO_LED) {
Tutorial de Arduino - Guía de Desarrollo Página 12 de 19
ultimoTiempoLED = tiempoActual;
estadoLed = !estadoLed;
digitalWrite(PIN_LED, estadoLed);
}
// Tarea 2: Lectura periódica simulada del sensor
if (tiempoActual - ultimoTiempoSensor >= INTERVALO_SENSOR) {
ultimoTiempoSensor = tiempoActual;
int valorRaw = analogRead(A0);
[Link](F("[LOG Tarea 2] Muestra Sensor A0: "));
[Link](valorRaw);
}
// El procesador permanece libre para procesar otras tareas críticas aquí
}
Tutorial de Arduino - Guía de Desarrollo Página 13 de 19
MÓDULO 7: INTERRUPCIONES DE HARDWARE Y
TIMERS
Las Interrupciones detienen temporalmente la ejecución habitual del programa principal cuando ocurre un
evento eléctrico crítico en un pin determinado, ejecutan una función especial denominada ISR (Interrupt
Service Routine), y reanudan el programa exactamente en el punto donde fue pausado.
7.1 Ventajas y Reglas de Oro de las ISR
• Velocidad Extrema: Responden en microsegundos, ideal para codificadores (encoders), detectores de
paso por cero o paradas de emergencia.
• Ejecución Atómica e Ininterrumpida: Dentro de una ISR, el resto de interrupciones están
deshabilitadas.
• Regla 1: La ISR debe ser lo más corta y rápida posible. NUNCA usar delay() , millis() ni llamadas
extensas a [Link]() .
• Regla 2: Toda variable modificada dentro de una ISR y leída fuera de ella DEBE declararse
obligatoriamente con el calificador volatile .
7.2 Pines de Interrupción Externa en Arduino Uno
Arduino Uno soporta 2 pines de interrupción dedicada de hardware:
• INT0 : Mapeado al pin Digital 2.
• INT1 : Mapeado al pin Digital 3.
7.3 Modos de Disparo de Interrupción
Modo (Trigger) Condición de Activación
LOW Se dispara continuamente mientras el pin permanezca en nivel bajo (0V).
CHANGE Se dispara en cualquier cambio de estado (transición de HIGH a LOW o LOW a HIGH).
RISING Se dispara en el flanco de subida (transición estricta de LOW a HIGH).
FALLING Se dispara en el flanco de bajada (transición estricta de HIGH a LOW).
7.4 Ejemplo Práctico: Contador de Pulsos de Alta Velocidad por Hardware
const byte PIN_INTERRUPT = 2;
volatile unsigned long contadorPulsos = 0; // Uso estricto de volatile
Tutorial de Arduino - Guía de Desarrollo Página 14 de 19
// Rutina de Servicio de Interrupción (ISR)
void AtenderPulso() {
contadorPulsos++; // Operación ultra rápida de incremento
}
void setup() {
[Link](115200);
pinMode(PIN_INTERRUPT, INPUT_PULLUP);
// Vincula la función AtenderPulso al Flanco de Bajada en el Pin 2
attachInterrupt(digitalPinToInterrupt(PIN_INTERRUPT), AtenderPulso, FALLING);
}
void loop() {
// Imprime el conteo cada segundo sin bloquear el cálculo de impulsos
delay(1000);
noInterrupts(); // Bloqueo temporal para lectura atómica segura de la variable
unsigned long copiaContador = contadorPulsos;
interrupts(); // Reabre interrupciones
[Link](F("Pulsos detectados: "));
[Link](copiaContador);
}
Tutorial de Arduino - Guía de Desarrollo Página 15 de 19
MÓDULO 8: INTEGRACIÓN DE MOTORES Y
ACTUADORES
Los pines de microcontroladores no disponen de potencia para mover motores directamente. Se requieren
drivers o etapas de potencia (puentes H, MOSFETs, Relés).
8.1 Control de Motores DC mediante Driver Puente H (L298N / TB6612FNG)
Un puente H permite controlar la dirección del flujo de corriente a través de las bobinas de un motor,
permitiendo invertir su sentido de giro y ajustar su velocidad vía PWM.
IN1 IN2 Habilitador (ENA - PWM) Estado del Motor
LOW LOW Cualquiera Freno Pasivo (Detenido)
HIGH LOW PWM (0-255) Giro Sentido Horario (Velocidad Regulada)
LOW HIGH PWM (0-255) Giro Sentido Antihorario (Velocidad Regulada)
HIGH HIGH Cualquiera Freno Activo Reconstructivo
8.2 Servomotores de Precisión
Los servomotores utilizan una señal PWM específica (período de 20ms con pulsos activos de 1ms a 2ms)
para posicionar su eje en un ángulo exacto entre 0° y 180°.
#include <Servo.h>
Servo miServo; // Creación del objeto Servo
const byte PIN_SERVO = 9;
void setup() {
[Link](PIN_SERVO); // Vincula el servo al pin 9
}
void loop() {
// Barrido angular continuo de 0 a 180 grados
for (int angulo = 0; angulo <= 180; angulo += 10) {
[Link](angulo);
delay(150);
}
for (int angulo = 180; angulo >= 0; angulo -= 10) {
[Link](angulo);
delay(150);
Tutorial de Arduino - Guía de Desarrollo Página 16 de 19
}
}
Tutorial de Arduino - Guía de Desarrollo Página 17 de 19
MÓDULO 9: BUENAS PRÁCTICAS DE INGENIERÍA
DE SOFTWARE
9.1 Arquitectura de Código Limpio para Sistemas Embebidos
• Uso estricto de const o constexpr : Reemplaza directivas #define viejas para el número de
pines para obtener validación de tipo en compilación.
• Modularización en Archivos (.h / .cpp): Separa controladores de hardware e interfaces de usuario
en archivos fuente independientes.
• Evitar Reservas Dinámicas de Memoria: No utilices el operador new , malloc() o el tipo de
objeto String . Favorece el uso de arrays de caracteres C estáticos ( char array[] ) para evitar la
fragmentación irreversible de la RAM.
MÓDULO 10: PROYECTO INTEGRADOR AVANZADO
Estación de Monitoreo Automatizada con Alarmas y Control Térmico: Integración de entradas
analógicas, actuadores, temporización millis() y comunicación serie.
#include <Wire.h>
// Configuración Hardware
const byte PIN_NTC = A0;
const byte PIN_VENTILADOR = 3; // Salida PWM
const byte PIN_ALARMA = 5;
// Umbrales Operativos
const float TEMP_UMBRAL_ACTIVACION = 30.0; // °C
const unsigned long CADENCIA_MUESTREO = 1000; // 1 Segundo
unsigned long ultimoMuestreo = 0;
float LeerTemperaturaCelsius() {
int raw = analogRead(PIN_NTC);
// Simulación de conversión NTC Steinhart-Hart / Escala Linealizada
return (raw * 5.0 / 1023.0) * 20.0;
}
void setup() {
pinMode(PIN_VENTILADOR, OUTPUT);
pinMode(PIN_ALARMA, OUTPUT);
[Link](115200);
[Link](F("=== Sistema de Control Térmico Inicializado ==="));
Tutorial de Arduino - Guía de Desarrollo Página 18 de 19
}
void loop() {
unsigned long ahora = millis();
if (ahora - ultimoMuestreo >= CADENCIA_MUESTREO) {
ultimoMuestreo = ahora;
float temp = LeerTemperaturaCelsius();
[Link](F("Temperatura Actual: "));
[Link](temp);
[Link](F(" °C"));
if (temp >= TEMP_UMBRAL_ACTIVACION) {
analogWrite(PIN_VENTILADOR, 255); // Ventilador 100%
digitalWrite(PIN_ALARMA, HIGH);
[Link](F("[ALERTA] Umbral superado. Activando refrigeración."));
} else {
analogWrite(PIN_VENTILADOR, 0);
digitalWrite(PIN_ALARMA, LOW);
}
}
}
Tutorial de Arduino - Guía de Desarrollo Página 19 de 19