ESP32 I2C Communication: Set Pins, Multiple Bus Interfaces and Peripherals (Arduino IDE)

The ESP32 has two I2C bus interfaces that can serve as I2C master or slave. In this tutorial we’ll take a look at the I2C communication protocol with the ESP32 using Arduino IDE: how to choose I2C pins, connect multiple I2C devices to the same bus and how to use the two I2C bus interfaces.

ESP32 I2C Communication: Set Pins, Multiple Bus Interfaces and Peripherals (Arduino IDE)

In this tutorial, we’ll cover the following concepts:

We’ll program the ESP32 using Arduino IDE, so before proceeding with this tutorial you should have the ESP32 add-on installed in your Arduino IDE. Follow the next tutorial to install the ESP32 on the Arduino IDE, if you haven’t already.

Introducing ESP32 I2C Communication Protocol

I²C means Inter Integrated Circuit (it’s pronounced I-squared-C), and it is a synchronous, multi-master, multi-slave communication protocol. You can connect :

  • multiple slaves to one master: for example, your ESP32 reads from a BME280 sensor using I2C and writes the sensor readings in an I2C OLED display.
  • multiple masters controlling the same slave: for example, two ESP32 boards writing data to the same I2C OLED display.

We use this protocol many times with the ESP32 to communicate with external devices like sensors and displays. In these cases, the ESP32 is the master chip and the external devices are the slaves.

I2C Communication protocol with ESP32 board using Arduino IDE multiple devices

We have several tutorials with the ESP32 interfacing with I2C devices:

ESP32 I2C Bus Interfaces

The ESP32 supports I2C communication through its two I2C bus interfaces that can serve as I2C master or slave, depending on the user’s configuration. Accordingly to the ESP32 datasheet, the I2C interfaces of the ESP32 supports:

  • Standard mode (100 Kbit/s) 
  • Fast mode (400 Kbit/s) 
  • Up to 5 MHz, yet constrained by SDA pull-up strength 
  • 7-bit/10-bit addressing mode 
  • Dual addressing mode. Users can program command registers to control I²C interfaces, so that they have more flexibility

Connecting I2C Devices with ESP32

I2C communication protocol uses two wires to share information. One is used for the clock signal (SCL) and the other is used to send and receive data (SDA).

Note: in many breakout boards, the SDA line may also be labeled as SDI and the SCL line as SCK.

BME280 sensor I2C SDA (SDI) and SCL (SCK) pins

The SDA and SCL lines are active low, so they should be pulled up with resistors. Typical values are 4.7k Ohm for 5V devices and 2.4k Ohm for 3.3V devices.

Most sensors we use in our projects are breakout boards that already have the resistors built-in. So, usually, when you’re dealing with this type of electronics components you don’t need to worry about this.

Connecting an I2C device to an ESP32 is normally as simple as connecting GND to GND, SDA to SDA, SCL to SCL and a positive power supply to a peripheral, usually 3.3V (but it depends on the module you’re using).

I2C DeviceESP32
SDASDA (default is GPIO 21)
SCLSCL (default is GPIO 22)
GNDGND
VCCusually 3.3V or 5V

When using the ESP32 with Arduino IDE, the default I2C pins are GPIO 22 (SCL) and GPIO 21 (SDA) but you can configure your code to use any other pins.

Recommended reading: ESP32 GPIO Reference Guide

Scan I2C Address with ESP32

With I2C communication, each slave on the bus has its own address, a hexadecimal number that allows the ESP32 to communicate with each device.

The I2C address can be usually found on the component’s datasheet. However, if it is difficult to find out, you may need to run an I2C scanner sketch to find out the I2C address.

You can use the following sketch to find your devices’ I2C address.

/*********
  Rui Santos
  Complete project details at https://randomnerdtutorials.com  
*********/

#include <Wire.h>
 
void setup() {
  Wire.begin();
  Serial.begin(115200);
  Serial.println("\nI2C Scanner");
}
 
