ESP32 multiple I2C bus for using MCP23017 and DS3231

Dear community,
In my project I use a GPIO expander for a keypad, called MCP23017 and an external RTC module, called DS3231. For the MCP23017 I use arduino_keypads library (GitHub - joeyoung/arduino_keypads: arduino libraries for keypad interface on I2C bus), for the RTC module I use RTClib (GitHub - adafruit/RTClib: A fork of Jeelab's fantastic RTC Arduino library). Both of them works beautiful separately, the problem is when I use simultaneously. Both of the libraries use I2C communication on the default pins GPIO 21 (SDA) and GPIO 22 (SCL). My question is how to set the RTC module to use the second I2C bus of the ESP32(for example on GPIO 25, GPIO 26)?

I tried different solutions:

  1. I rewrite in the RTClib.h the "bool begin(TwoWire *wireInstance = &Wire);" to "bool begin(TwoWire *wireInstance = &I2Ctwo);"
  2. rtc.begin(&I2Ctwo)
  3. rtc.begin(0x57, &I2Ctwo) //0x57 is the address of the RTC module
  4. I tried to use the MCP23017 on GPIOs 25 and 26
    Nothing works, I'm stuck for a few days, any help would be great.

This is my code:

#include <Keypad_MC17.h>
#include <Wire.h>
#include <Keypad.h>
#include "RTClib.h"

#define I2CADDR 0x20

DS3231 myRTC;

TwoWire I2C1 = TwoWire(0); //I2C1 bus
TwoWire I2C2 = TwoWire(1); //I2C2 bus

const byte ROWS = 4; //four rows
const byte COLS = 4; //three columns
char keys[ROWS][COLS] = {
  {'1','2','3'},
  {'4','5','6'},
  {'7','8','9'},
  {'*','0','#'}
};
byte rowPins[ROWS] = {0, 1, 2, 3}; 
byte colPins[COLS] = {4, 5, 6, 7}; 

Keypad_MC17 keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS, I2CADDR );

void setup(){
  Serial.begin(57600);
  while( !Serial ){/*wait*/}  

  I2C1.begin(21, 22, 100000UL); 
  I2C2.begin(26, 25, 100000UL);  

  keypad.begin();
  rtc.begin(&I2C2);
  rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
}
  
void loop(){
  char key = keypad.getKey();
  if (key){
    Serial.println(key);
    rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
    DateTime now = rtc.now();
    Serial.print(now.year(), DEC);
    Serial.print('/');
    Serial.print(now.month(), DEC);
    Serial.print('/');
    Serial.print(now.day(), DEC);*/
    Serial.print(myRTC.getYear(), DEC);
    Serial.print(' ');
  }
}

Why use two I2C buses? As the name 'bus' suggests, it's a bus and you can connect different devices in parallel. The devices are distinguished by their address. Of course every connected device must have its own address. Usually you can set the address at the device ( it has a preset address that you usually use ). But that must be done even if only one device is connected.

2 Likes

As @MicroBahner said, the whole point of a bus is that you can have multiple devices on it.

Thank you for your answears. Can you please show how can I write my code to work the two library together on one bus(arduino_keypads library, RTClib )?
Because I nowhere find an example to figure it out.

My board is an ESP32 Devkit V1, with an ESP32-WROOM-32D module. Arduino framework, v2.1.0

Please show the code of both. It should be easy to combine them with both devices at the same bus. - Apart from other possible problems when combine two sketches, but that has nothing to do with the I2C bus.

1 Like

Code for using MCP23017:

#include <Keypad_MC17.h>
#include <Wire.h>
#include <Keypad.h>

#define I2CADDR 0x20

const byte ROWS = 4; //four rows
const byte COLS = 4; //three columns
char keys[ROWS][COLS] = {
  {'1','2','3','3'},
  {'4','5','6','3'},
  {'7','8','9','3'},
  {'*','0','#','3'}
};
byte rowPins[ROWS] = {0, 1, 2, 3}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {4, 5, 6, 7}; //connect to the column pinouts of the keypad

Keypad_MC17 keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS, I2CADDR );

void setup(){
  Serial.begin(9600);
  while( !Serial ){/*wait*/}       // for USB serial switching boards (MKR)
  Wire.begin( );                   // now needed
  keypad.begin( );
}
  
void loop(){
  char key = keypad.getKey();
  
  if (key){
    Serial.println(key);
  }
}

