Introducción
Los protocolos de comunicación son la base de los sistemas embebidos. Ya sea que estés leyendo un sensor, comunicándote con una tarjeta SD o imprimiendo mensajes de depuración, estás utilizando uno de los tres protocolos principales: Serial (UART), I2C (TWI) o SPI. En este tutorial, ejecutaremos los tres simultáneamente en un Arduino Uno usando el simulador en navegador Velxio. Verás cómo escanear el bus I2C, leer/escribir una EEPROM, realizar transferencias SPI y registrar todo a través de Serial.

Recorrido del Circuito
Este ejemplo utiliza solo la placa Arduino Uno en sí misma — no se requieren componentes externos. Los periféricos integrados del Arduino proporcionan todo lo necesario:
- Serial (UART): Utiliza los pines 0 (RX) y 1 (TX) para la comunicación con el Monitor Serial.
- I2C (TWI): Utiliza los pines A4 (SDA) y A5 (SCL). Las resistencias de pull‑up internas son habilitadas por la librería Wire.
- SPI: Utiliza los pines 10 (SS), 11 (MOSI), 12 (MISO) y 13 (SCK). El pin 10 está configurado como salida de selección de esclavo.
No es necesario cableado porque todas las conexiones son internas al Arduino. El lienzo de Velxio muestra el Uno con sus etiquetas de pines, y puedes inspeccionar los componentes virtuales abriendo el selector de componentes.

Explicación del Código
Analicemos el sketch paso a paso.
Librerías y Definiciones
#include <Wire.h>
#include <SPI.h>
#define DS1307_ADDR 0x68
#define EEPROM_ADDR 0x50
#define SS_PIN 10
Incluimos la librería Wire para I2C y la librería SPI para SPI. Se definen dos direcciones I2C: 0x68 para un RTC DS1307 (aunque no está físicamente presente, lo escanearemos) y 0x50 para una EEPROM 24Cxx. El pin de selección de esclavo SPI se establece en el pin digital 10.
Funciones Auxiliares
byte bcdToDec(byte val) {
return ((val >> 4) * 10) + (val & 0x0F);
}
void readRTC(byte &hr, byte &min, byte &sec) {
Wire.beginTransmission(DS1307_ADDR);
Wire.write(0x00);
Wire.endTransmission();
Wire.requestFrom(DS1307_ADDR, 3);
sec = bcdToDec(Wire.read() & 0x7F);
min = bcdToDec(Wire.read());
hr = bcdToDec(Wire.read() & 0x3F);
}
La función bcdToDec convierte Decimal Codificado en Binario a decimal. readRTC lee los segundos, minutos y horas de un RTC DS1307. Las máscaras & 0x7F y & 0x3F limpian los bits no utilizados.
void writeEEPROM(byte reg, byte value) {
Wire.beginTransmission(EEPROM_ADDR);
Wire.write(reg);
Wire.write(value);
Wire.endTransmission();
delay(5);
}
byte readEEPROM(byte reg) {
Wire.beginTransmission(EEPROM_ADDR);
Wire.write(reg);
Wire.endTransmission();
Wire.requestFrom(EEPROM_ADDR, 1);
return Wire.available() ? Wire.read() : 0xFF;
}
Estas funciones escriben y leen un solo byte de una EEPROM I2C en la dirección 0x50. El delay(5) asegura que la EEPROM tenga tiempo para completar el ciclo de escritura.
byte spiTransfer(byte data) {
digitalWrite(SS_PIN, LOW);
byte result = SPI.transfer(data);
digitalWrite(SS_PIN, HIGH);
return result;
}
Esta función realiza una transferencia SPI full‑dúplex: pone SS en LOW, envía un byte, recibe la respuesta y pone SS en HIGH.
Setup
void setup() {
Serial.begin(9600);
Wire.begin();
pinMode(SS_PIN, OUTPUT);
digitalWrite(SS_PIN, HIGH);
SPI.begin();
Serial.println("===================================");
Serial.println(" Multi-Protocol Demo");
Serial.println(" Serial (USART) + I2C (TWI) + SPI");
Serial.println("===================================");
Serial.println();
// ── I2C: Scan bus ──
Serial.println("[I2C] Scanning bus...");
int found = 0;
for (byte addr = 1; addr < 127; addr++) {
Wire.beginTransmission(addr);
if (Wire.endTransmission() == 0) {
Serial.print(" Found device at 0x");
if (addr < 16) Serial.print("0");
Serial.println(addr, HEX);
found++;
}
}
Serial.print(" ");
Serial.print(found);
Serial.println(" device(s) on I2C bus.");
Serial.println();
// ── I2C: Write/read EEPROM ──
Serial.println("[I2C] EEPROM write/read test:");
writeEEPROM(0, 42);
writeEEPROM(1, 99);
byte v0 = readEEPROM(0);
byte v1 = readEEPROM(1);
Serial.print(" Wrote 42, read ");
Serial.print(v0);
Serial.println(v0 == 42 ? " [OK]" : " [FAIL]");
Serial.print(" Wrote 99, read ");
Serial.print(v1);
Serial.println(v1 == 99 ? " [OK]" : " [FAIL]");
Serial.println();
// ── SPI: Transfer test ──
Serial.println("[SPI] Transfer test:");
byte spiData[] = {0xAA, 0x55, 0x42};
for (int i = 0; i < 3; i++) {
byte rx = spiTransfer(spiData[i]);
Serial.print(" TX=0x");
if (spiData[i] < 16) Serial.print("0");
Serial.print(spiData[i], HEX);
Serial.print(" RX=0x");
if (rx < 16) Serial.print("0");
Serial.println(rx, HEX);
}
Serial.println();
Serial.println("Setup complete. Reading RTC...");
Serial.println();
}
En setup(), inicializamos los tres protocolos. Luego:
- Escaneamos el bus I2C en busca de dispositivos desde la dirección 1 hasta 126.
- Probamos la EEPROM escribiendo los valores 42 y 99 en las direcciones 0 y 1, luego los leemos y verificamos.
- Probamos SPI enviando tres bytes (
0xAA,0x55,0x42) e imprimiendo los bytes recibidos.
Loop
void loop() {
// ── Serial: Print RTC time every 2 seconds ──
byte hr, min, sec;
readRTC(hr, min, sec);
Serial.print("[RTC] ");
if (hr < 10) Serial.print("0");
Serial.print(hr);
Serial.print(":");
if (min < 10) Serial.print("0");
Serial.print(min);
Serial.print(":");
if (sec < 10) Serial.print("0");
Serial.print(sec);
Serial.print(" | Uptime: ");
Serial.print(millis() / 1000);
Serial.println("s");
delay(2000);
}
Cada dos segundos, el bucle lee la hora del RTC y la imprime junto con el tiempo de actividad del sistema. Esto demuestra la salida Serial continua mientras I2C y SPI están inactivos.
Conceptos Clave
- Serial (UART): Asíncrono, punto a punto. Se utiliza para salida de depuración y comunicación con PCs o módulos como GPS.
- I2C: Síncrono, multi‑maestro, utiliza dos cables (SDA, SCL). Cada dispositivo tiene una dirección única. Ideal para sensores y EEPROMs.
- SPI: Síncrono, full‑dúplex, utiliza cuatro cables (MOSI, MISO, SCK, SS). Más rápido que I2C pero requiere más pines.
- Escaneo de Bus: Iterar a través de direcciones I2C para descubrir dispositivos conectados.
- Lectura/Escritura de EEPROM: Memoria no volátil que retiene datos después de una pérdida de energía.
- Transferencia SPI: Envío y recepción simultánea de datos.
Errores Comunes y Consejos de Depuración
- Resistencias Pull‑up I2C: La librería Wire habilita pull‑ups internos, pero para buses más largos se recomiendan resistencias externas (4.7kΩ).
- Pin SS de SPI: Siempre configura SS como salida y mantenlo en HIGH cuando no esté en uso para evitar entrar accidentalmente en modo esclavo.
- Tiempo de Escritura de EEPROM: Después de una escritura, espera al menos 5 ms antes de la siguiente operación.
- Velocidad de Baudios del Monitor Serial: Asegúrate de que el Monitor Serial esté configurado a 9600 baudios.
- Sin Dispositivos Externos: En esta simulación, el escaneo I2C no encontrará dispositivos (excepto posiblemente el TWI interno del Arduino). La prueba de EEPROM utiliza una EEPROM virtual integrada en el simulador.
Extensiones Sugeridas
- Añadir Sensores Reales: Conecta un RTC DS1307 y una EEPROM 24Cxx al bus I2C, y un módulo de tarjeta SD vía SPI.
- Registrar Datos en la EEPROM: Almacena lecturas de sensores y recupéralas después de un reinicio.
- Usar Interrupciones: Activa una transferencia SPI con la pulsación de un botón.
- Implementar una Interfaz de Comandos: Analiza comandos Serial para controlar operaciones I2C y SPI.
Pruébalo Tú Mismo
¿Listo para ver los tres protocolos en acción? Abre el ejemplo en vivo en Velxio, compila y ejecuta. Observa la salida del Monitor Serial mientras el Arduino escanea el bus I2C, prueba la EEPROM, realiza transferencias SPI y luego imprime continuamente la hora del RTC.
Iniciar la Demo Multi-Protocolo en Velxio

¡Feliz hacking!