New I2C library for Teensy3

propose adding timeout and NAK error counters to library

@nox771 I'd like to add some uint32_t error counters for timeout/call to resetBus as well as addr NAK and data NAK, and imagine these would be worth adding to the library. I don't want to fork and diverge. Why I propose this: if timeout and recovery (with busReset) happens with I2C_AUTO_RETRY defined, the timeout is "fixed" so it's not returned by status() so I can't see and log it in my application code. The NAKs do get returned by status() but if adding the timeout counter, those are the other two solid errors so why not log all three? The counters would stop at the max of 0xFFFFFFFF, not wrap to zero, and could be cleared by the application if desired. 2**32 is a lot of errors (4 billion) so that should be large enough. In a realistic use scenario I'm getting 4500 I2C messages per second (100 KHz SCL). If they are all errors, that's 265 hours to fill a 32 bit int.

So I am asking for any input from you and others before I do this (with the hope of it getting merged into the main master). These would be small changes with little impact on performance of the libraries (code only increments a counter when the error occurs, and three uint32_t would take 12 bytes of data space). I'd put them in a struct for error counts, or they could be added to the existing i2cStruct. Since that's a private struct there would be simple functions to view and clear them.

I don't want to step on any toes proposing such a change.

I have a first hack at this in my fork of i2c_t3 if anyone wants to try it. The new function is Wire.resetBusCountRead(). I'm using it to test the Systronix_PCA9548A library since that MUX seems to rather easily get into a state where it sticks SDA low and there is no recovery other than the wonderful resetBus() of @nox771. Here is typical output after a TyQt reset which apparently left the mux in such a state. But note that it does recover! Woohoo!
Code:
PCA9548A Library Test Code at 0x70
write CFG: 1
init failed with return of 0x04
I2C_TIMEOUT
Interval is 1 sec, Setup Complete!
Send Q/q for quiet, V/v for verbose output.

control write failed with return of 0x07, I2C_ARB_LOST
control write failed with return of 0x04, I2C_TIMEOUT
control write failed with return of 0x07, I2C_ARB_LOST
et:3 Good:1152 1152/sec bad:4 busReset: 2
et:4 Good:5760 2880/sec bad:4 busReset: 2
You can see in the 9548A lib I have set up text strings to index into the Wire.status() return codes and output the description. This is a single-master system so I am surprised to see the ARB_LOST error. Also it surprises me that this mux seems to be so inclined to get stuck (just resetting Teensy will do it about 1/10 times, I'm guessing because it terminates an I2C message in progress).
 
Last edited:
It is strange to see ARB lost errors on single-master. Perhaps some timing artifact or analog effect is confusing the hardware.

For the error counters, I can try to add some in, but it may take some time. I need to think about this. Some errors are detected in the ISR but it is probably better to code them into the done_() or finish_() functions to limit overhead (it would be an implicit (hidden) status update).

There are different ways to structure and read back the error counts. I'm generally not a fan of unnecessary hierarchy, so I prefer to keep i2cStruct flat. I also don't like stuffing the API with one-off error read functions. Probably I would have a single read function and pass it an enum identifier, eg. Wire.getErrorCount(I2C_TIMEOUT); or something like that (could reuse the i2c_status enum). Especially if the return value is standardized.

There are a number of possible errors:
  • Timeout
  • Addr NAK
  • Data NAK
  • ARB Lost
  • Buf overflow
resetBus() and auto-retry are a little odd. Auto-retry can be undefined, but I can still manually call resetBus(). So the question is do you put the counter inside resetBus() function and include manual calls, or only in the auto-retry code, in which case it is detecting that specific case. Or have counters for each?

If separate this could become 7 different error counters. 32-bit might be excessive, is 16-bit more appropriate? I would hope I could correct my line problem before I count up 4 billion errors.
 
@nox771, yeah the ARB_LOST is weird but you have that comment in there. Paul has a similar comment in a similar place in his Wire library. Using his Wire library and testing it, I see the same kind of error situation and the only recovery is what you did with resetBus(). Wire has no such ability, and in i2c_t3 once ARB_LOST happens, then the library doesn't attempt to execute resetBus().

I like the idea of reading the error count by passing the enum status value. If you pass it a value for which there is no counter, just return 0?

I put the resetBus() count in the auto-retry code so it has no impact unless it is recovering from a stuck bus. I don't count manual calls, which I think is fine since I know when I am calling it (so I could just count that myself, no counter needed in the library). What I want to track is the incidence of unexpected timeouts and recoveries. Ideally those should never happen, of course, but with noise or interrupted I2C cycles (power bumps, manual resets, whatever) it is possible.

32-bits might seem a lot, but these systems are expected to run in field for 10+ years (I think of them like elevators: just expected to work forever) and could reasonably run for weeks or months without a restart. Now while I'm running tests I see counts of 2000 or more in just a few minutes. In the shipped systems I expect that building power typically would go out every few months and that would cause a system reboot so I do expect them to get reset every few months. At the moment we don't restore the counters from FRAM on a restart, they get cleared. But they are logged to uSD card daily for a long-term record. I guess 16 bits would be OK, as long as they don't wrap back to zero. But on a 32-bit M4 architecture is 32-bits a more convenient size? Is a uint32_t++ any slower than a uint16_t?
 
Last edited:
Compile Error: "invalid conversion from 'int' to 'i2c_rate' [-fpermissive]"

I just downloaded this library from Github and attempted to compile the example "basic_master.ino".
This may be a newbie mistake, but I wasn't able to get it to compile. Not sure what I'm missing here and didn't find this specific error message in the documentation.

Thanks
pzschulte

Here is the full error message:

Code:
Arduino: 1.6.8 (Windows 8.1), TD: 1.28, Board: "Teensy 3.2 / 3.1, Serial, 24 MHz, US English"

basic_master: In function 'void setup()':
basic_master:31: error: invalid conversion from 'int' to 'i2c_rate' [-fpermissive]
     Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, 400000);

                                                                        ^

In file included from C:\Users\Peter\Documents\Arduino\libraries\i2c_t3-master\examples\basic_master\basic_master.ino:16:0:

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\i2c_t3/i2c_t3.h:406:17: error:   initializing argument 5 of 'void i2c_t3::begin(i2c_mode, uint8_t, i2c_pins, i2c_pullup, i2c_rate, i2c_op_mode)' [-fpermissive]

     inline void begin(i2c_mode mode, uint8_t address, i2c_pins pins, i2c_pullup pullup, i2c_rate rate, i2c_op_mode opMode=I2C_OP_MODE_ISR)

                 ^

invalid conversion from 'int' to 'i2c_rate' [-fpermissive]

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

The library can also be downloaded separately (eg. for updates), and used by unpacking the library contents into your sketchbook/libraries folder.

To use with existing Arduino sketches, simply change the #include <Wire.h> to #include <i2c_t3.h>

Example sketches can be found in the Arduino menus at: File->Examples->i2c_t3

----------------------------------------------------------------------------------------------------------------------
Example List

Examples are divided into two categories, basic and advanced. Basic examples are demonstrate basic "Arduino-like" function of the library. Advanced examples demonstrate more complex scenarios, such as multi-bus, concurrent Master/Slave, and background transfer (ISR or DMA) operations.

  • basic_master - this creates a Master device which is setup to talk to the Slave device given in the basic_slave sketch.
  • basic_slave - this creates a Slave device which responds to the basic_master sketch.
 
Code:
In file included from C:\Users\Peter\Documents\Arduino\libraries\i2c_t3-master\examples\basic_master\basic_master.ino:16:0:

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\i2c_t3/i2c_t3.h:406:17: error:   initializing argument 5 of 'void i2c_t3::begin(i2c_mode, uint8_t, i2c_pins, i2c_pullup, i2c_rate, i2c_op_mode)' [-fpermissive]

     inline void begin(i2c_mode mode, uint8_t address, i2c_pins pins, i2c_pullup pullup, i2c_rate rate, i2c_op_mode opMode=I2C_OP_MODE_ISR)

                 ^

invalid conversion from 'int' to 'i2c_rate' [-fpermissive]

The problem is your directory name is wrong. For Arduino linker to find the file the directory name has to match. In your libraries you need to change i2c_t3-master to just i2c_t3:
C:\Users\Peter\Documents\Arduino\libraries\i2c_t3\examples\basic_master\basic_master.ino

The i2c_t3.cpp and i2c_t3.h files should be in the directory:
C:\Users\Peter\Documents\Arduino\libraries\i2c_t3

And the Arduino sketchbook location should be set to:
C:\Users\Peter\Documents\Arduino
 
The problem is your directory name is wrong. For Arduino linker to find the file the directory name has to match. In your libraries you need to change i2c_t3-master to just i2c_t3:
C:\Users\Peter\Documents\Arduino\libraries\i2c_t3\examples\basic_master\basic_master.ino

The i2c_t3.cpp and i2c_t3.h files should be in the directory:
C:\Users\Peter\Documents\Arduino\libraries\i2c_t3

