Introdução
Protocolos de comunicação são a espinha dorsal dos sistemas embarcados. Seja lendo um sensor, comunicando-se com um cartão SD ou imprimindo mensagens de depuração, você está usando um dos três principais protocolos: Serial (UART), I2C (TWI) ou SPI. Neste tutorial, executaremos todos os três simultaneamente em um Arduino Uno usando o simulador online Velxio. Você verá como escanear o barramento I2C, ler/gravar uma EEPROM, realizar transferências SPI e registrar tudo via Serial.

Explicação do Circuito
Este exemplo usa apenas a própria placa Arduino Uno — nenhum componente externo é necessário. Os periféricos integrados do Arduino fornecem tudo que precisamos:
- Serial (UART): Usa os pinos 0 (RX) e 1 (TX) para comunicação com o Monitor Serial.
- I2C (TWI): Usa os pinos A4 (SDA) e A5 (SCL). Os resistores de pull‑up internos são habilitados pela biblioteca Wire.
- SPI: Usa os pinos 10 (SS), 11 (MOSI), 12 (MISO) e 13 (SCK). O pino 10 é configurado como saída de seleção de escravo.
Nenhuma conexão é necessária porque todas as conexões são internas ao Arduino. A tela do Velxio mostra o Uno com seus rótulos de pino, e você pode inspecionar os componentes virtuais abrindo o seletor de componentes.

