Skip to content

Changing I2C pins on the fly (Question/suggestion, not a bug) #5669

Closed
@leifclaesson

Description

@leifclaesson

Hi!

On an already finished custom PCB based on the WROOM-32U module, I realized I needed another I2C bus.
I'm already using one high-speed I2C bus locally on the PCB for an OLED display, and the second I2C bus is brought out to headers for use to interface with external I/O expander boards with relays, also custom.
It's not a matter of running out of addresses -- with too many external boards the signalling just became unreliable -- the fact that these boards live in an electrical box around lots of 240 volt wiring might have something to do with it.

I have plenty of other pins brought out to headers, so I looked for a way to change TwoWire(0) or TwoWire(1) pins on the fly, and was rather surprised that there wasn't a way!

So, I dug into the Arduino Core source code and found esp32-hal-i2c.c and after some analysis, I wrote the following new function into the aforementioned file:

void i2cSetPins(uint8_t i2c_num, uint8_t sda, uint8_t scl)
{
    if(i2c_num > 1) {
        return;
    }

    i2c_t * i2c = &_i2c_bus_array[i2c_num];

    if(i2c->sda >= 0){
        i2cDetachSDA(i2c, i2c->sda);
    }
    if(i2c->scl >= 0){
        i2cDetachSCL(i2c, i2c->scl);
    }
    i2c->sda = sda;
    i2c->scl = scl;

    if(i2c->sda >= 0){
        i2cAttachSDA(i2c, i2c->sda);
    }
    if(i2c->scl >= 0){
        i2cAttachSCL(i2c, i2c->scl);
    }
}

With this function, I can change the I2C pins before calling the normal Wire functions, and change them back again afterwards.

Let me make absolutely clear, it works perfectly and it solved my problem.

My questions are:

  1. Is it a bad idea to change the pins on the fly like this, using this method? If so, why, and what would be a better solution to use more pins and have more I2C buses on an esp32?
  2. If it's not a bad idea, should it be officially supported without needing to modify the arduino core files?

As the code is written now, although you could call the i2cAttach/Detach functions from your own code, you don't have access to the i2c_dev_t * pointer unless you subclass TwoWire and save it from the i2cInit return value, and even if you do that, the i2c_dev_t and related definition such as enums are all private so you still couldn't change the sda/scl pins in the structure.

So, I tried simply modifying my copy, and it works beautifully. The lack of access to the i2c_dev_t pointer is why I simply used the i2c_num value (0 or 1), if I'm hacking the core code anyway why bother making it pretty.

Is there a better way? I'd be surprised if I was the last/only person to need more i2c buses by pin reassignment even if it's probably not all that common.

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions