Using multiple i2c devices with adafruit sensors library

I have a project with 1 BME680 (temp, humidity, gas, pressure sensor) and two SHCT3 temp/humidity sensors. As the SHCT3 have neither cs_ nor programmable addressing, I have to use two i2c connections - 1st with 1 SHCT3 and the BME680, and the second bus for the second SHCT3. The host board is an Arduino MKR1000.

Adafruit provide nice libraries for each sensor which assume the standard Wire interface which is fine for i2c bus1. I have found SlowSoftWire which is a Wire like wrapper for SlowSoftI2CMaster - a bit bashed library for I2C which I proposed to used for bus2.

I blithely thought I could use the Adafruit code directly like this:

SlowSoftWire wire2=SlowSoftWire(6,7);// create bit bashed wire interface SDA2 on 6 SK2 on 7
if (! shtc3.begin(wire2)) {
Serial.println("Couldn't find SHTC3");
while (1) delay(1);

But the compiler errors with:

SHTC3test:23:26: error: no matching function for call to 'Adafruit_SHTC3::begin(SlowSoftWire&)'

I interpret that its expecting a Wire object, but getting a SlowSoft Wire object. In the Adafruit library the function def is:

bool begin(TwoWire *theWire = &Wire);

This is total Greek to me :slight_smile:

Given that the wrapper is there to make the library look like a Wire library, I assume that there is some way to allow my software i2c to work with the Adafruit library? However, its gone beyond my expertise to work out how. Maybe there is a simpler approach altogether, idk :slight_smile:

Many thanks for any insight,

Kevin.

That is indeed a problem. Not every compatible library can be used with a pointer to the object and the Wire class does not allow another template. At this moment, a common good solution that everyone agrees on is not even in sight :cry:

Which SlowSoftWire did you find ?
This one ? https://github.com/felias-fogg/SlowSoftWire. I looked at it before and put in my list.
That one also is part of the Stream family, but sometimes a sensor library has a reference to "TwoWire". The SlowSoftWire is not "TwoWire".
Perhaps you can fix that and replace TwoWire with SlowSoftWire. Perhaps you need to change the whole sensor library.

If I change the adafruit library to accept the other object, then presumably I'd have to have two distinct libraries, one for each type? This seems rather wasteful.

I guess I could modify the library internally with new methods, but it will need restructuring, I think. I was hoping to avoid that, but I guess I'm going to have to take a deep breath! My OO skills are still weak.

Yes its that library,btw.

The MKR1000 is a SAMD21 board. Check out Adafruit's tutorial on instantiating additional SERCOM interfaces on that chip. You should be able to add another Hardware I2C interface:
https://learn.adafruit.com/using-atsamd21-sercom-to-add-more-spi-i2c-serial-ports?view=all

Then, create a new instance of the TwoWire class using the new interface.

2 Likes

Hey thats really cool. Looks like with a bit of a re-wire, that should be able to work, and without any hacks - this also explains the 'twowire' type in the code I was looking at. A bit hairy looking in the guide, but I hope I can navigate that OK.

OK unstuck by the 'greek' sytnax in the SHCT library :frowning:

bool begin(TwoWire *theWire = &Wire); <---- the line I dont understand the syntax of..

I tried:
pinPeripheral(4, PIO_SERCOM_ALT);
pinPeripheral(3, PIO_SERCOM_ALT);
TwoWire com2(&sercom2, 4, 3);
if (! shtc3.begin(com2)) {

as per the guide. But got the error:

.....\Arduino\libraries\Adafruit_SHTC3-master/Adafruit_SHTC3.h:100:8: note: candidate: bool Adafruit_SHTC3::begin(arduino::TwoWire*)
bool begin(TwoWire theWire = &Wire);
^~~~~
.....\Arduino\libraries\Adafruit_SHTC3-master/Adafruit_SHTC3.h:100:8: note: no known conversion for argument 1 from 'arduino::TwoWire' to 'arduino::TwoWire
'
exit status 1
no matching function for call to 'Adafruit_SHTC3::begin(arduino::TwoWire&)'

so I tried &com2 - which compiles but crashed the arduino. so is obviously not right

Which goes to show that I don't understand the syntax being employed here.. why does it say it wants a (I think) pointer, but barfs when I try that?

Here's the whole code for completeness - its the AF test example modfified:

#include "Adafruit_SHTC3.h"
#include <Wire.h>
#include "wiring_private.h" // pinPeripheral() function
Adafruit_SHTC3 shtc3 = Adafruit_SHTC3();

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

while (!Serial)
delay(10); // will pause Zero, Leonardo, etc until serial console opens

Serial.println("SHTC3 test");

pinPeripheral(4, PIO_SERCOM_ALT);
pinPeripheral(3, PIO_SERCOM_ALT);
TwoWire com2(&sercom2, 4, 3);
if (! shtc3.begin(&com2)) {
Serial.println("Couldn't find SHTC3");
while (1) delay(1);
}
Serial.println("Found SHTC3 sensor");
}

void loop() {
sensors_event_t humidity, temp;

shtc3.getEvent(&humidity, &temp);// populate temp and humidity objects with fresh data

Serial.print("Temperature: "); Serial.print(temp.temperature); Serial.println(" degrees C");
Serial.print("Humidity: "); Serial.print(humidity.relative_humidity); Serial.println("% rH");

delay(1000);
}

That would be the correct syntax. My guess is that you didn't configure the SERCOM properly.

Another approach if you don't need the values updated at highest possible speed you could use a I2C-bus multiplexer like this:

This I2C-bus multilexer has 8 channels and 3 adresspins so a maximum of 64 devices can be accessed

The multiplexer is connected to the I2C-pins of the microcontroller
and the sensors are connected to one of the 8 buses

selecting one of the 8 buses is as cheap as this

void TCA9548A(uint8_t p_busNr)
{
  Wire.beginTransmission(0x70);  // TCA9548A address is 0x70
  Wire.write(1 << p_busNr);          // send byte to select bus
  Wire.endTransmission();
}

and after that you just talk to the sensor as the sensor would be connected directly to the I2C-pins
best regards Stefan

1 Like

The debug port reports the 1st line, but no other text is produced - it should say either it found or didn't find the sensor, so I think it crashed?

Perhaps. But, how would anyone know? You haven't posted the complete code.

Yes I did - 7 above..

Your code proposes to use MKR1000 pins 3 and 4 for the new I2C interface. Per 'variant.cpp' for this board, those are pins PA11 and PB10 on the SAMD21 chip. You've selected SERCOM_ALT as the multiplexer setting for both.

Going to Table 7-1 in the SAMD21 datasheet, you'll see that SERCOM_ALT for PA11 selects SERCOM2/PAD[3] and SERCOM_ALT for PB10 selects SERCOM4/PAD[2].

That's obviously not going to work.

Also, your code is trying to use SERCOM2 for the new I2C. That one is already used for the existing I2C interface (i.e. Wire).

Also, your code creates the new TwoWire instance (com2) as a local stack variable inside setup(). It will cease to exist once that function exits. Make it global.

Ah right ! I expected the settings to be the same as in the guide - silly me !

Digesting the pin descriptions is proving tricky. There seem to be some inconsistencies between the comments in the variants.cpp and the .h, although likely its my interpretation..

In the variant .h, for instance:

but in the variants. cpp:

comments table:

!
and code:

In the .h, the default SPI port is shown as Sercom1
In the comments of the variant.cpp, pins 8&9 sercom1 is not alt, and sercom 3 is alt and non_alt is 'starred' (consistent with .h)
Then in the code it says Sercom_ALT, and the comment at the end of the line says SERCOM4

I'm confused!

Same confusions for SPI wire
Sercom 2 in .h
Sercom 0 starred on pins 11,12 in .cpp comment table
no definition in the table for those pins.

Can you explain how I should interpret this so I can determine which Sercoms are available.

Thanks for pointing out my mistakes. Its really appreciated - this is getting deeper than I'm used to going, and I'm rusty after not doing arduino stuff for a few years.

Looks consistent to me. The SPI object will use SERCOM1 and pins 8, 9, 10 on the MRK1000:

#define PIN_SPI_MISO  (10u)
#define PIN_SPI_MOSI  (8u)
#define PIN_SPI_SCK   (9u)
#define PIN_SPI_SS    (24u)
#define PERIPH_SPI    sercom1

Those are SAMD21 pins PA16, PA17, PA19:

 +------------+------------------+--------+-----------------+--------+-----------------------+---------+---------+--------+--------+----------+----------+
 | Pin number |  MKR  Board pin  |  PIN   | Notes           | Peri.A |     Peripheral B      | Perip.C | Perip.D | Peri.E | Peri.F | Periph.G | Periph.H |
 |            |                  |        |                 |   EIC  | ADC |  AC | PTC | DAC | SERCOMx | SERCOMx |  TCCx  |  TCCx  |    COM   | AC/GLCK  |
 |            |                  |        |                 |(EXTINT)|(AIN)|(AIN)|     |     | (x/PAD) | (x/PAD) | (x/WO) | (x/WO) |          |          |
 +------------+------------------+--------+-----------------+--------+-----+-----+-----+-----+---------+---------+--------+--------+----------+----------+
 |            |       SPI        |        |                 |        |     |     |     |     |         |         |        |        |          |          |
 | 08         | MOSI             |  PA16  |                 |  *00   |     |     | X04 |     |  *1/00  |   3/00  |*TCC2/0 | TCC0/6 |          | GCLK_IO2 |
 | 09         | SCK              |  PA17  |                 |  *01   |     |     | X05 |     |  *1/01  |   3/01  | TCC2/1 | TCC0/7 |          | GCLK_IO3 |
 | 10         | MISO             |  PA19  |                 |   03   |     |     | X07 |     |  *1/03  |   3/03  |* TC3/1 | TCC0/3 | I2S/SD0  | AC/CMP1  |
 +------------+------------------+--------+-----------------+--------------------+-----+-----+---------+---------+--------+--------+----------+----------+

Their MUXes are set to PIO_SERCOM:

  { PORTA, 16, PIO_SERCOM,  (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER    ), No_ADC_Channel, PWM2_CH0,   TCC2_CH0,     EXTERNAL_INT_0    }, // MOSI: SERCOM1/PAD[0]
  { PORTA, 17, PIO_SERCOM,  (PIN_ATTR_DIGITAL                                ), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_1    }, // SCK:  SERCOM1/PAD[1]
  { PORTA, 19, PIO_SERCOM,  (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER    ), No_ADC_Channel, PWM3_CH1,   TC3_CH1,      EXTERNAL_INT_NONE }, // MISO: SERCOM1/PAD[3]

And, if you check in Table 7-1, you'll see that the SERCOM setting for these pins configures them for various pads of SERCOM1.

Finally, the SPI object is instantiated:

SPIClass SPI (&PERIPH_SPI,  PIN_SPI_MISO,  PIN_SPI_SCK,  PIN_SPI_MOSI,  PAD_SPI_TX,  PAD_SPI_RX);

All perfectly consistent.

OK - so what was confusing was the pin numbering - in the table the third column is the pin that should be looked up in the code below. Thanks. So PA16 is PortA,16. That makes more sense ( told you it was my interpretation :slight_smile: )

That means there's still an error in the variant cpp. For Sercom 2, the comment at the end of the line says sercom4 incorrectly.

I didn't see where the instantiation code comes from. Which file is that?

So far I have this:

Sercom Use Ard. Pins Comment
0 none? A3,A4 could use alt pins?
1 SPI 8,9,10 Used for TFT
2 Wire 11,12 Used for default I2C interface
3 none? 0,1 will this interfere with USB ?
4 Wifi 26,27,29 SPI to the onboard Wifi chip
5 UART 13,14 TxD/RxD Uart connections

I'm concerned that SERCOM 3 if activated will interfere with the USB port which has some of the pins. I need pad 0 and 1, but 2 and 3 are used for USB.

[Edit:] I just noticed that pins 0 and 1 are also connected to the TFT module so 3 is out too!

On my hardware, the touch sensor of the display is connected to pin A4 and the CS of the TFT is on pin A3 ruling out Sercom 0- this is a PCB connection I cant change without hacking tracks :frowning:

So this would mean none of the ports are going to work for me. -sigh-

( I should point out that the MKR1000 is mounted on a wall mounted touch display panel by Zihatec. The board provides an enclosure, and mounts the arduino plus a shield plus a TFT and provides a beeper and backlight switch, supply conditioning etc.)

SPI.h:

#if SPI_INTERFACES_COUNT > 0
  extern SPIClass SPI;
#endif

SPI.cpp:

  SPIClass SPI (&PERIPH_SPI,  PIN_SPI_MISO,  PIN_SPI_SCK,  PIN_SPI_MOSI,  PAD_SPI_TX,  PAD_SPI_RX);
1 Like

Thanks again for that. Looks like, despite you kind efforts, I'm back to bit bashed I2C and having to modify or duplicate the Adafruit sensor library.

I learnt alot on the way though, so many thanks for opening my eyes to a few useful things.

I wasnt ignoring you Stefan - thanks for the suggestion. I havent got space in the enclosure for another board, but its useful to know such things exist.