void loop() {
  byte error, address;
  int nDevices;
  Serial.println("Scanning...");
  nDevices = 0;
  for(address = 1; address < 127; address++ ) {
    Wire.beginTransmission(address);
    error = Wire.endTransmission();
    if (error == 0) {
      Serial.print("I2C device found at address 0x");
      if (address<16) {
        Serial.print("0");
      }
      Serial.println(address,HEX);
      nDevices++;
    }
    else if (error==4) {
      Serial.print("Unknow error at address 0x");
      if (address<16) {
        Serial.print("0");
      }
      Serial.println(address,HEX);
    }    
  }
  if (nDevices == 0) {
    Serial.println("No I2C devices found\n");
  }
  else {
    Serial.println("done\n");
  }
  delay(5000);          
}

View raw code

You’ll get something similar in your Serial Monitor. This specific example is for an I2C LCD Display.

I2C scanner sketch serial monitor found I2C address

Use Different I2C Pins with ESP32 (change default I2C pins)

With the ESP32 you can set almost any pin to have I2C capabilities, you just need to set that in your code.

When using the ESP32 with the Arduino IDE, use the Wire.h library to communicate with devices using I2C. With this library, you initialize the I2C as follows:

Wire.begin(I2C_SDA, I2C_SCL);

So, you just need to set your desired SDA and SCL GPIOs on the I2C_SDA and I2C_SCL variables.

However, if you’re using libraries to communicate with those sensors, this might not work and it might be a bit tricky to select other pins. That happens because those libraries might overwrite your pins if you don’t pass your own Wire instance when initializing the library.

In those cases, you need to take a closer look at the .cpp library files and see how to pass your own TwoWire parameters.

For example, if you take a closer look at the Adafruit BME280 library, you’ll find out that you can pass your own TwoWire to the begin() method.

Adafruit_BME280 library using different I2C pins

So, the example sketch to read from the BME280 using other pins, for example GPIO 33 as SDA and and GPIO 32 as SCL is as follows.

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-i2c-communication-arduino-ide/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

#define I2C_SDA 33
#define I2C_SCL 32

#define SEALEVELPRESSURE_HPA (1013.25)

TwoWire I2CBME = TwoWire(0);
Adafruit_BME280 bme;

unsigned long delayTime;

void setup() {
  Serial.begin(115200);
  Serial.println(F("BME280 test"));
  I2CBME.begin(I2C_SDA, I2C_SCL, 100000);

  bool status;

  // default settings
  // (you can also pass in a Wire library object like &Wire2)
  status = bme.begin(0x76, &I2CBME);  
  if (!status) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }

  Serial.println("-- Default Test --");
  delayTime = 1000;

  Serial.println();
}

void loop() { 
  printValues();
  delay(delayTime);
}

void printValues() {
  Serial.print("Temperature = ");
  Serial.print(bme.readTemperature());
  Serial.println(" *C");
  
  // Convert temperature to Fahrenheit
  /*Serial.print("Temperature = ");
  Serial.print(1.8 * bme.readTemperature() + 32);
  Serial.println(" *F");*/
  
  Serial.print("Pressure = ");
  Serial.print(bme.readPressure() / 100.0F);
  Serial.println(" hPa");

  Serial.print("Approx. Altitude = ");
  Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
  Serial.println(" m");

  Serial.print("Humidity = ");
  Serial.print(bme.readHumidity());
  Serial.println(" %");

  Serial.println();
}

View raw code

ESP32 with BME280 on Different I2C Pins Schematic Diagram Wiring
Click image to enlarge

Let’s take a look at the relevant parts to use other I2C pins.

First, define your new I2C pins on the I2C_SDA and I2C_SCL variables. In this case, we’re using GPIO 33 and GPIO 32.

#define I2C_SDA 33
#define I2C_SCL 32

Create a new TwoWire instance. In this case, it’s called I2CBME. This simply creates an I2C bus.

TwoWire I2CBME = TwoWire(0);