And the Arduino sketchbook location should be set to:
C:\Users\Peter\Documents\Arduino

Ok, I changed those things, but now I get a different set of errors:

Code:
Arduino: 1.6.8 (Windows 8.1), TD: 1.28, Board: "Teensy 3.2 / 3.1, Serial, 24 MHz, US English"

In file included from C:\Users\Peter\Documents\Arduino\basic_master\basic_master.ino:16:0:

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:372:6: error: 'I2C_F_DIV20' was not declared in this scope

     {I2C_F_DIV20,I2C_F_DIV22,I2C_F_DIV24,I2C_F_DIV26,

      ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:372:18: error: 'I2C_F_DIV22' was not declared in this scope

     {I2C_F_DIV20,I2C_F_DIV22,I2C_F_DIV24,I2C_F_DIV26,

                  ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:372:30: error: 'I2C_F_DIV24' was not declared in this scope

     {I2C_F_DIV20,I2C_F_DIV22,I2C_F_DIV24,I2C_F_DIV26,

                              ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:372:42: error: 'I2C_F_DIV26' was not declared in this scope

     {I2C_F_DIV20,I2C_F_DIV22,I2C_F_DIV24,I2C_F_DIV26,

                                          ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:373:6: error: 'I2C_F_DIV28' was not declared in this scope

      I2C_F_DIV28,I2C_F_DIV30,I2C_F_DIV32,I2C_F_DIV34,

      ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:373:18: error: 'I2C_F_DIV30' was not declared in this scope

      I2C_F_DIV28,I2C_F_DIV30,I2C_F_DIV32,I2C_F_DIV34,

                  ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:373:30: error: 'I2C_F_DIV32' was not declared in this scope

      I2C_F_DIV28,I2C_F_DIV30,I2C_F_DIV32,I2C_F_DIV34,

                              ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:373:42: error: 'I2C_F_DIV34' was not declared in this scope

      I2C_F_DIV28,I2C_F_DIV30,I2C_F_DIV32,I2C_F_DIV34,

                                          ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:374:6: error: 'I2C_F_DIV36' was not declared in this scope

      I2C_F_DIV36,I2C_F_DIV40,I2C_F_DIV44,I2C_F_DIV48,

      ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:374:18: error: 'I2C_F_DIV40' was not declared in this scope

      I2C_F_DIV36,I2C_F_DIV40,I2C_F_DIV44,I2C_F_DIV48,

                  ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:374:30: error: 'I2C_F_DIV44' was not declared in this scope

      I2C_F_DIV36,I2C_F_DIV40,I2C_F_DIV44,I2C_F_DIV48,

                              ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:374:42: error: 'I2C_F_DIV48' was not declared in this scope

      I2C_F_DIV36,I2C_F_DIV40,I2C_F_DIV44,I2C_F_DIV48,

                                          ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:375:18: error: 'I2C_F_DIV56' was not declared in this scope

      I2C_F_DIV52,I2C_F_DIV56,I2C_F_DIV60,I2C_F_DIV64,

                  ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:375:42: error: 'I2C_F_DIV64' was not declared in this scope

      I2C_F_DIV52,I2C_F_DIV56,I2C_F_DIV60,I2C_F_DIV64,

                                          ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:376:6: error: 'I2C_F_DIV68' was not declared in this scope

      I2C_F_DIV68,I2C_F_DIV72,I2C_F_DIV80,I2C_F_DIV88,

      ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:376:18: error: 'I2C_F_DIV72' was not declared in this scope

      I2C_F_DIV68,I2C_F_DIV72,I2C_F_DIV80,I2C_F_DIV88,

                  ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:376:30: error: 'I2C_F_DIV80' was not declared in this scope

      I2C_F_DIV68,I2C_F_DIV72,I2C_F_DIV80,I2C_F_DIV88,

                              ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:376:42: error: 'I2C_F_DIV88' was not declared in this scope

      I2C_F_DIV68,I2C_F_DIV72,I2C_F_DIV80,I2C_F_DIV88,

                                          ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:377:6: error: 'I2C_F_DIV96' was not declared in this scope

      I2C_F_DIV96,I2C_F_DIV104,I2C_F_DIV112,I2C_F_DIV128,

      ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:377:18: error: 'I2C_F_DIV104' was not declared in this scope

      I2C_F_DIV96,I2C_F_DIV104,I2C_F_DIV112,I2C_F_DIV128,

                  ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:377:31: error: 'I2C_F_DIV112' was not declared in this scope

      I2C_F_DIV96,I2C_F_DIV104,I2C_F_DIV112,I2C_F_DIV128,

                               ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:377:44: error: 'I2C_F_DIV128' was not declared in this scope

      I2C_F_DIV96,I2C_F_DIV104,I2C_F_DIV112,I2C_F_DIV128,

                                            ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:378:19: error: 'I2C_F_DIV144' was not declared in this scope

      I2C_F_DIV136,I2C_F_DIV144,I2C_F_DIV160,I2C_F_DIV176,

                   ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:378:32: error: 'I2C_F_DIV160' was not declared in this scope

      I2C_F_DIV136,I2C_F_DIV144,I2C_F_DIV160,I2C_F_DIV176,

                                ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:379:6: error: 'I2C_F_DIV192' was not declared in this scope

      I2C_F_DIV192,I2C_F_DIV224,I2C_F_DIV240,I2C_F_DIV256,

      ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:379:19: error: 'I2C_F_DIV224' was not declared in this scope

      I2C_F_DIV192,I2C_F_DIV224,I2C_F_DIV240,I2C_F_DIV256,

                   ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:379:32: error: 'I2C_F_DIV240' was not declared in this scope

      I2C_F_DIV192,I2C_F_DIV224,I2C_F_DIV240,I2C_F_DIV256,

                                ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:379:45: error: 'I2C_F_DIV256' was not declared in this scope

      I2C_F_DIV192,I2C_F_DIV224,I2C_F_DIV240,I2C_F_DIV256,

                                             ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:380:6: error: 'I2C_F_DIV288' was not declared in this scope

      I2C_F_DIV288,I2C_F_DIV320,I2C_F_DIV352,I2C_F_DIV384,

      ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:380:19: error: 'I2C_F_DIV320' was not declared in this scope

      I2C_F_DIV288,I2C_F_DIV320,I2C_F_DIV352,I2C_F_DIV384,

                   ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:380:45: error: 'I2C_F_DIV384' was not declared in this scope

      I2C_F_DIV288,I2C_F_DIV320,I2C_F_DIV352,I2C_F_DIV384,

                                             ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:381:6: error: 'I2C_F_DIV448' was not declared in this scope

      I2C_F_DIV448,I2C_F_DIV480,I2C_F_DIV512,I2C_F_DIV576,

      ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:381:19: error: 'I2C_F_DIV480' was not declared in this scope

      I2C_F_DIV448,I2C_F_DIV480,I2C_F_DIV512,I2C_F_DIV576,

                   ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:381:32: error: 'I2C_F_DIV512' was not declared in this scope

      I2C_F_DIV448,I2C_F_DIV480,I2C_F_DIV512,I2C_F_DIV576,

                                ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:381:45: error: 'I2C_F_DIV576' was not declared in this scope

      I2C_F_DIV448,I2C_F_DIV480,I2C_F_DIV512,I2C_F_DIV576,

                                             ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:382:6: error: 'I2C_F_DIV640' was not declared in this scope

      I2C_F_DIV640,I2C_F_DIV768,I2C_F_DIV896,I2C_F_DIV960,

      ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:382:19: error: 'I2C_F_DIV768' was not declared in this scope

      I2C_F_DIV640,I2C_F_DIV768,I2C_F_DIV896,I2C_F_DIV960,

                   ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:382:32: error: 'I2C_F_DIV896' was not declared in this scope

      I2C_F_DIV640,I2C_F_DIV768,I2C_F_DIV896,I2C_F_DIV960,

                                ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:382:45: error: 'I2C_F_DIV960' was not declared in this scope

      I2C_F_DIV640,I2C_F_DIV768,I2C_F_DIV896,I2C_F_DIV960,

                                             ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:383:6: error: 'I2C_F_DIV1024' was not declared in this scope

      I2C_F_DIV1024,I2C_F_DIV1152,I2C_F_DIV1280,I2C_F_DIV1536,

      ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:383:20: error: 'I2C_F_DIV1152' was not declared in this scope

      I2C_F_DIV1024,I2C_F_DIV1152,I2C_F_DIV1280,I2C_F_DIV1536,

                    ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:383:34: error: 'I2C_F_DIV1280' was not declared in this scope

      I2C_F_DIV1024,I2C_F_DIV1152,I2C_F_DIV1280,I2C_F_DIV1536,

                                  ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:383:48: error: 'I2C_F_DIV1536' was not declared in this scope

      I2C_F_DIV1024,I2C_F_DIV1152,I2C_F_DIV1280,I2C_F_DIV1536,

                                                ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:384:6: error: 'I2C_F_DIV1920' was not declared in this scope

      I2C_F_DIV1920,I2C_F_DIV1792,I2C_F_DIV2048,I2C_F_DIV2304,

      ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:384:20: error: 'I2C_F_DIV1792' was not declared in this scope

      I2C_F_DIV1920,I2C_F_DIV1792,I2C_F_DIV2048,I2C_F_DIV2304,

                    ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:384:34: error: 'I2C_F_DIV2048' was not declared in this scope

      I2C_F_DIV1920,I2C_F_DIV1792,I2C_F_DIV2048,I2C_F_DIV2304,

                                  ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:384:48: error: 'I2C_F_DIV2304' was not declared in this scope

      I2C_F_DIV1920,I2C_F_DIV1792,I2C_F_DIV2048,I2C_F_DIV2304,

                                                ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:385:6: error: 'I2C_F_DIV2560' was not declared in this scope

      I2C_F_DIV2560,I2C_F_DIV3072,I2C_F_DIV3840};

      ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:385:20: error: 'I2C_F_DIV3072' was not declared in this scope

      I2C_F_DIV2560,I2C_F_DIV3072,I2C_F_DIV3840};

                    ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:385:34: error: 'I2C_F_DIV3840' was not declared in this scope

      I2C_F_DIV2560,I2C_F_DIV3072,I2C_F_DIV3840};

                                  ^

Multiple libraries were found for "i2c_t3.h"
 Used: C:\Users\Peter\Documents\Arduino\libraries\i2c_t3
 Not used: C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\i2c_t3
Error compiling for board Teensy 3.2 / 3.1.

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.
 
Ok, I changed those things, but now I get a different set of errors:

Code:
Arduino: 1.6.8 (Windows 8.1), TD: 1.28, Board: "Teensy 3.2 / 3.1, Serial, 24 MHz, US English"
In file included from C:\Users\Peter\Documents\Arduino\basic_master\basic_master.ino:16:0:

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:372:6: error: 'I2C_F_DIV20' was not declared in this scope

     {I2C_F_DIV20,I2C_F_DIV22,I2C_F_DIV24,I2C_F_DIV26,

      ^
Those defines are in the kinetis.h file. It means your Teensyduino version is too old. You need to update your install. The latest non-beta looks like 1.35:
https://forum.pjrc.com/threads/41547-Teensyduino-1-35-Released

Edit: You will probably need a newer Arduino base install also, refer to this page:
https://www.pjrc.com/teensy/td_download.html
 
Last edited:
Thanks nox771, that fixed it! I now have the basic_master and basic_slave examples running and talking to each other across 2 Teensys.
You may want to add a note in the documentation that the user must update to latest version of Arduino and teensyduino before running code with i2c_t3.

Thanks!
pzschulte
 
i2c_t3 Conflict with MPU6050 code (I2Cdev library)

Thanks for the help nox771! I have been able to successfully implement simple sending/receiving of data from the AttoPilot Voltage and Current Sense breakout using i2c_t3 with the modified basic_master and basic_slave example scripts. Now I am running into a slightly more complex issue...

I have 2 Teensy's connected together via I2C. One Teensy (Master) is collecting data from the Sparkfun MPU6050 accelerometer breakout board via I2C (using Jeff Rowberg's I2Cdev library: https://github.com/jrowberg/i2cdevlib/). The other (Slave) is collecting data from an AttoPilot Voltage and Current Sense breakout. I have properly setup the Master to collect MPU6050 data, and the slave to collect Voltage/Current data. The problem comes when I try to add in the i2c_t3 library (#include <i2c_t3.h>) to my MPU6050 sketch on the Master Teensy.

I get the following error message:
In file included from C:\Users\Peter\Documents\Arduino\TeensyTutorials\M PU6050_DMP6_Teensy_FrSkySPort_Temp_HallEffect\MPU6 050_DMP6_Teensy_FrSkySPort_Temp_HallEffect.ino:108 :0:

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h: At global scope:

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:994:15: error: conflicting declaration 'i2c_t3 Wire'

extern i2c_t3 Wire;

^

In file included from C:\Users\Peter\Documents\Arduino\libraries\I2Cdev/I2Cdev.h:80:0,

from C:\Users\Peter\Documents\Arduino\TeensyTutorials\M PU6050_DMP6_Teensy_FrSkySPort_Temp_HallEffect\MPU6 050_DMP6_Teensy_FrSkySPort_Temp_HallEffect.ino:98:

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Wire/Wire.h:104:16: error: 'Wire' has a previous declaration as 'TwoWire Wire'

extern TwoWire Wire;

Because "Wire" is already declared (since it is used by the MPU6050/I2Cdev libraries), the additional declaration of a new "Wire" from i2c_t3 conflicts with it.
Is there any way around this so I can use both i2c_t3 and the MPU6050/I2Cdev libraries in the same sketch without rewriting one of them? I have tried playing around with the libraries (for example changing the name of "Wire" to something else in either i2c_t3 or I2Cdev/MPU6050) but haven't been able to get it working easily.

Full source code for all my sketches can be found here:
https://forum.pjrc.com/threads/40664-Multiple-Devices-Over-I2C

Thanks!
pzschulte
 
Are they on the same i2c bus wires? Seems you'd only want one library to perform operations at a time on those bus lines for all connected devices.

If one device can be on alternate SDA/SCL i2c bus pins then you could use i2c_t3.h:Wire2 to access the device on those lines.

Otherwise both devices would need to use the same Wire code - removing the compile conflict and what would otherwise have resulted in a low level hardware conflict with two master software sets on that hardware.
 
defragster,

Interesting idea. I am currently running them on the same i2c bus wires.

Can I use A2 and A3 (pins 16 and 17 on the Teensy 3.2) in order to have alternate SDA0/SCL0 wires? (they are grayed out on the pinout card). Or do I need to go to the pads on the back (SDA1/SDL1, on A18/A19, or pins 29 and 30 for Teensy 3.2)? [not ideal for my system configuration but I could make it work if needed].

Also, I think I will still have the compile issue with "Wire". Would I need to modify my i2c_t3 library code in order to use "Wire2"?

Thanks!
pzschulte
 
... need to go to the pads on the back (SDA1/SDL1, on A18/A19, or pins 29 and 30 for Teensy 3.2)? [not ideal for my system configuration but I could make it work if needed].

Also, I think I will still have the compile issue with "Wire". Would I need to modify my i2c_t3 library code in order to use "Wire2"?

Yes, to get second bus you'd need those backside pins on T_3.2. (there are OSH boards that let you bring those out nicely - FrankB made one - linked around)

I've found something like "#define Wire Wire2" to work to get the offending library to use the right wires with the help of the compiler without actually editing. That was on the K66 / T_3.6 beta thread with multiple Adafruit displays on same sketch and the other i2c's it had.

<edit> from hardware\teensy\avr\libraries\i2c_t3\i2c_t3.cpp:
// Wire1 3.1/3.2 I2C_PINS_26_31 26 31
// Wire1 3.1/3.2 I2C_PINS_29_30 29 30
 
Last edited:
Yes, to get second bus you'd need those backside pins on T_3.2. (there are OSH boards that let you bring those out nicely - FrankB made one - linked around)

I assume this is the board you are referring to? https://oshpark.com/shared_projects/0T6ZdhhG

I've found something like "#define Wire Wire2" to work to get the offending library to use the right wires with the help of the compiler without actually editing.

Ok, to be clear where in the code do I need to use the #define? In my Arduino sketch or in one of the libraries? (i2c_t3 or I2Cdev).

Also, I will need to use Wire1, not Wire2 with T3.2 because of the pinout assignments correct? Will adding the #define make this change or do I need to use Wire1.(functioncall) in my sketch code when using i2c_t3 functions?

Thanks
pzschulte
 
Yes, that looks like a usable FrankB board to underside solder and pull out the bottom pins.

I found the #define to work when using Adafruit as noted - BING pointed me to this post: K66-Beta-Test

The offending library in that case was the adafruit code with edits made something like this in two files - then every place they called "Wire" after compiling - the linker used "Wire1":
// Adafruit_SSD1306.h edit:
#define Wire Wire1

// Edit Adafruit_SSD1306.cpp:
#include <Wire.h> // line 33
#include <i2c_t3.h>

#define Wire Wire1
 
I found the #define to work when using Adafruit as noted - BING pointed me to this post: K66-Beta-Test

The offending library in that case was the adafruit code with edits made something like this in two files - then every place they called "Wire" after compiling - the linker used "Wire1":

I'm not quite sure I am following. I think you are saying to keep i2c_t3 on Wire and then set the other I2C protocol (I2Cdev2.h) to use Wire1.

So I attempted to add (#define Wire Wire1) to the top of I2Cdev.h and I2Cdev.cpp.

However, then there is a conflict between Wire1 in I2Cdev2.h and Wire1 in i2c_t3.h:

Code:
Arduino: 1.8.1 (Windows 8.1), TD: 1.35, Board: "Teensy 3.2 / 3.1, Serial, 24 MHz, Fast, US English"

In file included from C:\Users\Peter\Documents\Arduino\TeensyTutorials\MPU6050_DMP6_Teensy_FrSkySPort_Temp_HallEffect_VoltCurr\MPU6050_DMP6_Teensy_FrSkySPort_Temp_HallEffect_VoltCurr.ino:99:0:

C:\Users\Peter\Documents\Arduino\libraries\I2Cdev2/I2Cdev2.h:47:14: error: conflicting declaration 'i2c_t3 Wire1'

 #define Wire Wire1

              ^

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:994:15: note: in expansion of macro 'Wire'

 extern i2c_t3 Wire;

               ^

C:\Users\Peter\Documents\Arduino\libraries\I2Cdev2/I2Cdev2.h:47:14: error: 'Wire1' has a previous declaration as 'TwoWire Wire1'

 #define Wire Wire1

              ^

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Wire/Wire.h:104:16: note: in expansion of macro 'Wire'

 extern TwoWire Wire;

                ^

In file included from C:\Users\Peter\Documents\Arduino\TeensyTutorials\MPU6050_DMP6_Teensy_FrSkySPort_Temp_HallEffect_VoltCurr\MPU6050_DMP6_Teensy_FrSkySPort_Temp_HallEffect_VoltCurr.ino:103:0:

C:\Users\Peter\Documents\Arduino\libraries\i2c_t3/i2c_t3.h:996:19: error: conflicting declaration 'i2c_t3 Wire1'

     extern i2c_t3 Wire1;

                   ^

In file included from C:\Users\Peter\Documents\Arduino\TeensyTutorials\MPU6050_DMP6_Teensy_FrSkySPort_Temp_HallEffect_VoltCurr\MPU6050_DMP6_Teensy_FrSkySPort_Temp_HallEffect_VoltCurr.ino:99:0:

C:\Users\Peter\Documents\Arduino\libraries\I2Cdev2/I2Cdev2.h:47:14: error: 'Wire1' has a previous declaration as 'TwoWire Wire1'

 #define Wire Wire1

              ^

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Wire/Wire.h:104:16: note: in expansion of macro 'Wire'

 extern TwoWire Wire;

                ^

Multiple libraries were found for "i2c_t3.h"
 Used: C:\Users\Peter\Documents\Arduino\libraries\i2c_t3
 Not used: C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\i2c_t3
Error compiling for board Teensy 3.2 / 3.1.

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

If I comment out i2c_t3.h in my Master sketch to get rid of the conflict, then Wire1 is undefined:

Code:
Arduino: 1.8.1 (Windows 8.1), TD: 1.35, Board: "Teensy 3.2 / 3.1, Serial, 24 MHz, Fast, US English"

C:\Users\Peter\AppData\Local\Temp\arduino_build_758107\sketch\MPU6050_DMP6_Teensy_FrSkySPort_Temp_HallEffect_VoltCurr.ino.cpp.o: In function `setup':

C:\Users\Peter\Documents\Arduino\TeensyTutorials\MPU6050_DMP6_Teensy_FrSkySPort_Temp_HallEffect_VoltCurr/MPU6050_DMP6_Teensy_FrSkySPort_Temp_HallEffect_VoltCurr.ino:498: undefined reference to `Wire1'

C:\Users\Peter\AppData\Local\Temp\arduino_build_758107\libraries\I2Cdev2\I2Cdev2.cpp.o: In function `I2Cdev2::writeBytes(unsigned char, unsigned char, unsigned char, unsigned char*)':

C:\Users\Peter\Documents\Arduino\libraries\I2Cdev2/I2Cdev2.cpp:631: undefined reference to `Wire1'

C:\Users\Peter\AppData\Local\Temp\arduino_build_758107\libraries\I2Cdev2\I2Cdev2.cpp.o: In function `I2Cdev2::writeWords(unsigned char, unsigned char, unsigned char, unsigned short*)':

C:\Users\Peter\Documents\Arduino\libraries\I2Cdev2/I2Cdev2.cpp:690: undefined reference to `Wire1'

C:\Users\Peter\AppData\Local\Temp\arduino_build_758107\libraries\I2Cdev2\I2Cdev2.cpp.o: In function `I2Cdev2::readBytes(unsigned char, unsigned char, unsigned char, unsigned char*, unsigned short)':

C:\Users\Peter\Documents\Arduino\libraries\I2Cdev2/I2Cdev2.cpp:318: undefined reference to `Wire1'

collect2.exe: error: ld returned 1 exit status

Error compiling for board Teensy 3.2 / 3.1.

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.
 
I'm not quite sure I am following. I think you are saying to keep i2c_t3 on Wire and then set the other I2C protocol (I2Cdev2.h) to use Wire1.
...

incomplete answer before and perhaps again ... short is time ... but I think WIRE.h doesn't support Wire1 - so that would go the other way around using 'i2c_t3 Wire1'.

Perhaps have to just use i2c_t3 for Wire and Wire1?
 
Perhaps have to just use i2c_t3 for Wire and Wire1?

But then I go back to my original problem of not being able to use i2c_t3 with the MPU6050/I2Cdev libraries...

I think my solution will need to involve collecting data from all sensors on a single Master Teensy and then sending the data as a full package between the Master and other slave Teensys using Wire (not i2c_t3). I don't really see any other (simple) way to overcome the MPU6050 and i2c_t3 incompatibility issues.
 
'perhaps' . . . did you try the part before that?

I've used onehorse MPU9050 units with i2c_t3?

I'm not able to #include i2c_t3 at all if the MPU6050/I2Cdev code is also included (that was my original post/question). I guess I could modify i2c_t3 to comment out all uses of "Wire" and use "Wire1" to remove the conflict with I2Cdev, but I had been hoping to avoid such a modification.
 
My use case wasn't as similar as I thought my experience might be to be useful.

I found this thread Multiple-Devices-Over-I2C - seems this has been trouble for some time - bummer.

I was wondering about adafruit and sparkfun - they both seem to point to the i2cdev that is giving you trouble here on Teensy.

Given your hardware - and wanting to use Teensy - the most capable route would be getting full use from all devices using the i2c_t3 library, that could mean getting the MPU6050 to use that for i2c comms would give the best results?

I noted onehorse above - I see he has code for a no longer shipping 6050 device that uses Wire.h in his examples. Perhaps you could try his existing code to work with your hardware and drop the conflicting software rather than rewriting any of it? If that works it may be more usable replacing the Wire.h code with i2c_t3.h code - or allow using both devices on the same Wire bus.
 
I'm not able to #include i2c_t3 at all if the MPU6050/I2Cdev code is also included (that was my original post/question). I guess I could modify i2c_t3 to comment out all uses of "Wire" and use "Wire1" to remove the conflict with I2Cdev, but I had been hoping to avoid such a modification.

I don't recommend any configuration whereby you essentially trick the compiler into using both the Wire library and the i2c_t3 library at the same time. Both these libs are going to be possibly manipulating the same hardware registers.

The general method of using i2c_t3 in your code with a 3rd-party library that has a I2C dependency, is to modify the 3rd-party library to include i2c_t3.h instead of Wire.h. This usually works because i2c_t3 exports a Wire compatible API, so the 3rd-party library sees no difference. In some cases it requires using i2c_t3 in "immediate" mode instead of ISR or DMA, so it operates just like Wire also, but that is rare.

If your 3rd-party lib is using Wire, then you should probably be using a different bus in your code, such as Wire1, Wire2, etc. That is done by substituting Wire1.<function> instead of Wire.<function> in your code. Of course the other buses map to different pins and such, so you need to modify your wiring as well. You could also share a bus with the 3rd-party lib if you have good control over when it communicates.

All that said, have you tried just modifying the header in the 3rd-party library to use i2c_t3.h? Is it working?
 
Is it safe to send a message without ending it immediately with a stop or a repeated start? I know that this library allows you to leave the stop off the end of a message so that the next start is interpreted as a repeated start by the slave, but I wonder if there are any risks to this approach, since an arbitrary amount of time can pass before it is finally sent.
 
nox771. Just want to confirm that I can i2c_t3.h for wire.h and still use the typical Wire calls as if I was using Wire.h? I am familiar with I2Cdevlib and a myriad of other libs for different sensors and I2Cdevlib only uses wire.function calls. If you want to use wire1 you have to manually change wire to wire1 but then it might affect any other libs that use I2Cdevlib. There is a way to use both libs that I used and that is to test for teensy board or not, but then that adds another layer of complication.
 
Is it safe to send a message without ending it immediately with a stop or a repeated start? I know that this library allows you to leave the stop off the end of a message so that the next start is interpreted as a repeated start by the slave, but I wonder if there are any risks to this approach, since an arbitrary amount of time can pass before it is finally sent.

In a single master setup it has no practical effect if you don't send a stop. The effect of not sending a stop is that the bus is still "busy" (SCL remains low), but there is no problem leaving it in that state for an indefinite period of time (since the single master is the only one which can initiate a new transfer). From a slave perspective it may keep a slave which uses repeated-starts in a sort of intermediate state, whereby it is perhaps expecting more data, but usually that is ok too. Addressing a different slave isn't a problem either, since the next start will send out a new slave address.
 
Back
Top