// I2C Slave is NOT implemented in Wokwi !!!
// ========== ESP32 - Double I2C : Master (émetteur) et Slave (récepteur) ==========
#include <Wire.h>
// Configuration I2C Master (TwoWire Wire = I2C0)
#define MASTER_SDA_PIN 21
#define MASTER_SCL_PIN 22
// Configuration I2C Slave (TwoWire Wire1 = I2C1)
#define SLAVE_SDA_PIN 18
#define SLAVE_SCL_PIN 19
#define SLAVE_ADDR 0x08
// Structure de 32 octets exactement
struct __attribute__((packed)) Message {
char text[16]; // 16 octets
int16_t value; // 2 octets
uint32_t timestamp; // 4 octets
uint8_t padding[10]; // 10 octets de padding
};
// Variables pour l'émetteur (Master)
Message msgEnvoi;
unsigned long compteurEnvoi = 0;
unsigned long dernierEnvoi = 0;
// Variables pour le récepteur (Slave)
Message msgRecu;
volatile bool nouveauMessage = false;
unsigned long nbMessagesRecus = 0;
// Créer le second bus I2C (Slave)
TwoWire I2C_Slave = TwoWire(1);
void setup() {
Serial.begin(115200);
while(!Serial) {}
Serial.println("OK, Let's go!");
Serial.println("========================================");
Serial.println("======== ESP32 - Dual I2C Test =========");
Serial.println("========================================");
Serial.print("Taille structure: ");
Serial.print(sizeof(Message));
Serial.println(" octets");
// Vérifie la taille
if (sizeof(Message) != 32) {
Serial.println("ERREUR: Structure != 32 octets!");
while(1);
}
Serial.println("\n--- Configuration I2C Master ---");
Serial.print("SDA: GPIO");
Serial.print(MASTER_SDA_PIN);
Serial.print(" | SCL: GPIO");
Serial.println(MASTER_SCL_PIN);
Serial.println("\n--- Configuration I2C Slave ---");
Serial.print("SDA: GPIO");
Serial.print(SLAVE_SDA_PIN);
Serial.print(" | SCL: GPIO");
Serial.print(SLAVE_SCL_PIN);
Serial.print(" | Adresse: 0x");
if(SLAVE_ADDR < 0x10) Serial.print('0');
Serial.println(SLAVE_ADDR, HEX);
// Initialise I2C Master (Wire = I2C0)
Wire.begin(MASTER_SDA_PIN, MASTER_SCL_PIN);
Wire.setClock(100000); // 100kHz
// Initialise I2C Slave (Wire1 = I2C1)
I2C_Slave.onReceive(receiveEvent);
I2C_Slave.begin(SLAVE_ADDR, SLAVE_SDA_PIN, SLAVE_SCL_PIN, 100000);
// Prépare le premier message
strncpy(msgEnvoi.text, "Hello I2C!", 16);
msgEnvoi.value = 0;
msgEnvoi.timestamp = 0;
memset(msgEnvoi.padding, 0xAA, 10);
Serial.println("\n========================================");
Serial.println("Systeme pret!");
Serial.println("Connectez GPIO21 -> GPIO18 (SDA)");
Serial.println("Connectez GPIO22 -> GPIO19 (SCL)");
Serial.println("========================================\n");
delay(1000);
}
void loop() {
// ENVOI : Toutes les 2 secondes via I2C Master
if (millis() - dernierEnvoi >= 2000) {
dernierEnvoi = millis();
// Prépare le message
msgEnvoi.timestamp = millis();
msgEnvoi.value = compteurEnvoi;
snprintf(msgEnvoi.text, 16, "Msg #%lu", compteurEnvoi);
Serial.println("┌─────────────── ENVOI ───────────────┐");
Serial.print("│ #");
Serial.print(compteurEnvoi);
Serial.print(" | Text: ");
Serial.print(msgEnvoi.text);
for(int i = strlen(msgEnvoi.text); i < 16; i++) Serial.print(" ");
Serial.println("│");
Serial.print("│ Value: ");
Serial.print(msgEnvoi.value);
Serial.print(" | Timestamp: ");
Serial.print(msgEnvoi.timestamp);
int spaces = 15 - String(msgEnvoi.value).length() - String(msgEnvoi.timestamp).length();
for(int i = 0; i < spaces; i++) Serial.print(" ");
Serial.println("│");
// Envoi via I2C Master
Wire.beginTransmission(SLAVE_ADDR);
Wire.write((uint8_t*)&msgEnvoi, sizeof(msgEnvoi));
uint8_t error = Wire.endTransmission();
Serial.print("│ Statut: ");
if (error == 0) {
Serial.print("OK ");
} else {
Serial.print("ERREUR ");
Serial.print(error);
Serial.print(" ");
}
Serial.println("│");
Serial.println("└─────────────────────────────────────┘\n");
compteurEnvoi++;
}
// RÉCEPTION : Traite les messages reçus via I2C Slave
if (nouveauMessage) {
nouveauMessage = false;
nbMessagesRecus++;
Serial.println("╔══════════════ RECEPTION ═════════════╗");
Serial.print("║ Message #");
Serial.print(nbMessagesRecus);
if (nbMessagesRecus < 10) Serial.print(" ");
Serial.println(" ║");
Serial.println("╟──────────────────────────────────────╢");
// Affiche le texte
Serial.print("║ Text: ");
char textTemp[17];
memcpy(textTemp, msgRecu.text, 16);
textTemp[16] = '\0';
Serial.print(textTemp);
for(int i = strlen(textTemp); i < 26; i++) Serial.print(" ");
Serial.println("║");
// Affiche value
Serial.print("║ Value: ");
Serial.print(msgRecu.value);
int spaces = 30 - String(msgRecu.value).length();
for(int i = 0; i < spaces; i++) Serial.print(" ");
Serial.println("║");
// Affiche timestamp
Serial.print("║ Timestamp: ");
Serial.print(msgRecu.timestamp);
spaces = 26 - String(msgRecu.timestamp).length();
for(int i = 0; i < spaces; i++) Serial.print(" ");
Serial.println("║");
// Vérifie l'intégrité
bool integrite = (msgRecu.value == msgEnvoi.value &&
strcmp(msgRecu.text, msgEnvoi.text) == 0);
Serial.print("║ Integrite: ");
Serial.print(integrite ? "OK ✓" : "ERREUR ✗");
Serial.print(integrite ? " " : " ");
Serial.println("║");
Serial.println("╚══════════════════════════════════════╝\n");
}
delay(10);
}
// Fonction callback pour la réception I2C Slave
void receiveEvent(int howMany) {
if (howMany == sizeof(Message)) {
uint8_t* ptr = (uint8_t*)&msgRecu;
int i = 0;
while (I2C_Slave.available() && i < sizeof(Message)) {
ptr[i] = I2C_Slave.read();
i++;
}
nouveauMessage = true;
} else {
// Taille incorrecte
Serial.print("\n!!! ERREUR: Taille incorrecte: ");
Serial.print(howMany);
Serial.println(" octets !!!\n");
while (I2C_Slave.available()) {
I2C_Slave.read();
}
}
}