In the setup(), initialize the I2C communication with the pins you’ve defined earlier. The third parameter is the clock frequency.

I2CBME.begin(I2C_SDA, I2C_SCL, 400000);

Finally, initialize a BME280 object with your sensor address and your TwoWire object.

status = bme.begin(0x76, &I2CBME);

After this, you can use use the usual methods on your bme object to request temperature, humidity and pressure.

Note: if the library you’re using uses a statement like wire.begin() in its file, you may need to comment that line, so that you can use your own pins.


ESP32 with Multiple I2C Devices

As we’ve mentioned previously, each I2C device has its own address, so it is possible to have multiple I2C devices on the same bus.

Multiple I2C devices (same bus, different addresses)

When we have multiple devices with different addresses, it is trivial how to set them up:

  • connect both peripherals to the ESP32 SCL and SDA lines;
  • in the code, refer to each peripheral by its address;

Take a look at the following example that gets sensor readings from a BME280 sensor (via I2C) and displays the results on an I2C OLED display.

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-i2c-communication-arduino-ide/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

Adafruit_BME280 bme;

void setup() {
  Serial.begin(115200);

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  
  bool status = bme.begin(0x76);  
  if (!status) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }
  
  delay(2000);
  display.clearDisplay();
  display.setTextColor(WHITE);
}

void loop() {
  display.clearDisplay();
  // display temperature
  display.setTextSize(1);
  display.setCursor(0,0);
  display.print("Temperature: ");
  display.setTextSize(2);
  display.setCursor(0,10);
  display.print(String(bme.readTemperature()));
  display.print(" ");
  display.setTextSize(1);
  display.cp437(true);
  display.write(167);
  display.setTextSize(2);
  display.print("C");
  
  // display humidity
  display.setTextSize(1);
  display.setCursor(0, 35);
  display.print("Humidity: ");
  display.setTextSize(2);
  display.setCursor(0, 45);
  display.print(String(bme.readHumidity()));
  display.print(" %"); 
  
  display.display();

  delay(1000);
}

View raw code

ESP32 with BME280 and OLED Display Schematic Diagram Wiring
Click image to enlarge

Because the OLED and the BME280 have different addresses, we can use the same SDA and SCL lines without any problem. The OLED display address is 0x3C and the BME280 address is 0x76.

if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { 
  Serial.println(F("SSD1306 allocation failed"));
  for(;;);
}
  
bool status = bme.begin(0x76);  
if (!status) {
  Serial.println("Could not find a valid BME280 sensor, check wiring!");
  while (1);
}
Display BME280 readings on I2C OLED display - two I2C peripherals on the same bus

Recommended reading: ESP32 OLED Display with Arduino IDE

Multiple I2C devices (same address)

But, what if you have multiple peripherals with the same address? For example, multiple OLED displays or multiple BME280 sensors? There are several solutions.

  • change the device I2C address;
  • use an I2C multiplexer.

Changing the I2C address

Many breakout boards have the option to change the I2C address depending on its circuitry. For example, that a look at the following OLED display.

Selecting a different I2C address on SSD1306 OLED Display

By placing the resistor on one side or the other, you can select different I2C addresses. This also happens with other components.

Using an I2C Multiplexer

However, in this previous example, this only allows you to have two I2C displays on the same bus: one with 0x3C address and another with 0x3D address.

Additionally, sometimes it is not trivial changing the I2C address. So, in order to have multiple devices with the same address in the same I2C bus, you can use an I2C multiplexer like the TCA9548A that allows you to communicate with up to 8 devices with the same address.

TCA9548A I2C Multiplexer

ESP32 Using Two I2C Bus Interfaces

To use the two I2C bus interfaces of the ESP32, you need to create two TwoWire instances.

TwoWire I2Cone = TwoWire(0);
TwoWire I2Ctwo = TwoWire(1)

Then, initialize I2C communication on your desired pins with a defined frequency.

void setup() {
  I2Cone.begin(SDA_1, SCL_1, freq1);
  I2Ctwo.begin(SDA_2, SCL_2, freq2); 
}

