Issue using both i2c buses (Wire and Wire1) in same program [Solved]

I have a frustratingly simple problem. I’m using the Particle Electron and there are two i2c buses (Wire and Wire1). I have two TCS34725 color sensors (one on each bus). I’m trying to read from the first sensor and then the second. When I run the code below, I get the same value printed for both sensor 1 and 2. More specifically both serial prints (sensor 1 and sensor 2) return the value read on sensor 1 (Wire)

Some notes:

  • Sensor 1 and sensor 2 are identical functions apart from Wire being changed to Wire1

  • If I break off the sensor function into it’s own program I can toggle between Wire and Wire1 and the sensors read as expected. It’s only the combination that seems to give me problems

  • I am re-initializing the sensor every read. I did that in a final attempt of “well if this doesn’t work”, once I figure out the issue, I’ll move it to setup.

  • I scanned the i2c ports to double check the address of the TCS34725 is the same on both buses

  • We have a spun board so I know the wiring is correct

I’ve included the code below. I’d appreciate any help!

#include <application.h>
#include <spark_wiring_i2c.h>

// TCS34725 I2C address is 0x29(41)
#define Addr 0x29

void setup()
{
    Particle.disconnect();
    // Initialise Serial Communication, set baud rate = 9600
    Serial.begin(9600);
}

void sensor1()
{
  unsigned int data[8];
  int red = 0, green = 0, blue = 0, cData = 0;
  // Initialise I2C communication as MASTER
  Wire.begin();


  // Start I2C Transmission
  Wire.beginTransmission(Addr);
  // Select Wait Time register
  Wire.write(0x83);
  // Set wait time = 2.4 ms
  Wire.write(0xFF);
  // Stop I2C Transmission
  Wire.endTransmission();

  // Start I2C Transmission
  Wire.beginTransmission(Addr);
  // Select Atime register
  Wire.write(0x81);
  // Atime = 700 ms, max count = 65536
  Wire.write(0x00);
  // Stop I2C Transmission on the device
  Wire.endTransmission();

  // Start I2C Transmission
  Wire.beginTransmission(Addr);
  // Select control register
  Wire.write(0x8F);
  // AGAIN = 1x
  Wire.write(0x00);
  // Stop I2C Transmission
  Wire.endTransmission();

  // Start I2C Transmission
  Wire.beginTransmission(Addr);
  // Select enable register
  Wire.write(0x80);
  // Power ON, RGBC enable, wait time disable
  Wire.write(0x03);
  // Stop I2C Transmission
  Wire.endTransmission();
  delay(800);

  // Start I2C Transmission on the device
  Wire.beginTransmission(Addr);
  // Select data register
  Wire.write(0x94);
  // Stop I2C Transmission on the device
  Wire.endTransmission();

  // Request 8 byte of data from the device
  Wire.requestFrom(Addr, 8);

  // Read 8 bytes of data
  // cData lsb, cData msb, red lsb, red msb, green lsb, green msb, blue lsb, blue msb
  if (Wire.available() == 8)
  {
    data[0] = Wire.read();
    data[1] = Wire.read();
    data[2] = Wire.read();
    data[3] = Wire.read();
    data[4] = Wire.read();
    data[5] = Wire.read();
    data[6] = Wire.read();
    data[7] = Wire.read();
  }
  Wire.endTransmission();

  // Convert the data
  cData = (data[1] * 256) + data[0];
  red = (data[3] * 256) + data[2];
  green = (data[5] * 256) + data[4];
  blue = (data[7] * 256) + data[6];

  Serial.println(cData);
  Serial.println(red);
  Serial.println(green);
  Serial.println(blue);
  Serial.println("Wire");


}

void sensor2()
{
  unsigned int data[8];
  int red = 0, green = 0, blue = 0, cData = 0;
  // Initialise I2C communication as MASTER
  Wire1.begin();


  // Start I2C Transmission
  Wire1.beginTransmission(Addr);
  // Select Wait Time register
  Wire1.write(0x83);
  // Set wait time = 2.4 ms
  Wire1.write(0xFF);
  // Stop I2C Transmission
  Wire1.endTransmission();

  // Start I2C Transmission
  Wire1.beginTransmission(Addr);
  // Select Atime register
  Wire1.write(0x81);
  // Atime = 700 ms, max count = 65536
  Wire1.write(0x00);
  // Stop I2C Transmission on the device
  Wire1.endTransmission();

  // Start I2C Transmission
  Wire1.beginTransmission(Addr);
  // Select control register
  Wire1.write(0x8F);
  // AGAIN = 1x
  Wire1.write(0x00);
  // Stop I2C Transmission
  Wire1.endTransmission();

  // Start I2C Transmission
  Wire1.beginTransmission(Addr);
  // Select enable register
  Wire1.write(0x80);
  // Power ON, RGBC enable, wait time disable
  Wire1.write(0x03);
  // Stop I2C Transmission
  Wire1.endTransmission();
  delay(800);

  // Start I2C Transmission on the device
  Wire1.beginTransmission(Addr);
  // Select data register
  Wire1.write(0x94);
  // Stop I2C Transmission on the device
  Wire1.endTransmission();

  // Request 8 byte of data from the device
  Wire1.requestFrom(Addr, 8);

  // Read 8 bytes of data
  // cData lsb, cData msb, red lsb, red msb, green lsb, green msb, blue lsb, blue msb
  if (Wire1.available() == 8)
  {
    data[0] = Wire1.read();
    data[1] = Wire1.read();
    data[2] = Wire1.read();
    data[3] = Wire1.read();
    data[4] = Wire1.read();
    data[5] = Wire1.read();
    data[6] = Wire1.read();
    data[7] = Wire1.read();
  }
  Wire1.endTransmission();

  // Convert the data
  cData = (data[1] * 256) + data[0];
  red = (data[3] * 256) + data[2];
  green = (data[5] * 256) + data[4];
  blue = (data[7] * 256) + data[6];

  Serial.println(cData);
  Serial.println(red);
  Serial.println(green);
  Serial.println(blue);
  Serial.println("Wire1");
}

