Introduction
Les protocoles de communication sont le pilier des systèmes embarqués. Que vous lisiez un capteur, dialoguiez avec une carte SD ou imprimiez des messages de débogage, vous utilisez l’un des trois protocoles majeurs : Serial (UART), I2C (TWI) ou SPI. Dans ce tutoriel, nous allons exécuter les trois simultanément sur un Arduino Uno en utilisant le simulateur en ligne Velxio. Vous verrez comment balayer le bus I2C, lire/écrire une EEPROM, effectuer des transferts SPI et journaliser le tout via Serial.

Présentation du circuit
Cet exemple utilise uniquement la carte Arduino Uno elle-même — aucun composant externe n’est nécessaire. Les périphériques intégrés de l’Arduino fournissent tout ce dont nous avons besoin :
- Serial (UART) : Utilise les broches 0 (RX) et 1 (TX) pour la communication avec le moniteur série.
- I2C (TWI) : Utilise les broches A4 (SDA) et A5 (SCL). Les résistances de tirage internes sont activées par la bibliothèque Wire.
- SPI : Utilise les broches 10 (SS), 11 (MOSI), 12 (MISO) et 13 (SCK). La broche 10 est configurée comme sortie de sélection esclave.
Aucun câblage n’est nécessaire car toutes les connexions sont internes à l’Arduino. Le canevas Velxio affiche l’Uno avec ses étiquettes de broches, et vous pouvez inspecter les composants virtuels en ouvrant le sélecteur de composants.

Explication du code
Décortiquons le sketch étape par étape.
Bibliothèques et définitions
#include <Wire.h>
#include <SPI.h>
#define DS1307_ADDR 0x68
#define EEPROM_ADDR 0x50
#define SS_PIN 10
Nous incluons la bibliothèque Wire pour I2C et la bibliothèque SPI pour SPI. Deux adresses I2C sont définies : 0x68 pour un RTC DS1307 (bien que non présent physiquement, nous allons le rechercher) et 0x50 pour une EEPROM 24Cxx. La broche de sélection esclave SPI est définie sur la broche numérique 10.
Fonctions auxiliaires
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 fonction bcdToDec convertit le décimal codé en binaire (BCD) en décimal. readRTC lit les secondes, minutes et heures d’un RTC DS1307. Les masques & 0x7F et & 0x3F effacent les bits inutilisés.
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;
}
Ces fonctions écrivent et lisent un seul octet depuis une EEPROM I2C à l’adresse 0x50. Le delay(5) garantit que l’EEPROM a le temps de terminer le cycle d’écriture.
byte spiTransfer(byte data) {
digitalWrite(SS_PIN, LOW);
byte result = SPI.transfer(data);
digitalWrite(SS_PIN, HIGH);
return result;
}
Cette fonction effectue un transfert SPI full-duplex : elle abaisse SS, envoie un octet, reçoit la réponse et remonte SS.
Setup
void setup() {
Serial.begin(9600);
Wire.begin();
pinMode(SS_PIN, OUTPUT);
digitalWrite(SS_PIN, HIGH);
SPI.begin();
Serial.println("===================================");
Serial.println(" Démo Multi-Protocole");
Serial.println(" Serial (USART) + I2C (TWI) + SPI");
Serial.println("===================================");
Serial.println();
// ── I2C : Balayage du bus ──
Serial.println("[I2C] Balayage du bus...");
int found = 0;
for (byte addr = 1; addr < 127; addr++) {
Wire.beginTransmission(addr);
if (Wire.endTransmission() == 0) {
Serial.print(" Périphérique trouvé à 0x");
if (addr < 16) Serial.print("0");
Serial.println(addr, HEX);
found++;
}
}
Serial.print(" ");
Serial.print(found);
Serial.println(" périphérique(s) sur le bus I2C.");
Serial.println();
// ── I2C : Écriture/lecture EEPROM ──
Serial.println("[I2C] Test d'écriture/lecture EEPROM :");
writeEEPROM(0, 42);
writeEEPROM(1, 99);
byte v0 = readEEPROM(0);
byte v1 = readEEPROM(1);
Serial.print(" Écrit 42, lu ");
Serial.print(v0);
Serial.println(v0 == 42 ? " [OK]" : " [ÉCHEC]");
Serial.print(" Écrit 99, lu ");
Serial.print(v1);
Serial.println(v1 == 99 ? " [OK]" : " [ÉCHEC]");
Serial.println();
// ── SPI : Test de transfert ──
Serial.println("[SPI] Test de transfert :");
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("Configuration terminée. Lecture du RTC...");
Serial.println();
}
Dans setup(), nous initialisons les trois protocoles. Ensuite nous :
- Balayons le bus I2C pour les périphériques des adresses 1 à 126.
- Testons l’EEPROM en écrivant les valeurs 42 et 99 aux adresses 0 et 1, puis en les relisant et en vérifiant.
- Testons le SPI en envoyant trois octets (
0xAA,0x55,0x42) et en affichant les octets reçus.
Loop
void loop() {
// ── Serial : Affichage de l'heure RTC toutes les 2 secondes ──
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(" | Temps de fonctionnement : ");
Serial.print(millis() / 1000);
Serial.println("s");
delay(2000);
}
Toutes les deux secondes, la boucle lit l’heure RTC et l’affiche avec le temps de fonctionnement du système. Cela démontre une sortie Serial continue pendant que I2C et SPI sont inactifs.
Concepts clés
- Serial (UART) : Asynchrone, point à point. Utilisé pour la sortie de débogage et la communication avec des PC ou des modules comme le GPS.
- I2C : Synchrone, multi-maître, utilise deux fils (SDA, SCL). Chaque périphérique a une adresse unique. Idéal pour les capteurs et les EEPROM.
- SPI : Synchrone, full-duplex, utilise quatre fils (MOSI, MISO, SCK, SS). Plus rapide que I2C mais nécessite plus de broches.
- Balayage de bus : Parcourir les adresses I2C pour découvrir les périphériques connectés.
- Lecture/écriture EEPROM : Mémoire non volatile qui conserve les données après une coupure de courant.
- Transfert SPI : Envoi et réception simultanés de données.
Pièges courants et conseils de débogage
- Résistances de tirage I2C : La bibliothèque Wire active les résistances de tirage internes, mais pour des bus plus longs, des résistances externes (4,7 kΩ) sont recommandées.
- Broche SS SPI : Toujours configurer SS comme sortie et la maintenir haute lorsqu’elle n’est pas utilisée pour éviter d’entrer accidentellement en mode esclave.
- Temporisation d’écriture EEPROM : Après une écriture, attendre au moins 5 ms avant l’opération suivante.
- Débit en bauds du moniteur série : Assurez-vous que le moniteur série est réglé sur 9600 bauds.
- Absence de périphériques externes : Dans cette simulation, le balayage I2C ne trouvera aucun périphérique (sauf peut-être le TWI interne de l’Arduino). Le test EEPROM utilise une EEPROM virtuelle intégrée au simulateur.
Extensions suggérées
- Ajouter de vrais capteurs : Connectez un RTC DS1307 et une EEPROM 24Cxx au bus I2C, et un module carte SD via SPI.
- Journaliser des données dans l’EEPROM : Stockez les lectures des capteurs et récupérez-les après une réinitialisation.
- Utiliser des interruptions : Déclenchez un transfert SPI sur une pression de bouton.
- Implémenter une interface de commandes : Analysez les commandes Serial pour contrôler les opérations I2C et SPI.
Essayez par vous-même
Prêt à voir les trois protocoles en action ? Ouvrez l’exemple en direct dans Velxio, compilez et exécutez. Observez la sortie du moniteur série pendant que l’Arduino balaie le bus I2C, teste l’EEPROM, effectue des transferts SPI, puis affiche en continu l’heure RTC.
Lancer la démo Multi-Protocole sur Velxio

Bon bricolage !