RobTillaart / SHT31

Arduino library for the SHT31 temperature and humidity sensor

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

SHT31 library two I2C interfaces: example, advice needed

tipo1000A opened this issue · comments

Hi Rob,

I have a custom ATSAMD21G18A based board where I try to read 4 SHT31 sensors connected to 2 I2C interfaces.
I have had no luck with Adafruit_SHT31.h library reading both them (switching back and forth to read).

If I declare TwoWire myWire(&sercom5, 16, 17); , I can read only sensors in the second I2C interface.
If I leave out the declaration, I can read only sensors in default I2C interface.

Your library mentions:

  • begin(address, TwoWire *wire) for platforms with multiple I2C busses.

Does it mean I could accomplish the task of reading from 2 I2C interfaces with your library?
Could you give an example how to do it exactly? How to switch back on forth reading with 2 interfaces?

Thanks,
Tipo

Hi Tipo,

I will make a branch with a test example so we have a "playground to test" OK?

check out - https://github.com/RobTillaart/SHT31/tree/issue_20

it has an extra example SHT31_two_I2C.ino which is straightforward 4 sensors.
I wrote it for an ESP32 that has 2 Wire buses and it compiles, I have no sensors around to test it.

You need to adapt in the top the TwoWire myWire(&sercom5, 16, 17); , declaration
And rename Wire1 to myWire in two of the four Begin() calls.

Give it a try and please post the result of the compilation and/or if it works.

updated the example a bit so less editing is needed.

Added a second example using array's (to be tested if the first works.


note: zero experience with ATSAMD21G18A

just a thought
A possible workaround is using - https://github.com/RobTillaart/TCA9548

Found - https://learn.adafruit.com/using-atsamd21-sercom-to-add-more-spi-i2c-serial-ports/overview

more specific
https://learn.adafruit.com/using-atsamd21-sercom-to-add-more-spi-i2c-serial-ports/creating-a-new-wire

interesting part on that page

#include "wiring_private.h" // pinPeripheral() function
....
  // Assign pins 13 & 11 to SERCOM functionality
  pinPeripheral(11, PIO_SERCOM);
  pinPeripheral(13, PIO_SERCOM);

so after your

TwoWire myWire(&sercom5, 16, 17); 

you might need a reconfig of your pins too

#include "wiring_private.h" // pinPeripheral() function
...
  pinPeripheral(16, PIO_SERCOM);
  pinPeripheral(17, PIO_SERCOM);

other possible option is to use any other sercom. (just try all 6 to see which is free)

@tipo1000A
Any progress made?

@RobTillaart
Hi Rob,

I did try the solution/example you created (thanks), but it didn't seem to work. Then I had to dive to fix another issue on the same board. Now I have that done and I get back to "second I2C" issue.

I did double check with I2C scanner that second I2C I/F (sercom) is indeed working. I also checked with a scope that there is traffic ongoing on those I2C pins.

I'll post you better, more comprehensive info as I'm getting back to that now....

I have updated the branch, as I released 0..3.3 with a more robust heater code.
Should not change or disrupt the I2C investigation.

  1. Checking second I2C (SERCOM) functionality with I2C scanner (attached)

To make sure SERCOM I2C is working I connected SHT31 with address 0x44 to default I2C and SHT31 with address 0x45 to SERCOM I2C. I2C scanner output below seems to confirm both I2C’s are working.

I2C Scanner

Scanning myWire (sercom5 I2C)...
I2C device found at address 0x45  !
done

Scanning Wire (default I2C)...
I2C device found at address 0x44  !
done

I2C_scanner_SAMD21.ino.txt

  1. Having confirmed the above I added other SHT31 sensors' so that both I2C's have 0x44 and 0x45, and then I tried to run your sketch with couple of edits to match my board.

The only output was this:

SHT31_LIB_VERSION: 0.3.3

Checking with scope at the same time I didn't see any traffic in SERCOM I2C. In default I2C I saw traffic one time, then no traffic.
KUI_NO_LoRa_ROB_I2C_orig.ino.txt

  1. If I comment out this:
 b1 = sht_1.begin(SHT31_ADDRESS_1, &Wire);
  b2 = sht_2.begin(SHT31_ADDRESS_2, &Wire);
 // b3 = sht_3.begin(SHT31_ADDRESS_3, &myWire);
 // b4 = sht_4.begin(SHT31_ADDRESS_4, &myWire);

I get this output:

SHT31_LIB_VERSION: 	0.3.3
BEGIN:	1	1	0	0	
23.8	22.8	-45.0	-45.0	61.1	66.7	0.0	0.0
23.9	22.8	-45.0	-45.0	61.1	66.5	0.0	0.0
23.8	22.8	-45.0	-45.0	61.1	66.2	0.0	0.0
23.8	22.9	-45.0	-45.0	60.9	66.2	0.0	0.0
23.9	22.8	-45.0	-45.0	61.1	66.1	0.0	0.0

And if you commented out b1 and b2 the other pair worked?

With this:

  //b1 = sht_1.begin(SHT31_ADDRESS_1, &Wire);
  //b2 = sht_2.begin(SHT31_ADDRESS_2, &Wire);
  b3 = sht_3.begin(SHT31_ADDRESS_3, &myWire);
  b4 = sht_4.begin(SHT31_ADDRESS_4, &myWire);

The output is just this:

SHT31_LIB_VERSION: 0.3.3

So, didn't work...

rereading - https://learn.adafruit.com/using-atsamd21-sercom-to-add-more-spi-i2c-serial-ports/creating-a-new-wire

and looked at this datasheet - https://www.microchip.com/en-us/product/ATSAMD21G18

you use pin 16 and 17. Can you explain why you choose those two pins?

CHapter 7, page 28/29 of the datasheet does not mention pin 17 for the 21G column.

If I understand the theory correctly I would try sercom4 and pin 19, 20

image
(page 29 datasheet)

You might notice that pin 17 and 18 are skipped in that column.

OK found where the numbers 16 and 17 possibly come from.
image
(page 33 datasheet)

Looking up these in the MUX table.

image
(page 30 datasheet)

It looks like you need to use sercom3, and pins 25 and 26 ?

TwoWire myWire(&sercom3, 25, 26);

#include "wiring_private.h" // pinPeripheral() function
...
  pinPeripheral(25, PIO_SERCOM);
  pinPeripheral(26, PIO_SERCOM);

The history is that before I started to make the layout for my custom board I prototyped the use of physical pins #47 and #48 on the chip. I did that with I2C scanner and it was working (still is) and so I chose to use those pins. They were also most convenient to use from PCB layout perspective.

Physical pins #47 and #48 translate to I/O pins PB02 and PB03 respectively. From datasheet table 7-1.

image

image

The pin numbers to use with "pinPeripheral" (16 and 17) I picked up from variants.cpp file of MKRWAN1300. https://github.com/arduino/ArduinoCore-samd/blob/master/variants/mkrwan1300/variant.cpp

image

I thought I did everything right since I was able to ping sensors with myWire.beginTransmission(address);
error = myWire.endTransmission(); with I2C scanner. It must mean that the right "port" can be opened and physically it works, right?

Can you explain why I'm able ping sensors with myWire.beginTransmission(address);
error = myWire.endTransmission(); but can't actually communicate with them using library functions?

And thanks again for putting your time into this.

As far as I understand it looks like you have used the right info.

Can you give a link to the version of the datasheet you use?
This to be sure we use the same reference.

Can you explain why I'm able ping sensors with myWire.beginTransmission(address);
error = myWire.endTransmission(); but can't actually communicate with them using library functions?

No, normally that is the way one tests the connection,
the host sending the address which is acknowledged by the slave.
If the slave is removed the ping fails.

You might call myWire.setClock(100000); but that is the default normally.

Have you tried to communicate with another I2C device on that sercom5 device? EEPROM ?

If it is a bug in the library, a minimal sketch that uses just Wire calls, something like below should be able to detect it

#include "Arduino.h"
#include "Wire.h"
#include "wiring_private.h" // pinPeripheral() function

TwoWire myWire(&sercom5, 16, 17);

void setup()
{
  Serial.begin(115200);
  Serial.println("test");
  delay(100);

  myWire.begin();
  pinPeripheral(16, PIO_SERCOM);
  pinPeripheral(17, PIO_SERCOM);

  myWire.beginTransmission(44);
  int err = myWire.endTransmission(); 
  Serial.print("E:\t");
  Serial.println(err);

  int n = myWire.requestFrom(44,  (uint8_t) 6);
  Serial.print("N:\t");
  Serial.println(n);
  for (int i = 0; i < n; i++)
  {
    Serial.print("\t");
    Serial.println( myWire.read(), HEX);
  }
}

void loop()
{
}

updated pin numbers to 16 / 17 in code snippet

pinPeripheral(25, PIO_SERCOM);
pinPeripheral(26, PIO_SERCOM); // maybe SERCOM5 ???

nope

in https://github.com/arduino/ArduinoCore-samd/blob/master/variants/mkrwan1300/variant.cpp

line 268 or so states that sercom5 is already used for Serial1.
Could that be interfering?
What if you comment these lines ?

// Serial1
Uart Serial1(&sercom5, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX);

void SERCOM5_Handler()
{
Serial.IrqHandler();
}

did read

resulting in updating the example

#include "Arduino.h"
#include "Wire.h"
#include "wiring_private.h" // pinPeripheral() function

TwoWire myWire(&sercom5, 16, 17);

void setup()
{
  Serial.begin(115200);
  Serial.println("test");
  delay(100);

  myWire.begin();
  pinPeripheral(16, PIO_SERCOM_ALT);   // as sercom 5  uses for PAD0 PAD1 the sercom alt multiplexer.
  pinPeripheral(17, PIO_SERCOM_ALT);  // ++++++++++++++++++++++++++++++++++++++++

  myWire.beginTransmission(44);
  int err = myWire.endTransmission(); 
  Serial.print("E:\t");
  Serial.println(err);

  int n = myWire.requestFrom(44,  (uint8_t) 6);
  Serial.print("N:\t");
  Serial.println(n);
  for (int i = 0; i < n; i++)
  {
    Serial.print("\t");
    Serial.println( myWire.read(), HEX);
  }
}

void loop()
{
}

so if this works it is the PIO_SERCOM_ALT

  pinPeripheral(16, PIO_SERCOM_ALT);
  pinPeripheral(17, PIO_SERCOM_ALT); 

however that still does not explain why the I2C ping did work.

I have datasheet DS40001882F https://www.mouser.fi/datasheet/2/268/mchp_s_a0010084422_1-2274877.pdf

You might call myWire.setClock(100000); but that is the default normally.
This doesn't make a difference.

Have you tried to communicate with another I2C device on that sercom5 device? EEPROM ?
No, I haven't.

Trying this example:

#include "Arduino.h"
#include "Wire.h"
#include "wiring_private.h" // pinPeripheral() function

TwoWire myWire(&sercom5, 16, 17);

void setup()
{
  Serial.begin(115200);
  Serial.println("test");
  delay(100);

  myWire.begin();
  pinPeripheral(16, PIO_SERCOM_ALT);   // as sercom 5  uses for PAD0 PAD1 the sercom alt multiplexer.
  pinPeripheral(17, PIO_SERCOM_ALT);  // ++++++++++++++++++++++++++++++++++++++++

  myWire.beginTransmission(44);
  int err = myWire.endTransmission(); 
  Serial.print("E:\t");
  Serial.println(err);

  int n = myWire.requestFrom(44,  (uint8_t) 6);
  Serial.print("N:\t");
  Serial.println(n);
  for (int i = 0; i < n; i++)
  {
    Serial.print("\t");
    Serial.println( myWire.read(), HEX);
  }
}

void loop()
{
}

Results this:

E:	2
N:	0

If I comment out this in variant.cpp, the result is the same:

// Serial1
Uart Serial1(&sercom5, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX);

void SERCOM5_Handler()
{
Serial.IrqHandler();
}

So where does this leave us? I'm not educated enough in coding to say...

Only now I see this in the datasheet:
image

So those pins PB02 and PB03 was never meant to work as I2C...? But somehow pinging worked....?

If I have to make a new PCB layout, the pins (from the above list) I could use are preferably PA22 and PA23 (from layout perspective). Second option would be to use PA16 and PA17.

I could prototype with those pins before creating a new PCB layout. From what you understand they should work? Could you provide a test sketch to try out PA22 and PA23? I guess I could do it myself, but I don't trust myself on this right now...

(updated your post to get syntax highlighting - adding cpp after the three backquotes)

So where does this leave us? I'm not educated enough in coding to say...

For me this is also new territory, but I have the feeling we are closing in on the issue (which is outside the library context)

Results this:
E: 2
N: 0

Error code 2 means NOT connected => so it makes sense that it could not read the device.


Only now I see this in the datasheet:
image

So those pins PB02 and PB03 was never meant to work as I2C...? But somehow pinging worked....?

If I have to make a new PCB layout, the pins (from the above list) I could use are preferably PA22 and PA23 (from layout perspective). Second option would be to use PA16 and PA17.

I could prototype with those pins before creating a new PCB layout. From what you understand they should work? Could you provide a test sketch to try out PA22 and PA23? I guess I could do it myself, but I don't trust myself on this right now...

PA22/23 are pins 0 and 1 (based upon variants.cpp )
That would mean for the code to change to

#include "Arduino.h"
#include "Wire.h"
#include "wiring_private.h" // pinPeripheral() function

TwoWire myWire(&sercom5, 0, 1);  // <<<<<<<<<<<<<<<<<<<<

void setup()
{
  Serial.begin(115200);
  Serial.println("test");
  delay(100);

  myWire.begin();
  pinPeripheral(0, PIO_SERCOM_ALT);   // <<<<<<<<<<<<
  pinPeripheral(1, PIO_SERCOM_ALT);   // <<<<<<<<<<<<

  myWire.beginTransmission(44);
  int err = myWire.endTransmission(); 
  Serial.print("E:\t");
  Serial.println(err);

  int n = myWire.requestFrom(44,  (uint8_t) 6);
  Serial.print("N:\t");
  Serial.println(n);
  for (int i = 0; i < n; i++)
  {
    Serial.print("\t");
    Serial.println( myWire.read(), HEX);
  }
}

void loop()
{
}

Should be tested before making the PCB - very true...

Any progress made?

I think I'll have update on this on Monday 6.9.

Any progress?

Hi,

Using MKR WAN1310 board I tested SHT31 on pins PA22 (SDA) and PA23 (SCL) and it works.

TwoWire myWire(&sercom5, 0, 1);

  pinPeripheral(0, PIO_SERCOM_ALT);
  pinPeripheral(1, PIO_SERCOM_ALT);

I tested with one SHT31 (0x45) on this SERCOM5 interface. I still need to connect a sensor to default I2C I/F and make sure they both work in the same sketch.

I hooked up another SHT31 to default I2C and they both (default and SERCOM) work fine!

Thanks for putting your time and effort on this. In the end the whole thing was because I didn't read the spec carefully.

In the end the whole thing was because I didn't read the spec carefully.

Reading specs is hard, you are definitely not alone in that one.

Can you post your final sketch that works?
I would like to add it to the examples in the library.