How to read data from I2C or SPI-based sensors?

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • MyrinNew
    Senior Member
    • Feb 2024
    • 5175

    #1

    How to read data from I2C or SPI-based sensors?

    Reading data from I2C or SPI sensors with microcontrollers (like Arduino, STM32, or ESP32) involves understanding the protocol, wiring, and communication steps. Below is a concise guide for both interfaces.





    1. Reading from I2C Sensors

    I2C Basics
    • 2-wire interface (SDA = data, SCL = clock).
    • 7-bit addressing (common sensors: 0x68 for MPU6050, 0x27 for LCD, etc.).
    • Master-slave communication (microcontroller = master, sensor = slave).


    Steps to Read Data

    (1) Wiring




    (Add pull-up resistors (4.7kΩ) if not built into the sensor.)


    (2) Scan for I2C Address (Optional)






    cpp
    #include
    void setup() {
    Wire.begin();
    Serial.begin(9600);
    Serial.println("Scanning I2C devices...");
    for (byte addr = 0; 0x00 Wire.beginTransmission(addr);
    if (Wire.endTransmission() == 0) {
    Serial.print("Found device at: 0x");
    Serial.println(addr, HEX);
    }
    }
    }
    void loop() {}







    (3) Read Data (Example: BMP280)






    cpp
    #include
    #define BMP280_ADDR 0x76 // Check datasheet

    void setup() {
    Wire.begin();
    Serial.begin(9600);
    // Configure sensor (refer to datasheet)
    Wire.beginTransmission(BMP280_ADDR);
    Wire.write(0xF4); // Control register
    Wire.write(0x27); // Normal mode, 1x temp/pressure oversampling
    Wire.endTransmission();
    }

    void loop() {
    // Read temperature (2 bytes)
    Wire.beginTransmission(BMP280_ADDR);
    Wire.write(0xFA); // Temp MSB register
    Wire.endTransmission();
    Wire.requestFrom(BMP280_ADDR, 2); // Request 2 bytes
    int16_t temp = (Wire.read() float real_temp = temp / 100.0; // BMP280 scaling
    Serial.print("Temperature: ");
    Serial.println(real_temp);
    delay(1000);
    }







    2. Reading from SPI Sensors

    SPI Basics

    4-wire interface:
    • SCK = Clock (from master)
    • MOSI = Master Out Slave In (data to sensor)
    • MISO = Master In Slave Out (data from sensor)
    • SS/CS = Slave Select (chip enable, active LOW)


    Full-duplex (simultaneous send/receive).


    Faster than I2C (MHz speeds possible).


    Steps to Read Data

    (1) Wiring




    (Some sensors use SDI/SDO instead of MOSI/MISO.)


    (2) Read Data (Example: BME280)






    cpp
    #include
    #define BME_CS 10 // Chip Select pin

    void setup() {
    SPI.begin();
    pinMode(BME_CS, OUTPUT);
    Serial.begin(9600);
    }

    void loop() {
    // Read temperature (SPI)
    digitalWrite(BME_CS, LOW); // Activate sensor
    SPI.transfer(0xFA); // Send register address
    uint8_t msb = SPI.transfer(0x00); // Dummy write to read
    uint8_t lsb = SPI.transfer(0x00);
    digitalWrite(BME_CS, HIGH); // Deactivate sensor
    int16_t temp = (msb float real_temp = temp / 100.0;
    Serial.print("Temperature: ");
    Serial.println(real_temp);
    delay(1000);
    }







    Key Differences & Tips




    Common Pitfalls

    ✔ I2C:
    • Missing pull-up resistors → communication fails.
    • Address conflicts (check datasheet).


    ✔ SPI:
    • Forgetting to toggle CS → no response.
    • Clock polarity/phase mismatch (use SPI_MODE0, SPI_MODE3 as per sensor).


    Libraries for Easier Use
    • I2C: Wire.h (Arduino), HAL_I2C (STM32).
    • SPI: SPI.h (Arduino), HAL_SPI (STM32).
    • Sensor-Specific:
      • Adafruit_Sensor (BME280, MPU6050).
      • SparkFun_ADS1015 (ADC over I2C).


    Final Advice

    1. Check the datasheet for register maps and protocols.
    2. Start with known libraries before writing raw I2C/SPI.
    3. Use logic analyzers (Saleae, PulseView) if communication fails.


    Both protocols are powerful—I2C for simplicity, SPI for speed!




    More...
Working...