void loop()
{
  //sensor1();
  sensor2();
  sensor1();//This one works... sensor2 doesn't
  while(Serial.read()!=116){}
}

What system version are you running?
Try 0.7.0-rc.3

@ScruffR

  • I had been using 0.6.2 I recompiled with 0.7.0-rc3 still having the same problem. Just as sanity on how I did this at the bottom of the IDE I clicked the tag and changed from 0.6.2 to 0.7.0-rc3. When I flashed my code the Electron blinked magenta for a bit (updating firmware) then ran as expected.

  • I also commented out #include <spark_wiring_i2c.h> as that should be covered in <application.h> (at least it didn’t give me any compile or additional run issues :slight_smile:)

  • I added an error check on the endTransmission if(Wire.endTransmission()!=0){Serial.println("error");} before //Convert the data to see if something wasn’t getting properly terminated… no issues there.

Web IDE won’t update the system of your device OTA.
You need to use CLI to upgrade an Electron by flashing the respective binaries.

@ScruffR
Okay I downloaded the binaries from the link you provided and ran the following from the command line (including tinker)

Then I recompiled my program with the appropriate tag 0.7.0-rc.3 (selected in the IDE) and flashed… now “Wire1” doesn’t work even when separated out into its own program. When I say doesn’t work I mean that previously if I separated Wire and Wire1 into separate programs, they would read from the appropriate (respective) sensor. Now Wire and Wire1 read from the same sensor (the one previously assigned to Wire) regardless of whether they are separated into their own program.

That is quite odd then.
Maybe @rickkas7 can provide some more insight - I’ll have to check with one of my Electrons

the docs suggest that there is one I2C interface:

with two possible configurations...

These pins are used via the Wire object.
SCL => D1
SDA => D0
Additionally on the Electron, there is an alternate pin location for the I2C interface, which can be used via the Wire1 object. This alternate location is mapped as follows:
SCL => C5
SDA => C4

True, but these should still be both usable in time multiplex.

perhaps, but the docs scornfully remind us that

Note: Because there are multiple I2C locations available, be sure to use the same Wire or Wire1 object with all associated functions. I.e.,Do NOT use Wire.begin() with Wire1.write();Do use Wire1.begin() with Wire1.transfer();

This suggests to me that there is something looming over this issue. You don't see that warning anywhere in the Serial discussion, for example.

Anyways, I2C multiplexers are cheap. :wink:

I’d read that - but might still be wrong :blush: - as: “Don’t be surprised that Wire.write() won’t work if you initialised with Wire1.begin(), since that switched the HW interface over to the alternative pins so you won’t have it attached to the Wire pins (unless you switch it back with Wire.begin())”

1 Like

yes, sort of like OP has done in his test code...

Exactly, hence my puzzled look :hushed: and this discussion when it didn’t work as expected :wink:

1 Like

I’m going to go with that you can’t use both Wire and Wire1 in the same program. The reason is likely that both sets of pins are connected to the same I2C block in the STM32F205 processor I2C1.

Here’s my test circuit. There are two DS75 I2C temperature sensors, connected to Wire (D0/D1) and Wire1 (C4/C5).

And this is my test program:

#include "Particle.h"
#include "DS75-RK.h"

SYSTEM_MODE(MANUAL);

DS75 sensor1(Wire, 0);
DS75 sensor2(Wire1, 0);

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

	sensor1.begin();
	sensor2.begin();
}

void loop() {
	Serial.printlnf("sensor1: %f", sensor1.getTemperature());
	Serial.printlnf("sensor2: %f", sensor2.getTemperature());
	delay(1000);
}

They always read the same, even if I put my finger on only one of them to change the temperature of one.

sensor1: 25.500000
sensor2: 25.500000
sensor1: 27.500000
sensor2: 27.500000

If I comment out one or the other, they work correctly individually, but not if I use both.

I’d just use a TCA9548A multiplexer.

3 Likes

@ScruffR @BulldogLowell @rickkas7 First off thanks for the feedback. Good to know that I’m not crazy for something that seemed super simple.

So here’s my problem, our group has already spun a board so updating the hardware isn’t an easy option. It’s a pretty big deal for us to change our board at this point (we’re small and on a tight budget). Is there anyone at Particle I could possibly DM to find out definitively whether the hardware can or cannot support writing to Wire and Wire1 in the same program?

…unfortunately if the Particle hardware can’t support it, we’ll have to bite the bullet. I’d just really like to know for sure before I try and broach that conversation. Please feel free to DM me and thank you again for your help!

Here is the relevant code in firmware/hal/src/stm32f2xx/i2c_hal.c and the comment seems very definitive:

void HAL_I2C_Begin(HAL_I2C_Interface i2c, I2C_Mode mode, uint8_t address, void* reserved)
{
    STM32_Pin_Info* PIN_MAP = HAL_Pin_Map();

#if PLATFORM_ID == 10
    /*
     * On Electron both I2C_INTERFACE1 and I2C_INTERFACE2 use the same peripheral - I2C1,
     * but on different pins. We cannot enable both of them at the same time.
     */
    if (i2c == HAL_I2C_INTERFACE1 || i2c == HAL_I2C_INTERFACE2) {
        HAL_I2C_Interface dependent = (i2c == HAL_I2C_INTERFACE1 ? HAL_I2C_INTERFACE2 : HAL_I2C_INTERFACE1);
        if (HAL_I2C_Is_Enabled(dependent, NULL) == true) {
            // Unfortunately we cannot return an error code here
            return;
        }
    }
#endif

The documentation in this should be much more clearer than what it is… you can’t use both at the same time!

@bko, if this was written pre Einstein we wouldn’t need to argue :wink:
But what exactly is “at the same time”?

This would definetly go as “same time” (for our frame of reference)

  Wire.begin();
  Wire1.begin();
  Wire.write(x);
  Wire1.write(y);

But how about this?

  Wire.begin();
  Wire.write(x);
  Wire.end();     // can't see the end() call in any of the tests above

  Wire1.begin();
  Wire1.write(y);
  Wire1.end();    // can't see the end() call in any of the tests above
1 Like

I haven’t come round to testing this myself yet, but can you try adding Wire.end()/Wire1.end() calls to your tests?

Maybe the bug is that the pinmap is cached and does not update when you call Wire1.begin()?

Okay, it works! Looks like you need both Wire.end (which as I understand it releases the pins and allows them to be used for subsequent I/O) and to reassign the Pins before the next Wire1.begin. I pasted the working function below for others future reference!

Thanks again for the help!

void sensor1()
{
  unsigned int data[8];
  int red = 0, green = 0, blue = 0, cData = 0;
  // Initialise I2C communication as MASTER
  Wire.begin();
  // Start I2C Transmission
  Wire.beginTransmission(Addr);
  // Select Wait Time register
  Wire.write(0x83);
  // Set wait time = 2.4 ms
  Wire.write(0xFF);
  // Stop I2C Transmission
  Wire.endTransmission();

  // Start I2C Transmission
  Wire.beginTransmission(Addr);
  // Select Atime register
  Wire.write(0x81);
  // Atime = 700 ms, max count = 65536
  Wire.write(0x00);
  // Stop I2C Transmission on the device
  Wire.endTransmission();

  // Start I2C Transmission
  Wire.beginTransmission(Addr);
  // Select control register
  Wire.write(0x8F);
  // AGAIN = 1x
  Wire.write(0x00);
  // Stop I2C Transmission
  Wire.endTransmission();

  // Start I2C Transmission
  Wire.beginTransmission(Addr);
  // Select enable register
  Wire.write(0x80);
  // Power ON, RGBC enable, wait time disable
  Wire.write(0x03);
  // Stop I2C Transmission
  Wire.endTransmission();
  delay(800);

  // Start I2C Transmission on the device
  Wire.beginTransmission(Addr);
  // Select data register
  Wire.write(0x94);
  // Stop I2C Transmission on the device
  Wire.endTransmission();

  // Request 8 byte of data from the device
  Wire.requestFrom(Addr, 8);

  // Read 8 bytes of data
  // cData lsb, cData msb, red lsb, red msb, green lsb, green msb, blue lsb, blue msb
  if (Wire.available() == 8)
  {
    data[0] = Wire.read();
    data[1] = Wire.read();
    data[2] = Wire.read();
    data[3] = Wire.read();
    data[4] = Wire.read();
    data[5] = Wire.read();
    data[6] = Wire.read();
    data[7] = Wire.read();
  }

  if(Wire.endTransmission()!=0){Serial.println("error");}
  Wire.end();
  while(Wire.isEnabled()==TRUE){Serial.println("Wire hasn't been released"); Serial.println(Wire.isEnabled()); delay(10000);}

  pinMode(C4, INPUT);
  pinMode(C5, INPUT);
  pinMode(D0, INPUT);
  pinMode(D1, INPUT);

  // Convert the data
  cData = (data[1] * 256) + data[0];
  red = (data[3] * 256) + data[2];
  green = (data[5] * 256) + data[4];
  blue = (data[7] * 256) + data[6];

  Serial.println(cData);
  Serial.println(red);
  Serial.println(green);
  Serial.println(blue);
  Serial.println("Wire");
}
3 Likes