Then, you can use the methods from the Wire.h library to interact with the I2C bus interfaces.

A simpler alternative is using the predefined Wire() and Wire1() objects. Wire().begin() creates an I2C communication on the first I2C bus using the default pins and default frequency. For the Wire1.begin() you should pass your desired SDA and SCL pins as well as the frequency.

setup(){
  Wire.begin(); //uses default SDA and SCL and 100000HZ freq
  Wire1.begin(SDA_2, SCL_2, freq);
}

This method allows you to use two I2C buses, one of them uses the default parameters.

To better understand how this works, we’ll take a look at a simple example that reads temperature, humidity and pressure from two BME280 sensors.

Multiple BME280 sensors with ESP32 - using the ESP32 two I2C bus interfaces

Each sensor is connected to a different I2C bus.

  • I2C Bus 1: uses GPIO 27 (SDA) and GPIO 26 (SCL);
  • I2C Bus 2: uses GPIO 33 (SDA) and GPIO 32 (SCL);
ESP32 with Multiple I2C BME280 (multiple I2C bus) Schematic Diagram
Click image to enlarge
/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-i2c-communication-arduino-ide/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

#define SDA_1 27
#define SCL_1 26

#define SDA_2 33
#define SCL_2 32

TwoWire I2Cone = TwoWire(0);
TwoWire I2Ctwo = TwoWire(1);

Adafruit_BME280 bme1;
Adafruit_BME280 bme2;

void setup() {
  Serial.begin(115200);
  Serial.println(F("BME280 test"));

  I2Cone.begin(SDA_1, SCL_1, 100000); 
  I2Ctwo.begin(SDA_2, SCL_2, 100000);

  bool status1 = bme1.begin(0x76, &I2Cone);  
  if (!status1) {
    Serial.println("Could not find a valid BME280_1 sensor, check wiring!");
    while (1);
  }
  
  bool status2 = bme2.begin(0x76, &I2Ctwo);  
  if (!status2) {
    Serial.println("Could not find a valid BME280_2 sensor, check wiring!");
    while (1);
  }
  
  Serial.println();
}

void loop() { 
  // Read from bme1
  Serial.print("Temperature from BME1= ");
  Serial.print(bme1.readTemperature());
  Serial.println(" *C");

  Serial.print("Humidity from BME1 = ");
  Serial.print(bme1.readHumidity());
  Serial.println(" %");

  Serial.print("Pressure from BME1 = ");
  Serial.print(bme1.readPressure() / 100.0F);
  Serial.println(" hPa");

  Serial.println("--------------------");

  // Read from bme2
  Serial.print("Temperature from BME2 = ");
  Serial.print(bme2.readTemperature());
  Serial.println(" *C");

  Serial.print("Humidity from BME2 = ");
  Serial.print(bme2.readHumidity());
  Serial.println(" %");

  Serial.print("Pressure from BME2 = ");
  Serial.print(bme2.readPressure() / 100.0F);
  Serial.println(" hPa");

  Serial.println("--------------------");
  
  delay(5000);
}

View raw code

Let’s take a look at the relevant parts to use the two I2C bus interfaces.

Define the SDA and SCL pins you want to use:

#define SDA_1 27
#define SCL_1 26

#define SDA_2 33
#define SCL_2 32

Create two TwoWire objects (two I2C bus interfaces):

TwoWire I2Cone = TwoWire(0);
TwoWire I2Ctwo = TwoWire(1);

Create two instances of the Adafruit_BME280 library to interact with your sensors: bme1 and bme2.

Adafruit_BME280 bme1;
Adafruit_BME280 bme2;

Initialize an I2C communication on the defined pins and frequency:

I2Cone.begin(SDA_1, SCL_1, 100000); 
I2Ctwo.begin(SDA_2, SCL_2, 100000);

Then, you should initialize the bme1 and bme2 objects with the right address and I2C bus. bme1 uses I2Cone:

bool status = bme1.begin(0x76, &I2Cone);  

And bme2 uses I2Ctwo:

bool status1 = bme2.begin(0x76, &I2Ctwo);

Now, you can use the methods from the Adafruit_BME280 library on your bme1 and bme2 objects to read temperature, humidity and pressure.

Another alternative

For simplicity, you can use the predefined Wire() and Wire1() objects:

  • Wire(): creates an I2C bus on the default pins GPIO 21 (SDA) and GPIO 22 (SCL)
  • Wire1(SDA_2, SCL_2, freq): creates an I2C bus on the defined SDA_2 and SCL_2 pins with the desired frequency.
ESP32 with Multiple I2C BME280 Schematic Diagram
Click image to enlarge

Here’s the same example but using this method. Now, one of your sensors uses the default pins, and the other uses GPIO 32 and GPIO 33.

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-i2c-communication-arduino-ide/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

#define SDA_2 33
#define SCL_2 32

Adafruit_BME280 bme1;
Adafruit_BME280 bme2;

void setup() {
  Serial.begin(115200);
  Serial.println(F("BME280 test"));

  Wire.begin();
  Wire1.begin(SDA_2, SCL_2);

  bool status1 = bme1.begin(0x76);  
  if (!status1) {
    Serial.println("Could not find a valid BME280_1 sensor, check wiring!");
    while (1);
  }
  
  bool status2 = bme2.begin(0x76, &Wire1);  
  if (!status2) {
    Serial.println("Could not find a valid BME280_2 sensor, check wiring!");
    while (1);
  }
 
  Serial.println();
}

void loop() { 
  // Read from bme1
  Serial.print("Temperature from BME1= ");
  Serial.print(bme1.readTemperature());
  Serial.println(" *C");

  Serial.print("Humidity from BME1 = ");
  Serial.print(bme1.readHumidity());
  Serial.println(" %");
  
  Serial.print("Pressure from BME1 = ");
  Serial.print(bme1.readPressure() / 100.0F);
  Serial.println(" hPa");

  Serial.println("--------------------");
  
  // Read from bme2
  Serial.print("Temperature from BME2 = ");
  Serial.print(bme2.readTemperature());
  Serial.println(" *C");

  Serial.print("Humidity from BME2 = ");
  Serial.print(bme2.readHumidity());
  Serial.println(" %");
  
  Serial.print("Pressure from BME2 = ");
  Serial.print(bme2.readPressure() / 100.0F);
  Serial.println(" hPa");

  Serial.println("--------------------");
  delay(5000);
}

View raw code

You should get both sensor readings on your Serial Monitor.

ESP32 multiple BME280 sensors serial monitor output

Wrapping Up

In this tutorial you learned more about I2C communication protocol with the ESP32. We hope you’ve found the information in this article useful.

To learn more about the ESP32 GPIOs, read our reference guide: ESP32 Pinout Reference: Which GPIO pins should you use?

Learn more about the ESP32 with our resources:

Thanks for reading.


Learn how to program and build projects with the ESP32 and ESP8266 using MicroPython firmware DOWNLOAD »

Learn how to program and build projects with the ESP32 and ESP8266 using MicroPython firmware DOWNLOAD »


Enjoyed this project? Stay updated by subscribing our weekly newsletter!