Explicação do Código
Vamos detalhar o sketch passo a passo.
Bibliotecas e Definições
#include <Wire.h>
#include <SPI.h>
#define DS1307_ADDR 0x68
#define EEPROM_ADDR 0x50
#define SS_PIN 10
Incluímos a biblioteca Wire para I2C e a biblioteca SPI para SPI. Dois endereços I2C são definidos: 0x68 para um RTC DS1307 (embora não esteja fisicamente presente, vamos escaneá-lo) e 0x50 para uma EEPROM 24Cxx. O pino de seleção de escravo SPI é configurado como pino digital 10.
Funções 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);
}
A função bcdToDec converte Decimal Codificado em Binário (BCD) para decimal. readRTC lê os segundos, minutos e horas de um RTC DS1307. As máscaras & 0x7F e & 0x3F limpam bits não 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;
}
Essas funções escrevem e leem um único byte de uma EEPROM I2C no endereço 0x50. O delay(5) garante que a EEPROM tenha tempo para completar o ciclo de gravação.
byte spiTransfer(byte data) {
digitalWrite(SS_PIN, LOW);
byte result = SPI.transfer(data);
digitalWrite(SS_PIN, HIGH);
return result;
}
Esta função realiza uma transferência SPI full‑duplex: ela coloca SS em nível baixo, envia um byte, recebe a resposta e coloca SS em nível alto.
Setup
void setup() {
Serial.begin(9600);
Wire.begin();
pinMode(SS_PIN, OUTPUT);
digitalWrite(SS_PIN, HIGH);
SPI.begin();
Serial.println("===================================");
Serial.println(" Demonstração Multi-Protocolo");
Serial.println(" Serial (USART) + I2C (TWI) + SPI");
Serial.println("===================================");
Serial.println();
// ── I2C: Escanear barramento ──
Serial.println("[I2C] Escaneando barramento...");
int found = 0;
for (byte addr = 1; addr < 127; addr++) {
Wire.beginTransmission(addr);
if (Wire.endTransmission() == 0) {
Serial.print(" Dispositivo encontrado em 0x");
if (addr < 16) Serial.print("0");
Serial.println(addr, HEX);
found++;
}
}
Serial.print(" ");
Serial.print(found);
Serial.println(" dispositivo(s) no barramento I2C.");
Serial.println();
// ── I2C: Gravar/ler EEPROM ──
Serial.println("[I2C] Teste de gravação/leitura de EEPROM:");
writeEEPROM(0, 42);
writeEEPROM(1, 99);
byte v0 = readEEPROM(0);
byte v1 = readEEPROM(1);
Serial.print(" Gravou 42, leu ");
Serial.print(v0);
Serial.println(v0 == 42 ? " [OK]" : " [FALHA]");
Serial.print(" Gravou 99, leu ");
Serial.print(v1);
Serial.println(v1 == 99 ? " [OK]" : " [FALHA]");
Serial.println();
// ── SPI: Teste de transferência ──
Serial.println("[SPI] Teste de transferência:");
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 concluído. Lendo RTC...");
Serial.println();
}
No setup(), inicializamos todos os três protocolos. Em seguida, fazemos:
- Escanear o barramento I2C em busca de dispositivos do endereço 1 ao 126.
- Testar a EEPROM gravando os valores 42 e 99 nos endereços 0 e 1, depois lendo-os de volta e verificando.
- Testar SPI enviando três bytes (
0xAA,0x55,0x42) e imprimindo os bytes recebidos.
Loop
void loop() {
// ── Serial: Imprimir hora do RTC a cada 2 segundos ──
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(" | Tempo de atividade: ");
Serial.print(millis() / 1000);
Serial.println("s");
delay(2000);
}
A cada dois segundos, o loop lê a hora do RTC e a imprime junto com o tempo de atividade do sistema. Isso demonstra a saída Serial contínua enquanto I2C e SPI estão ociosos.
Conceitos Principais
- Serial (UART): Assíncrona, ponto a ponto. Usada para saída de depuração e comunicação com PCs ou módulos como GPS.
- I2C: Síncrona, multi-mestre, usa dois fios (SDA, SCL). Cada dispositivo tem um endereço único. Ótimo para sensores e EEPROMs.
- SPI: Síncrona, full‑duplex, usa quatro fios (MOSI, MISO, SCK, SS). Mais rápida que I2C, mas requer mais pinos.
- Varredura de Barramento: Iterar pelos endereços I2C para descobrir dispositivos conectados.
- Leitura/Gravação de EEPROM: Memória não volátil que retém dados após perda de energia.
- Transferência SPI: Envio e recebimento simultâneos de dados.
Armadilhas Comuns e Dicas de Depuração
- Resistores de Pull‑up I2C: A biblioteca Wire habilita pull‑ups internos, mas para barramentos mais longos, resistores externos (4,7kΩ) são recomendados.
- Pino SS do SPI: Sempre configure SS como saída e mantenha-o em nível alto quando não estiver em uso para evitar entrar acidentalmente no modo escravo.
- Tempo de Gravação da EEPROM: Após uma gravação, aguarde pelo menos 5 ms antes da próxima operação.
- Taxa de Baud do Monitor Serial: Certifique-se de que o Monitor Serial esteja configurado para 9600 baud.
- Sem Dispositivos Externos: Nesta simulação, a varredura I2C não encontrará dispositivos (exceto possivelmente o TWI interno do Arduino). O teste de EEPROM usa uma EEPROM virtual embutida no simulador.
Extensões Sugeridas
- Adicionar Sensores Reais: Conecte um RTC DS1307 e uma EEPROM 24Cxx ao barramento I2C, e um módulo de cartão SD via SPI.
- Registrar Dados na EEPROM: Armazene leituras de sensores e recupere-as após um reset.
- Usar Interrupções: Acione uma transferência SPI ao pressionar um botão.
- Implementar uma Interface de Comandos: Analise comandos Serial para controlar operações I2C e SPI.
Experimente Você Mesmo
Pronto para ver todos os três protocolos em ação? Abra o exemplo ao vivo no Velxio, compile e execute. Observe a saída do Monitor Serial enquanto o Arduino escaneia o barramento I2C, testa a EEPROM, realiza transferências SPI e, em seguida, imprime continuamente a hora do RTC.
Iniciar a Demonstração Multi-Protocolo no Velxio

Feliz programação!