Seeking Some Clarification on I2C

So, I had a weird one today while coding using Arduino on an ESP32-S3.

The project I'm working on uses both I2C buses on the device, a screen on BUS0 and a temp sensor and energy monitor on BUS1

The library that the energy meter chip uses, is hard coded to use only the "Wire" object but I know from other parts of my code that the following is true:

Wire == BUS0 == I2C0
Wire1 == BUS1 == I2C1

I ran the sample program for the energy monitor (Who's libs are hard coded to use Wire), and was surprised to see that it picked up the monitor and started to read it without any extra config telling it that it was on a different bus.

My own code however, in my own project using the exact same library consistently failed to read (again due to the device being on BUS1 and the libs using BUS0)

My own code I now know was doing the correct thing.

the default "hardware" configuration on the boards we are using however have the default pins for I2C0 (8 & 9) set as the defaults for I2C1 and (15 & 16) as the defaults for I2C0

The library we assume was using "default" pins (We couldn't find anywhere in the lib source code where the pins for the I2C where assigned), since 8 & 9 are the default for I2C0, we further assumed the example sketch was able to work because Wire using what it thinks are normal defaults, was actually looking at I2C1 by default, but as there is no way, and it worked in terms of I2C the Arduino wire library's didn't question it, and set Wire up to point to BUS1 thinking it was talking to BUS0.

With regards to Wire setting itself up for defaults specifically on Arduino on ESP32-S3 doe anyone know how the default detection is performed and/or set-up, and more specifically is it possible to have Wire1 and Wire mapped the wrong way round?

We've fixed the problem for now by making a local copy of the library and including it directly in the project, then replacing all the "Wire" instances with "Wire1", I may even alter the lib to allow wire config to be passed in and submit a pull request for them, for now however I'd like to try and at least work out how a lib hard coded for I2C0 could see and access a device on I2C1

I don't have any answer for you, but just a question: why do you need two I2Cs?
It's a bus, so provided each device has its own address (and the wires aren't too long), there's no problem in using a single physical channel to connect to multiple devices.
Sorry, just curiosity...

1 Like

I don't know if this answers your question, but I'll share what I know.

I assume that the "library" may be "Wire" by:

At the end of the Wire.cpp:

TwoWire Wire = TwoWire(0);
#if SOC_HP_I2C_NUM > 1
TwoWire Wire1 = TwoWire(1);
#endif /* SOC_HP_I2C_NUM */

And the constructor of TowWire accepts the bus number, and will be ported by initPins():

TwoWire::TwoWire(uint8_t bus_num)
  : num(bus_num & 1), sda(-1), scl(-1), bufferSize(I2C_BUFFER_LENGTH)  // default Wire Buffer Size
    ,
    rxBuffer(NULL), rxIndex(0), rxLength(0), txBuffer(NULL), txLength(0), txAddress(0), _timeOutMillis(50), nonStop(false)
#if !CONFIG_DISABLE_HAL_LOCKS
    ,
    currentTaskHandle(NULL), lock(NULL)
#endif
#if SOC_I2C_SUPPORT_SLAVE
    ,
    is_slave(false), user_onRequest(NULL), user_onReceive(NULL)
#endif /* SOC_I2C_SUPPORT_SLAVE */
{
}

The actual pin assignments for I2C two signals SDA and SCL vary depending on the board type, even for the same ESP32S3 core.

For example, the XIAO ESP32S3 I have used has one I2C bus, which is defined in pins_arduino.h as follows:

static const uint8_t SDA = 5;
static const uint8_t SCL = 6;

In another example, Adafruit QT Py ESP32-S3, two I2C ports are defined in its pins_arduino.h:

static const uint8_t SDA = 7;
static const uint8_t SCL = 6;

#define WIRE1_PIN_DEFINED 1  // See Wire.cpp at bool TwoWire::initPins(int sdaPin, int sclPin)
static const uint8_t SDA1 = 41;
static const uint8_t SCL1 = 40;

We use 2 separate busses due to the nature of the product. One is routed to the normal "MCU Level" board and safe voltages, the other is routed to a secondary board that has High Voltages on it and bus isolation to prevent those dangerous voltages getting to the board where the user is poking buttons and screens :slight_smile:

Yes we use the Wire lib, I should perhaps have been a bit more clear...

"The Library we use" is a lib called "DrWattson" which knows how to read the electricity monitor chip we use, this chip as you can imagine has a direct connection to the mains electricity in order that it can monitor what is happening.

I'm aware that Wire.h can have various parameters sent to it to configure pin numbers etc, my question was more that the behaviour of the system was "odd" to say the least.

As I mentioned....

our "Electricity Monitor" is physically connected to BUS1 (Wire1 in Arduino), the library that reads the monitor chip (DrWattson) uses the "Wire" object NOT the "Wire1" object to access the monitor chip.

If I read the Arduino I2C documentation correctly, then the DrWattson library should NEVER have been able to see or even read the monitor chip, but it does and it does so using just the "Wire" object.

Our own code however, when we set "Wire" AND "Wire1" up correctly, then the DrWattson library behaves as we expect, it refuses to see and/or read the chip.

We suspect that there's some oddness going on with default pin assignments in the base libraries, we know that DrWattson does not set any pins it only uses "Wire.begin()" , so how is "Wire.begin()" with no set-up actually in reality pointing to "Wire1.begin()", is it possible to reassign WIre1 to be equal to just Wire, or reassign Wire to Wire1, I can see from the code snippets you have given above that "Wire" uses different pins depending on the selected device.. we use the generic "ESP32-S3 dev module" which uses ESP-IDF under the covers.

If I just set up a single I2C bus using "Wire.begin()" then what is it pointing to I2C0 or I2C1?

if it's I2C1 then I go and set up the second bus using "Wire1.begin()" does "Wire" then get moved from I2C1 to I2C0 or is "Wire1" pointed at I2C0 ?

Are the default pin assignments in the runtime, such that the runtime thinks it's talking to I2C0 when it's actually talking to I2C1?

as you can see, lot's of questions, but no apparent consistency in the runtime libs or device operation :slight_smile:

Cheers
Shawty

I assume you mean there are I2C devices in circuit with high voltage, not the bus itself. :flushed:

There is no BUS0, BUS1, I2C0, I2C1.
There are two I2C controllers that can be connected to any pins.
Wire will use the default pins unless otherwise specified
Wire1 will also use the same default pins unless otherwise specified

1 Like

Huh, still unclear to me, sorry. Are we talking about two Arduino boards, right? It's generally a good idea to separate high voltage sections from "user-managed" one, but I still can't get why you're planning this "double I2C" thing. Firstly and mainly because MCU-to-MCU communication could use a plain serial connection (why an I2C connection is needed?), and last but not least, I2C is commonly a "short range" connection (generally devices on the same board/box, and not more than a few inches away), so if that dangerous "High voltage" section is close to the "user interface" MCU, safety issues aren't solved by using an I2C wired.

And if you have some dangerous voltages potentially coming from the first board, it doesn't matter if you use I2C or serial, you need a better separation (together with a surge protection/arrester unit over any wire connecting the "user" to high voltage sections).

Yes indeed :slight_smile:

Ah Ha!

and that's what I was looking for clarification on.

and THAT explains why I was seeing the behaviour I was.

I was under the impression that Wire was always connected to I2C0/BUS0 and that Wire1 was always connected to I2C1/BUS1 irrespective of pin mapping, and that pin mapping basically only acted as the gateway to the bus that it was mapped to, via the mapped object.

However, as you've confirmed ... Wire CAN indeed be mapped to I2C1/BUS1 if need be just as Wire1 CAN be mapped to I2C0/BUS0 if required.

Cheers

No, we are on about one ESP32-S3 board, and 2 separate sub assembly's both of which have I2C devices on them.

One of the I2C busses is wired to one board, the other to the second board.

The board where the voltages are is properly isolated using optos from the I2C bus, and there is sufficient "gaps" in the PCB to prevent spark jumps.

One of the I2C devices however needs to be on the HOT side of the isolation so that it can measure the mains voltages present on the board, so the board with the voltages on goes HV-->I2C meter-->Opto-->Other I2C device-->MCU.

The boards have been professionally designed by a company that specialises in that kind of stuff.

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