45 thoughts on “ESP32 I2C Communication: Set Pins, Multiple Bus Interfaces and Peripherals (Arduino IDE)”

  1. Hi Sara,
    Very good tutorial. You have mentioned that multiple devices can be looped Pl, let me know how many devices we can connect on same bus. What will be the maximum distance between devices and the ESp32. Which type and size of cable is required to connect various devices since different types of cable have different resistance per meter of length.
    Thanks

    Reply
  2. Hi Rui and Sara

    I can see the advantage of having two I2C busses when you want to use two sensors with the same fixed addresses (although the two BME280s in your example produce different results when located next to each other) but what other advantages are there in running two separate busses?

    Apart from when I need BlueTooth, I tend to stick to ESP8266, but maybe I’m missing out on something…

    Reply
  3. Rui, great tutorial. Thanks.

    Looking forward to the ESP8266 version, since that’s the kind of hardware I have, mostly.

    Reply
  4. Hello, i would like to use my ESP32 as a Slave, to do this, i use de Wire.h library, but it’s look like the .onReceive don’t working…
    Do you have some solution ?

    Reply
  5. Hi! Thanks for the tutorial!
    I’m trying to connect a gpio extender (PCF8574) to the esp32 CAM, with no luck. Do you know how can I set up a new i2c bus for the extender? I’ve tried using ports I2C_SDA 0 , I2C_SCL 2 with no luck.

    Reply
  6. Hello friends.
    Could you please explain how this works when using the Adafruit ADS1115 library?

    How to tell her that it is necessary to use the second woWire bus (1)?

    I tried pointing as Adafruit_ADS1115 ads1115 (& I2CSensors);
    This does not work.

    Reply
  7. hi !
    really great tutorial!! many many thanks !!
    My question is : is it available a tutorial to connect ESP32 as master and one or more d1-mini (esp8266) as slaves ?
    Many thanks
    peppe

    Reply
    • You may find a wireless protocol such as ESP-NOW would be a better solution than I2C. I am pretty sure ESP32 and ESP8266 would talk to each other just fine, with a payload of 250 bytes per transmission. It all depends what you want to do with the system.

      Reply
  8. Having worked a bit with I2C sensors in my past, I can say they are not very good for long distance (i.e, more than a meter) separation. One wire is a much better protocol for that. I2C was designed for use on the same board with distances of a few centimeters of bus length.

    That said, you can get more distance with lower bus frequencies, The maximum distance for your sensor would need to be found experimentally, and many factors affect it. Things such as the type of cable, external interference/noise, and the voltage used to drive the bus. The longer the distance the more critical these become.

    Personally, I prefer other methods to connect over longer distances. It is easier and more time efficient to use a protocol that was designed for distance such as RS232 or RS485 with appropriate driver ICs. For short distances such as to a display on on board sensor, I2C is great though!

    It is important in this hobby to pay attention to what things were designed to do, and use them appropriately for the greatest success.

    Reply
  9. Hello friends.
    Has anyone managed to combine this board with a solar battery for autonomous power supply?

    Share the schematic pls!

    Reply
  10. I have found your tutorials most enlightening. I am trying to log a BMP280 with ESP32 CAM on alternate I2C, but keep getting compiler errors with your code, which was written for BME280. I have delved into the .h and .cpp codes but cannot find why the BMP280 is not being found. Your code works fine with ESP12E and BMP280, if I use the library BMP280_DEV instead of the Adafruit library, but that library code does not support ESP32 with I2C, only SPI. I need to use the SD card facility on the ESP32CAM to log temperature and pressure. I tried mapping defaults 21 & 22 which are not accessible, to 3 & 16. Could it be that the Adafruit BMP280 library does not support alternate I2C ?

    Reply
    • Hi.
      The BME280 library from adafruit and the camera library for the ESP32-CAM conflict with each other.
      I have used the Saprkfun BME280 library with the ESP32-CAM and it works well.
      But I don’t know if it works with the BMP280.
      I hope this helps.
      Regards,
      Sara

      Reply
  11. Hi Sara,
    Thanks so much for the quick reply. I tried the Sparkfun Library you suggested but I get a compiler error “Error compiling for board AiThinker ESP32 CAM”, which is what I get with Adafruit as well.
    I do not think there has been much written for ESP32 Cam that uses alternate I2C, which is needed because of the scarcity of GPIO pins. I will try the TTGO next up.
    Regards
    Lockie

    Reply
  12. Hi Sara,
    I ran the example sketch”softI2C” from the Sparkfun BME library. I set compiler preferences to verbose and picked out examples of the noted errors. See below:

    C:\Users\marts\Documents\Arduino\libraries\SoftwareWire\SoftwareWire.cpp:134:16: error: cannot convert ‘volatile uint32_t* {aka volatile unsigned int}’ to ‘volatile uint8_t {aka volatile unsigned char}’ in assignment
    _sdaPinReg = portInputRegister(port); // PinReg is the input register, not the Arduino pin.
    C:\Users\marts\Documents\Arduino\libraries\SoftwareWire\SoftwareWire.cpp: In member function ‘void SoftwareWire::printStatus(Print&)’:
    C:\Users\marts\Documents\Arduino\libraries\SoftwareWire\SoftwareWire.cpp:533:27: error: cast from ‘volatile uint8_t
    {aka volatile unsigned char*}’ to ‘uint16_t {aka short unsigned int}’ loses precision [-fpermissive]
    Ser.println( (uint16_t) _sdaPortReg, HEX);
    Using library SoftwareWire at version 1.5.1 in folder: C:\Users\marts\Documents\Arduino\libraries\SoftwareWire
    Using library Wire at version 1.0.1 in folder: C:\Users\marts\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.4\libraries\Wire
    Using library SparkFun_BME280 at version 2.0.8 in folder: C:\Users\marts\Documents\Arduino\libraries\SparkFun_BME280
    Using library SPI at version 1.0 in folder: C:\Users\marts\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.4\libraries\SPI
    exit status 1
    Error compiling for board AI Thinker ESP32-CAM.

    I haven’t found anything yet that works with alternate I2C on ESP32-CAM.
    Cheers,

    lockie

    Reply
  13. Hi Sara,
    Thank you for your effort in helping me. In my debugging efforts I noticed that all I have to do is include “SparkFunBME280.h” to get compiler errors which relate to “SoftwareWire.h” by Testato, which is called by the former if you enable software I2C as per the instructions.
    Does your example (noted in your last post) enable software i2c?
    I have tried it with TTGO and get compiler errors as well, although if I use BMP280_Dev.h instead all works OK (But not for software I2C, which BMP280_Dev does not support for ESP32 devices)
    Regards

    lockie

    Reply
    • Hi.
      We use the library as shown in the post.
      include the following libraries:
      #include <Wire.h>
      #include “SparkFunBME280.h”

      Define the I2C pins:
      #define I2C_SDA 14
      #define I2C_SCL 15

      Initialize the sensor as follows:
      Wire.begin(I2C_SDA, I2C_SCL);
      bme.settings.commInterface = I2C_MODE;
      bme.settings.I2CAddress = 0x76;
      bme.settings.runMode = 3;
      bme.settings.tStandby = 0;
      bme.settings.filter = 0;
      bme.settings.tempOverSample = 1;
      bme.settings.pressOverSample = 1;
      bme.settings.humidOverSample = 1;
      bme.begin();

      Get readings:
      float temperature, humidity;
      temperature = bme.readTempC();
      //temperature = bme.readTempF();
      humidity = bme.readFloatHumidity();

      No need for any other libraries or other configurations.

      Regards,
      Sara

      Reply
  14. Hi Sara,
    I have included the code you posted in my sketch, but I still get the same errors in the SoftwareWire.cpp library, when using software I2C with my TTGO as “ESPDEV Module” in Arduino as suggested by TTGO. So that makes me think that the source of the error is in the SoftwareWire library.
    So I checked the Github for Testato’s SoftwareWire and it appears there are issues when using MCU’s with 32 bit registers. Maybe you are not using the software I2C feature – that might explain why the code works for you.
    Anyway, I appreciated your suggestions.
    Regards

    Lockie

    Reply
  15. Hello again,
    I tried to compile some example sketches from SparkfunBME280 and I get compiler errors. Then I tried to compile a sketch from SoftwareWire and that also gives a compile error. So then I reverted to the SparkfunBME280.h that doesn’t call SoftwareWire and tried compiling your code and it compiled OK.
    That suggests my argument that SoftwareWire library does not support ESP32 devices may be correct. My original intention was to use a BMP280 with ESP32-Cam using software defined I2C as there is a shortage of GPIO’s available. The hardware GPIO pins are 21 & 22, but when using the SD card on ESP32-CAM using one of RUI’s sketches only GPIO 0, 3, &16 are accessible. That is why I wanted to use software defined I2C pins.
    I have tried numerous BMP280 libraries and the only one that seems to work well is BMP280_DEV, but as I said previously it does not support ESP32.
    I then changed my board to AiThinker ESP32CAM and recompiled and got an out of memory error. So I thought that was more positive and decided to power down, then power-up and re-compile. This time it worked!!
    So to re-cap for other newbies, SoftwareWire library does not appear to work with ESP32 devices, so do not call it in SparkFunBME280 for software I2C.
    Once again, Thanks.

    Reply
  16. Whilst I can now print temperature and pressure to the serial monitor, I am struggling to print it to the SD card. I am using a version of Rui’s logSD which successfully writes the date and time to the card, but leaves the temperature blank. The code for the routine is:
    // Write the sensor readings on the SD card
    void logSDCard() {
    dataMessage = String(readingID) + “,” + String(dayStamp) + “,” + String(timeStamp) + “,” + String(temperature) + “\r\n”;
    Serial.print(“Save data: “);
    Serial.println(dataMessage);
    appendFile(SD_MMC, “/data.txt”, dataMessage.c_str());
    }

    I have not been able to pass the temperature variable to this routine.
    Any ideas?
    Thanks, Lockie

    Reply
    • Maybe this will help:
      You may need to convert the data to a string first. A welcomed utility function I found is dtostrf. (decimal to string). Remember that serial comms know nothing about data types, they simply send characters.

      char * dtostrf (double __val, signed char __width, unsigned char __prec, char *__s)

      The dtostrf() function converts the double value passed in val into an ASCII representationthat will be stored under s. The caller is responsible for providing sufficient storage in s.

      Conversion is done in the format “[-]d.ddd”. The minimum field width of the output string (including the ‘.’ and the possible sign for negative values) is given in width, and prec determines the number of digits after the decimal sign. width is signed value, negative for left adjustment.

      The dtostrf() function returns the pointer to the converted string s.

      char buffer[10];
      dtostrf(float or double Var, 5, 1, buffer);

      //now you can print or send the buffer to a text display

      Below is how I used it with a couple of one wire probes. As per above
      tempInC is a float, I expect a max of 6 chars, with 1 after decimal point. I have two char buffers named testInC and testOutC respectively, each 7 chars.
      dtostrf(tempInC, 6,1, testInC);
      dtostrf(tempOutC, 6,1, testOutC);

      Best of luck!
      Dave K.

      Reply
  17. Thanks so much, Dave.
    As a newbie what you told me was DD, but I perservered and after dozens of compiles I got the declarations correct, and so now it prints both to the screen and the SD card.
    Here is the code snippit:
    static char tempc[8];
    static char pressr[9];
    static float temperature;
    static float pressure;
    // Function to get temperature
    String Getreadings(){
    //float temperature, pressure;
    temperature = bme.readTempC();
    pressure = bme.readFloatPressure()/100;
    dtostrf(temperature,6,2,tempc);
    dtostrf(pressure,7,2,pressr);
    String message = “Temperature: ” + String (temperature) + ” ºC \n”;
    message += “Pressure: ” + String (pressure) + ” hPa \n”;
    return message;

    So now I am logging BMP280 temperature and pressure every minute and writing the data to the onboard SD card on the ESP32 CAM together with time and date off the internet. (I started Arduino mid-August)
    Thanks to both you and Sara for getting me there, and to Rui (for the ideas).
    I have been at this project for some weeks now, and I have been frustrated by the lack of documentation of the various libraries and their foibles.

    Reply
    • Sounds like it helped! GREAT!
      Congratulations on your success!

      I know for sure that info helped me get something to display on an OLED, and in a log, so I was happy to share it.

      Dave

      Reply

Leave a Comment

Download our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.