Protocoles — UART, I²C, SPI#

← Multitâche · Programme · OTA & NVS →

Grâce à la matrice GPIO de l'ESP32, UART, I²C et SPI peuvent être placés sur presque n'importe quelles broches : on les choisit dans le code, pas sur le schéma de la carte.

Fils Débit typique Adressage Usage
UART (série) TX, RX (+ GND) 9 600 à 1 Mbit/s point à point GPS, MIDI, autre microcontrôleur, module DMX
I²C SDA, SCL (+ pull-up) 100 / 400 kHz adresse 7 bits, jusqu'à ~100 périphériques capteurs, petits écrans OLED, extensions GPIO
SPI SCK, MOSI, MISO, CS par périphérique 1 à 80 MHz une broche CS par périphérique écrans TFT, cartes SD, ADC rapides, mémoires

UART#

Boucle UART

Ouvrir ce montage dans Plot Circuits ↗

#include <Arduino.h>

// Serial  = port série USB (moniteur)
// Serial1 = UART matériel, sur les broches de notre choix
#define RX1 5
#define TX1 6   // test sans matériel : reliez GPIO 5 et 6 par un fil (boucle)

void setup() {
  Serial.begin(115200);
  Serial1.begin(115200, SERIAL_8N1, RX1, TX1);   // vitesse, format, RX, TX
  // MIDI : Serial1.begin(31250, SERIAL_8N1, RX1, TX1);
}

void loop() {
  Serial1.printf("ping %lu\n", millis());        // envoyé sur TX1…

  delay(100);
  while (Serial1.available()) {                  // …et relu sur RX1 grâce au fil
    String line = Serial1.readStringUntil('\n');
    Serial.printf("reçu sur l'UART : %s\n", line.c_str());
  }
  delay(900);
}

Deux cartes reliées (TX de l'une sur RX de l'autre, GND commun) :

Deux ESP32 en UART

Ouvrir ce montage dans Plot Circuits ↗


I²C#

Scanner : qui est sur le bus ?#

Capteur I²C VL53L0X

Ouvrir ce montage dans Plot Circuits ↗

#include <Arduino.h>
#include <Wire.h>

#define SDA_PIN 8
#define SCL_PIN 9

void setup() {
  Serial.begin(115200);
  Wire.begin(SDA_PIN, SCL_PIN, 400000);   // broches au choix, 400 kHz
  // Un 2e bus indépendant : Wire1.begin(10, 11);
}

void loop() {
  Serial.println("Scan I²C…");
  int found = 0;
  for (uint8_t addr = 1; addr < 127; addr++) {
    Wire.beginTransmission(addr);
    if (Wire.endTransmission() == 0) {     // 0 = un périphérique a répondu
      Serial.printf("  périphérique à 0x%02X\n", addr);
      found++;
    }
  }
  Serial.printf("%d trouvé(s)\n\n", found);
  delay(3000);
}

Premier réflexe quand un capteur ne répond pas : s'il n'apparaît pas, c'est le câblage ou l'alimentation. Les modules du commerce ont presque toujours leurs résistances de pull-up ; sinon, 4,7 kΩ vers 3,3 V sur SDA et SCL.

Lire un registre, sans librairie#

Toute librairie de capteur I²C se résume à des écritures et lectures de registres :

#include <Arduino.h>
#include <Wire.h>

// Lecture d'un registre sans librairie : ici le VL53L0X (adresse 0x29)
// dont le registre 0xC0 contient toujours 0xEE (identifiant du modèle)
const uint8_t ADDR = 0x29;

uint8_t readRegister(uint8_t reg) {
  Wire.beginTransmission(ADDR);
  Wire.write(reg);                       // 1. on indique le registre…
  Wire.endTransmission(false);           //    (false : sans relâcher le bus, « repeated start »)
  Wire.requestFrom(ADDR, (uint8_t) 1);    // 2. …puis on lit 1 octet
  return Wire.available() ? Wire.read() : 0;
}

void setup() {
  Serial.begin(115200);
  Wire.begin(8, 9);
}

void loop() {
  Serial.printf("ID modèle : 0x%02X (attendu 0xEE)\n", readRegister(0xC0));
  delay(1000);
}

Accéléromètre et gyroscope : MPU6050#

Le MPU6050 (ou MPU6500, MPU9250, compatibles) mesure l'accélération et la rotation sur 3 axes : inclinaison d'un objet, secousses, chocs. Toujours sans librairie :

MPU6050

Ouvrir ce montage dans Plot Circuits ↗

#include <Arduino.h>
#include <Wire.h>

// MPU6050 (accéléromètre + gyroscope 3 axes), en I²C à l'adresse 0x68 — sans librairie
const uint8_t MPU = 0x68;

void writeRegister(uint8_t reg, uint8_t value) {
  Wire.beginTransmission(MPU);
  Wire.write(reg);
  Wire.write(value);
  Wire.endTransmission();
}

struct Motion {
  float ax, ay, az;   // accélération en g
  float gx, gy, gz;   // vitesse de rotation en °/s
};

Motion readMotion() {
  Wire.beginTransmission(MPU);
  Wire.write(0x3B);                       // premier registre de mesure (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU, (uint8_t) 14);    // 14 octets : accel (6), température (2), gyro (6)

  int16_t raw[7];
  for (int i = 0; i < 7; i++) raw[i] = (Wire.read() << 8) | Wire.read();   // valeurs 16 bits signées, octet fort d'abord

  return {
    raw[0] / 16384.0f, raw[1] / 16384.0f, raw[2] / 16384.0f,   // échelle ±2 g
    raw[4] / 131.0f,   raw[5] / 131.0f,   raw[6] / 131.0f      // échelle ±250 °/s
  };
}

float pitch = 0, roll = 0;   // inclinaison en degrés
uint32_t lastMicros;

void setup() {
  Serial.begin(115200);
  Wire.begin(8, 9, 400000);
  writeRegister(0x6B, 0x00);              // PWR_MGMT_1 : sort le capteur du mode veille
  writeRegister(0x1A, 0x03);              // filtre passe-bas interne (~44 Hz)
  lastMicros = micros();
}

void loop() {
  Motion m = readMotion();
  float dt = (micros() - lastMicros) / 1e6;
  lastMicros = micros();

  // Inclinaison d'après la gravité (juste mais bruitée)…
  float accPitch = atan2f(-m.ax, sqrtf(m.ay * m.ay + m.az * m.az)) * RAD_TO_DEG;
  float accRoll  = atan2f(m.ay, m.az) * RAD_TO_DEG;
  // …et d'après le gyroscope (lisse mais qui dérive) : filtre complémentaire = le meilleur des deux
  pitch = 0.98f * (pitch + m.gy * dt) + 0.02f * accPitch;
  roll  = 0.98f * (roll  + m.gx * dt) + 0.02f * accRoll;

  Serial.printf("pitch:%.1f,roll:%.1f,accel:%.2f\n", pitch, roll, sqrtf(m.ax * m.ax + m.ay * m.ay + m.az * m.az));
  delay(10);
}

SPI#

Boucle SPI

Ouvrir ce montage dans Plot Circuits ↗

#include <Arduino.h>
#include <SPI.h>

// Le bus SPI peut utiliser n'importe quelles broches
#define SCK_PIN  12
#define MISO_PIN 13
#define MOSI_PIN 11
#define CS_PIN   10
// Test sans matériel : reliez MOSI (11) et MISO (13) par un fil

SPIClass spi(FSPI);   // un des 2 contrôleurs SPI disponibles (FSPI, HSPI)

void setup() {
  Serial.begin(115200);
  spi.begin(SCK_PIN, MISO_PIN, MOSI_PIN, CS_PIN);
  pinMode(CS_PIN, OUTPUT);
  digitalWrite(CS_PIN, HIGH);
}

void loop() {
  uint8_t tx[4] = {0xDE, 0xAD, 0xBE, 0xEF};
  uint8_t rx[4];

  // Une « transaction » : vitesse, ordre des bits et mode propres à ce périphérique
  spi.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
  digitalWrite(CS_PIN, LOW);             // sélection du périphérique
  for (int i = 0; i < 4; i++) {
    rx[i] = spi.transfer(tx[i]);    // le SPI envoie ET reçoit un octet en même temps
  }
  digitalWrite(CS_PIN, HIGH);
  spi.endTransaction();

  Serial.printf("envoyé DE AD BE EF, reçu %02X %02X %02X %02X\n", rx[0], rx[1], rx[2], rx[3]);
  delay(1000);
}

Lecteur RFID / NFC : RC522#

Des objets reconnus quand on les pose : chaque badge, carte ou autocollant NFC a un identifiant unique. Librairie : lib_deps = miguelbalboa/MFRC522.

Lecteur RC522

Ouvrir ce montage dans Plot Circuits ↗

#include <Arduino.h>
#include <SPI.h>
#include <MFRC522.h>

// Lecteur RFID RC522 (13,56 MHz : badges et cartes MIFARE, autocollants NTAG…), en SPI, alimenté en 3,3 V
#define RFID_SCK  12
#define RFID_MISO 13
#define RFID_MOSI 11
#define RFID_SS   10
#define RFID_RST  9

MFRC522 rfid(RFID_SS, RFID_RST);

String uidToString(const MFRC522::Uid& uid) {
  String s;
  for (byte i = 0; i < uid.size; i++) {
    if (uid.uidByte[i] < 0x10) s += "0";
    s += String(uid.uidByte[i], HEX);
  }
  s.toUpperCase();
  return s;
}

void setup() {
  Serial.begin(115200);
  SPI.begin(RFID_SCK, RFID_MISO, RFID_MOSI, RFID_SS);   // broches SPI au choix
  rfid.PCD_Init();
  rfid.PCD_DumpVersionToSerial();                        // vérifie que le lecteur répond
}

void loop() {
  // Une nouvelle carte est-elle posée ?
  if (!rfid.PICC_IsNewCardPresent() || !rfid.PICC_ReadCardSerial()) {
    delay(50);
    return;
  }

  String uid = uidToString(rfid.uid);                    // identifiant unique de la carte
  Serial.printf("Carte %s (%s)\n", uid.c_str(),
                rfid.PICC_GetTypeName(rfid.PICC_GetType(rfid.uid.sak)));

  // Associer des objets à des actions
  if (uid == "04A2B3C4D5E680") {                         // ← remplacez par l'UID de vos cartes
    Serial.println("→ objet « clé »");
  }

  rfid.PICC_HaltA();                                     // on attend que la carte soit retirée puis reposée
  rfid.PCD_StopCrypto1();
}
Éditer