Code for using DS3231:

#include <Wire.h>
#include "RTClib.h"

RTC_DS3231 rtc;

void setup(){
  Serial.begin(57600);
  while( !Serial ){/*wait*/}  
  rtc.begin();
  rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
}
  
void loop(){
    DateTime now = rtc.now();
    Serial.print(now.year(), DEC);
    Serial.print('/');
    Serial.print(now.month(), DEC);
    Serial.print('/');
    Serial.print(now.day(), DEC);
    Serial.print(' ');
}

Where does that set the I2C address of the RTC?

If you ever will use the other I2C bus, then please keep in mind not to use these:

TwoWire I2C1 = TwoWire(0); // don't use this, it is something from the past.
TwoWire I2C2 = TwoWire(1); // don't use this, it is something from the past.

You can use "Wire" and "Wire1", because they are already defined.

void setup()
{
  Wire.begin();         // this starts the first I2C bus at 21, 22
  Wire1.begin(26, 25);  // this starts the second I2C bus
}

Use a single I2C bus for your project.
@awneil The DS3231 is at 0x68, that is probably in the library.

Combine the includes.

#include <Wire.h>
#include <Keypad_MC17.h>
#include <Keypad.h>
#include <RTClib.h>

Create an object for both:

Keypad_MC17 keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS, I2CADDR );

RTC_DS3231 rtc;

And add the global variables for the keypad of course.

Initialize both:

void setup()
{
  Wire.begin();
  keypad.begin();
  rtc.begin();
}

Tip: This is how to solve a problem. Keep it simple. Use the most normal and most common way that everyone uses. Follow the KISS rule: https://en.wikipedia.org/wiki/KISS_principle

It's not necesarry, when I run rtc.begin() it calls the Wire.begin() and it starts communication on the default I2C pins (SDA 21, SCL 22). Am I wrong?

Anyway, I ran the I2C scanner:

/*********
  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);          
}

It gives me this result:

12:57:57.593 -> I2C Scanner

12:57:57.593 -> Scanning...

12:57:57.624 -> I2C device found at address 0x57

12:57:57.624 -> I2C device found at address 0x68

12:57:57.624 -> done

So somehow my DS3231 returns 2 I2C address.

Please be more specific :nerd_face: What was connected to the I2C bus when you did the I2C Scanner ? Only the RTC module ? There is probably a EEPROM on that module.

No, you are not wrong. Some libraries do a Wire.begin(), but I don't like that :woozy_face:
Perhaps not all the libraries that you use do that, perhaps you are going to add a library someday that does not do that. So it is best to start with Wire.begin() to start the I2C bus, and then keypad.begin() and rtc.begin() in any order.

I learned everything about the ESP32 from https://randomnerdtutorials.com/.
But he hesitates to fix mistakes. The wrong "TwoWire I2C1 = TwoWire(0);" is still on his website. The I2C Scanner does still have "else if (error==4) {" which has no meaning for the ESP32 and has no meaning anymore for any other Arduino board. I noticed a bug, with wrong resistor values here (it should be 100k and 220k instead of 100Ω and 220Ω) but my message was rejected and he did not fix the resistor values.

Thanks broo, it's working. I don't why I complicated so much.

#include <Keypad_MC17.h>
#include <Wire.h>
#include <Keypad.h>
#include "RTClib.h"

#define I2CADDR 0x20

RTC_DS3231 rtc;

const byte ROWS = 4; //four rows
const byte COLS = 4; //three columns
char keys[ROWS][COLS] = {
  {'1','2','3'},
  {'4','5','6'},
  {'7','8','9'},
  {'*','0','#'}
};
byte rowPins[ROWS] = {0, 1, 2, 3}; 
byte colPins[COLS] = {4, 5, 6, 7}; 

Keypad_MC17 keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS, I2CADDR );

void setup(){
  Serial.begin(57600);
  while( !Serial ){/*wait*/}  

  Wire.begin();
  keypad.begin();
  rtc.begin();

  rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));

}
  
void loop(){
  char key = keypad.getKey();
  if (key){
    Serial.println(key);
    DateTime now = rtc.now();
    Serial.print(now.year(), DEC);
    Serial.print('/');
    Serial.print(now.month(), DEC);
    Serial.print('/');
    Serial.print(now.day(), DEC);
    Serial.print(' ');
  }
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.