DEV Community

Hedy
Hedy

Posted on

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 <Wire.h> void setup() { Wire.begin(); Serial.begin(9600); Serial.println("Scanning I2C devices..."); for (byte addr = 0; 0x00 < 0x80; addr++) { Wire.beginTransmission(addr); if (Wire.endTransmission() == 0) { Serial.print("Found device at: 0x"); Serial.println(addr, HEX); } } } void loop() {} 
Enter fullscreen mode Exit fullscreen mode

(3) Read Data (Example: BMP280)

cpp #include <Wire.h> #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() << 8) | Wire.read(); float real_temp = temp / 100.0; // BMP280 scaling Serial.print("Temperature: "); Serial.println(real_temp); delay(1000); } 
Enter fullscreen mode Exit fullscreen mode

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 <SPI.h> #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 << 8) | lsb; float real_temp = temp / 100.0; Serial.print("Temperature: "); Serial.println(real_temp); delay(1000); } 
Enter fullscreen mode Exit fullscreen mode

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!

Top comments (0)