I2C Slave on Teensy 4

Status
Not open for further replies.

RichardG

Active member
Hi. I've just started using a Teensy for the first time. I'm trying to get a Raspberry Pi to talk to a Teensy 4 over I2C. I've configured the Teensy to listen in slave mode but it doesn't seem to be listening. (See code below)

I did a bit of digging and found this in WireIMXRT.cpp.
Code:
void TwoWire::begin(uint8_t address)
{
	// TODO: slave mode
}

Does this mean that the Teensy 4 doesn't support I2C slave mode? If not, what protocol would you suggest for communication to a Raspberry Pi?

Here's my code for the Teensy. Is there something wrong with it?

Code:
#include <Arduino.h>
#include <Wire.h>
void i2c_slave_request_ISR();
void i2c_slave_receive_ISR(int count);
void setup() {
  Wire.onRequest(i2c_slave_request_ISR);
  Wire.onReceive(i2c_slave_receive_ISR);
  Wire.begin(8);

  Serial.begin(9600);
  Serial.println("Started");
}
void loop() {
  delay(1000);
}
void i2c_slave_request_ISR() {
  Serial.println("Got I2C Request");
}
void i2c_slave_receive_ISR(int count) {
  Serial.println(" Got I2C Receive Request");
}

thanks very much,
Richard
 
To answer my own question...

I2C Slave mode is definitely not supported by the current version of Wire. The Teensy 4 isn't supported by the i2c_t3 library either.

I've started to write a new driver that extends Paul's version to add Slave mode. I'll be happy to share this with everyone as soon as it's usable. Please let me know if you're interested in using it. If you've got any other problems with I2C and the Teensy 4 I might be able to deal with them as well.

cheers,
Richard
 
You are correct. I2C slave mode isn't supported yet on the T4. Currently in the wire lib there are places that identify that.
 
FWIW, the NXP SDK for the 1060 has an I2C slave example
boards/evkmimxrt1060/driver_examples/lpi2c/interrupt_b2b_transfer/slave/
 
@RichardG - have you thought about porting your changes over to the existing Wire library so there are not 2 I2C libraries?
 
Hi @mjs513.

The library already includes a wrapper which implements the wire API. See https://github.com/Richard-Gemmell/teensy4_i2c/blob/master/src/i2c_driver_wire.h. I've put some of the standard Wire examples in there so you can see it in use. e.g. https://github.com/Richard-Gemmell/...r/examples/wire/slave_sender/slave_sender.ino

The Arduino IDE will pick it up as "Teensy4 I2C" if you paste the entire project into your Arduino custom libraries folder and restart. This includes adding the examples to list under File | Examples.

There are a couple of other interfaces for those who would rather avoid Wire. You can use the driver directly or use one of the classes that makes it easy to handle the usual I2C register pattern. e.g. https://github.com/Richard-Gemmell/...ple/i2c_register_slave/i2c_register_slave.ino

I'll be happy to tweak it to make it more useful for other people. All comments and suggestions are welcome.

cheers,
Richard
 
@RichardG

Thanks for putting this together. Its helping me move forward - sort of. I wish to put another word in in support of making it part of the wire library. For one reason, that I, with admittedly limited networking knowledge, would like to implement this through EasyTransferI2C.

I have a project where to do what needs to be done, the connecting device (nano) needs to be the master to initialize itself and find out what addresses are available on the I2C network, choose one which is available, and then communicate that back to the controller (Teensy 4), which then takes control of the network again to communicate out to the other previously connected devices. Its a bit loopy - but the logic is sound enough if I can get it working.

One thought that is just striking me now is that I suppose for the proto version of this, I could just assign addresses to all devices and leave the beyond ~100 devices to experts down the road. Though it seems sloppy to be sending requests out to the whole network all the time to find out who is now connected.

In thoughts on networking with EasyTransfer or correcting my views on master/slave relationships I welcome any feedback.

As pertains directly to EasyTransferI2C:

Without Easytransfer #included, things work as anticipated with the FindI2C script from a nano.

With Easytransfer #included we get the errors as shown below.

Code:
#include <arduino.h>
#include <i2c_driver.h>
#include <i2c_driver_wire.h>
//#include <Wire.h>
#include <EasyTransferI2C.h>

void setup() {
  Wire.begin(11);                // join i2c bus with address #9
  //start the library, pass in the data details and the name of the serial port. Can be Serial, Serial1, Serial2, etc. 
  //ET.begin(details(dataReceived), &Wire);
  //ET2.begin(details(returnData), &Wire);
  Wire.onReceive(receiveEvent); // register event
  Serial.begin(9600);           // start serial for output
}


Error received with code above:
Code:
In file included from C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Wire/Wire.h:26:0,

                 from C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\EasyTransferI2C/EasyTransferI2C.h:45,

                 from C:\Users\A\Documents\Arduino\Freeform\i2c_EasyTransfer_Practice-slave\i2c_EasyTransfer_Practice-slave.ino:21:

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Wire/WireIMXRT.h:183:16: error: conflicting declaration 'TwoWire Wire'

 extern TwoWire Wire;

                ^

In file included from C:\Users\A\Documents\Arduino\Freeform\i2c_EasyTransfer_Practice-slave\i2c_EasyTransfer_Practice-slave.ino:19:0:

C:\Users\A\Documents\Arduino\libraries\teensy4_i2c-master\src/i2c_driver_wire.h:180:15: note: previous declaration as 'I2CDriverWire Wire'

 I2CDriverWire Wire(Master, Slave);

               ^

In file included from C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Wire/Wire.h:26:0,

                 from C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\EasyTransferI2C/EasyTransferI2C.h:45,

                 from C:\Users\A\Documents\Arduino\Freeform\i2c_EasyTransfer_Practice-slave\i2c_EasyTransfer_Practice-slave.ino:21:

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Wire/WireIMXRT.h:184:16: error: conflicting declaration 'TwoWire Wire1'

 extern TwoWire Wire1;

                ^

In file included from C:\Users\A\Documents\Arduino\Freeform\i2c_EasyTransfer_Practice-slave\i2c_EasyTransfer_Practice-slave.ino:19:0:

C:\Users\A\Documents\Arduino\libraries\teensy4_i2c-master\src/i2c_driver_wire.h:181:15: note: previous declaration as 'I2CDriverWire Wire1'

 I2CDriverWire Wire1(Master1, Slave1);

               ^

In file included from C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Wire/Wire.h:26:0,

                 from C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\EasyTransferI2C/EasyTransferI2C.h:45,

                 from C:\Users\A\Documents\Arduino\Freeform\i2c_EasyTransfer_Practice-slave\i2c_EasyTransfer_Practice-slave.ino:21:

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Wire/WireIMXRT.h:185:16: error: conflicting declaration 'TwoWire Wire2'

 extern TwoWire Wire2;

                ^

In file included from C:\Users\A\Documents\Arduino\Freeform\i2c_EasyTransfer_Practice-slave\i2c_EasyTransfer_Practice-slave.ino:19:0:

C:\Users\A\Documents\Arduino\libraries\teensy4_i2c-master\src/i2c_driver_wire.h:182:15: note: previous declaration as 'I2CDriverWire Wire2'

 I2CDriverWire Wire2(Master2, Slave2);
 
Or..RTFM. Left here for others with future similar questions:

From the Git page by RichardG.

Change all #includes from Wire.h to i2c_driver_wire.h.

This had to happen in EasyTransferI2C.h as well.

Though it looks like from the error below that I need to also swap TwoWire with I2CDriverWire(). Which scares the pants off of me for lack of experience. I know in the actual clients, I have to reset the TWBR to 0 to set up a new wire. And I don't see any of that in the new driver code.

Though I would have thought it would be included when within the wrapper. Which, again probably due to my level of knowledge, I can't locate anywhere within the scope of any of the files placed within the custom libraries folder.

Thoughts?

Code:
In file included from C:\Users\A\Documents\Arduino\Freeform\i2c_EasyTransfer_Practice-slave\i2c_EasyTransfer_Practice-slave.ino:22:0:

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\EasyTransferI2C/EasyTransferI2C.h:50:32: error: 'TwoWire' has not been declared

 void begin(uint8_t *, uint8_t, TwoWire *theSerial);

                                ^

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\EasyTransferI2C/EasyTransferI2C.h:54:1: error: 'TwoWire' does not name a type

 TwoWire *_serial;

 ^
 
Last edited:
Hi Allister,

I'm glad to hear that you're trying Teensy4 I2C. I hope it works out for you.

I think you can use a 'using' command to tell the compiler that TwoWire and I2CDriverWire are the same thing. I haven't tried it but it should work. You probably need to include it in the middle of your #includes like this:

#include <i2c_driver_wire.h>
using TwoWire = I2CDriverWire;
#include <EasyTransferI2C.h>

As far as I know, there's no way of getting round the need to replace includes of Wire.h with i2c_driver_wire.h. That's the mechanism which says "use Teensy4 I2C" rather than the standard wire library. Maybe someone with more C++ knowledge than me knows of a way to do it.

Note that I2CDriverWire isn't absolutely identical to TwoWire. It's missing the setSDA() and setSCL() methods as they aren't very useful for the Teensy 4. It should work the same way though once you've solved the compilation problems.

I don't know much about TWBR. As far as I can tell it's a way of setting the I2C bus speed that's not required for the Wire library. The frequency is set by the master. Use this snippet to set the clock speed:

Wire.setClock(400000);
Wire.begin();

You can pass any number to setClock() but the clock will actually be set to one of 100 kHz, 400 kHz or 1 MHz. It picks the highest valid frequency below the number you supplied.

The Teensy has 3 I2C ports. These are statically defined as Wire, Wire2 and Wire2 in i2c_driver_wire.h. You don't need to define new ports or call the I2CDriverWire constructors. See https://github.com/Richard-Gemmell/teensy4_i2c/tree/master/examples/wire for examples.

I hope this helps. I apologise if some this post is just telling you what you already know.

cheers,
Richard
 
Hello Richard,

Thank you very much for getting back to me. I appreciate the "using TwoWire = I2CDriverWire;" hint! I'd managed to go in and replace all TwoWire with I2CDriverWire, which gives the same result.

Somehow, strangely, the code in EasyTransfer.h is:
1) Calling Wire1 and Wire2 even though I never do in the code
2)Trying to reinitialize
3) Saying that there are multiple versions of i2c_driver.h - even though there are not (checking full trees under both the program libraries and documents libraries folders).
4) The error below points at the EasyTransfe::receiveData member - I'll dig into that more later as commenting and breaking things gives me other errors

The output (same as when I swapped s/TwoWire/I2CDriverWire manually):
Code:
C:\Users\A\AppData\Local\Temp\arduino_build_633885\libraries\EasyTransferI2C\EasyTransferI2C.cpp.o:(.bss.Wire2+0x0): multiple definition of `Wire2'

C:\Users\A\AppData\Local\Temp\arduino_build_633885\sketch\i2c_EasyTransfer_Practice-master.ino.cpp.o:(.bss.Wire2+0x0): first defined here

c:/program files (x86)/arduino/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/bin/ld.exe: Disabling relaxation: it will not work with multiple definitions

C:\Users\A\AppData\Local\Temp\arduino_build_633885\libraries\EasyTransferI2C\EasyTransferI2C.cpp.o: In function `Print::flush()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\EasyTransferI2C/EasyTransferI2C.cpp:45: multiple definition of `Wire1'

C:\Users\A\AppData\Local\Temp\arduino_build_633885\sketch\i2c_EasyTransfer_Practice-master.ino.cpp.o:C:\Users\A\Documents\Arduino\Freeform\i2c_EasyTransfer_Practice-master/i2c_EasyTransfer_Practice-master.ino:71: first defined here

C:\Users\A\AppData\Local\Temp\arduino_build_633885\libraries\EasyTransferI2C\EasyTransferI2C.cpp.o: In function `Print::flush()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\EasyTransferI2C/EasyTransferI2C.cpp:45: multiple definition of `Wire'

C:\Users\A\AppData\Local\Temp\arduino_build_633885\sketch\i2c_EasyTransfer_Practice-master.ino.cpp.o:C:\Users\A\Documents\Arduino\Freeform\i2c_EasyTransfer_Practice-master/i2c_EasyTransfer_Practice-master.ino:71: first defined here

collect2.exe: error: ld returned 1 exit status

Multiple libraries were found for "EasyTransferI2C.h"
 Used: C:\Program
 Not used: C:\Users\A\Documents\Arduino\libraries\_EasyTransferI2C
Multiple libraries were found for "i2c_driver.h"
 Used: C:\Users\A\Documents\Arduino\libraries\teensy4_i2c-master
Error compiling for board Teensy 4.0.

Test Code. You can see I've even commented out any use of EasyTransfer. Just calling the .h does something:
Code:
#define myI2CAddress 22  // Set to 0 to be chosen by code once the tile has been connected
// #define mySerialNumber 12345   // Currently used to verify whether tile has been connected (use address if maintains state)
#define I2C_SLAVE_ADDRESS 0

char myDesignationLetter = 'A';               // This is the letter Tile 'A'
byte myNeighbors[4];    // set up container for addresses, starting at top and going clockwise
byte myConnection;   // The tile to which this tile is connected

//#include <Wire.h>
#include <i2c_driver.h>
#include <i2c_driver_wire.h>
using TwoWire = I2CDriverWire;	
#include <EasyTransferI2C.h>

//create object
//EasyTransferI2C ET; 
//EasyTransferI2C ET2;

struct SEND_DATA_STRUCTURE{
  //put your variable definitions here for the data you want to send
  //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
  byte I2CAddress;
  char designation;
  byte connectedSide;
  byte playerNumber;
};

struct RETURN_DATA {
	char designation;
	byte result;
};



//give a name to the group of data
SEND_DATA_STRUCTURE dataToSend;
SEND_DATA_STRUCTURE returnData;
//RETURN_DATA returnData;

void setup() {
  Wire.begin(); // join i2c bus (address optional for master)
  //ET.begin(details(dataToSend), &Wire); //start the library, pass in the data details and the name of the serial port. Can be Serial, Serial1, Serial2, etc.
  // ET2.begin(details(returnData), &Wire);
  Wire.onReceive(receiveEvent); // register event
  Serial.begin(9600);// Start serial for diagnostic output
  Serial.println("Plug in now.");
  delay(3000);
}

byte x = 0;

void loop() {
 
}

void receiveEvent(int numBytes) {}

I'm going to spend a bit more time poking around in hopes I can help others some day. If that doesn't work, I'm just going to have to cascade a serial port off of the Teensy to a Nano and figure out EasyTransfer on that side to maintain the computing and memory requirements.

My only question is whether
Code:
I2CDriverWire Wire(Master, Slave);
behaves differently because of its constructor than
Code:
extern TwoWire Wire;
which doesn't have the following calls in the brackets. I'm wondering if there is some bitwise operation which is expecting a single argument and is getting 3(Which I also can't find when searching by case. But I believe in magic so...)

Thanks again!

PS. For kicks and because I'm learning what I'm doing here one line at a time, what can otherse see in this??
Code:
boolean EasyTransferI2C::receiveData(){
  
  //start off by looking for the header bytes. If they were already found in a previous call, skip it.
  if(rx_len == 0){
  //this size check may be redundant due to the size check below, but for now I'll leave it the way it is.
    if(_serial->available() >= 3){
	//this will block until a 0x06 is found or buffer size becomes less then 3.
#if ARDUINO >= 100
      while(_serial->read() != 0x06) {
#else
      while(_serial->receive() != 0x06) {
#endif
		//This will trash any preamble junk in the serial buffer
		//but we need to make sure there is enough in the buffer to process while we trash the rest
		//if the buffer becomes too empty, we will escape and try again on the next call
		if(_serial->available() < 3)
			return false;
		}
#if ARDUINO >= 100
      if (_serial->read() == 0x85){
        rx_len = _serial->read();
#else
      if (_serial->receive() == 0x85){
        rx_len = _serial->receive();
#endif
		//make sure the binary structs on both Arduinos are the same size.
        if(rx_len != size){
          rx_len = 0;
          return false;
        }
      }
    }
  }
  
  //we get here if we already found the header bytes, the struct size matched what we know, and now we are byte aligned.
  if(rx_len != 0){
    while(_serial->available() && rx_array_inx <= rx_len){
#if ARDUINO >= 100
      rx_buffer[rx_array_inx++] = _serial->read();
#else
      rx_buffer[rx_array_inx++] = _serial->receive();
#endif
    }
    
    if(rx_len == (rx_array_inx-1)){
      //seem to have got whole message
      //last uint8_t is CS
      calc_CS = rx_len;
      for (int i = 0; i<rx_len; i++){
        calc_CS^=rx_buffer[i];
      } 
      
      if(calc_CS == rx_buffer[rx_array_inx-1]){//CS good
        memcpy(address,rx_buffer,size);
		rx_len = 0;
		rx_array_inx = 0;
		return true;
		}
		
	  else{
	  //failed checksum, need to clear this out anyway
		rx_len = 0;
		rx_array_inx = 0;
		return false;
	  }
        
    }
  }
  
  return false;
}
 
Last edited:
The line number in the errors just bumps around EasyTransferI2C.cpp when I comment things.
Code:
C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\EasyTransferI2C/EasyTransferI2C.cpp:45: multiple definition of `Wire'

I still get the multiple calls even when I comment out Wire1 and Wire2 in the i2c_driver_wire.h file.

Very curious it thinks there are multiple definitions.
 
I've reproduced the problem with a bare bones example. It's a bug in i2c_driver_wire.h. I'll fix it and upload a new version today.
 
I've fixed it and uploaded the new version. As you suspected, my mistake was to define Wire, Wire1 and Wire2 in the header file instead of defining them in a .cpp file and referring to them as extern. Sorry about that.

I've also put "using TwoWire = I2CDriverWire;" in i2c_driver_wire.h so you shouldn't need to do it yourself any more.

I've confirmed that it now compiles with EasyTransferI2C.cpp but I didn't actually run any code.

cheers,
Richard
 
Thanks so much Richard!

I appreciate how you stepped in to correct that.

I've done some preliminary testing with basic code. The program hangs with no warnings and no output -> Unless one removes the 10 from 'Wire.begin(10);'. Now in master mode Then the hang is gone at least - though the client isn't picking up anything. Until we use that same code with the master 'Wire.begin();' on a regular Arduino. Then it does pick things up.

The only other test I have yet to complete is to confirm that my level shifter from the teensy 4 3.3v and the nano 5v is working.

Can you recommend a tool I can use beyond the Arduino IDE to step through the code as it runs? (The verbose logging for compilation doesn't seem to show much of use). I feel that with a better tool I would be able to give more qualified feedback.

I think I'm going to have start investing time in the cascading approach... I'm falling down the rabbit hole of checking all the functions called by EasyTransferI2C in both this libary and wire.

Here's the code I've been using to test:

Master Code that Works Arduino->Arduino.:
Code:
#include <Wire.h>
//#include "i2c_driver.h"
//#include "i2c_driver_wire.h"
#include <EasyTransferI2C.h>

struct SEND_DATA_STRUCTURE{
  //put your variable definitions here for the data you want to send
  //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
  byte addy;
  char l;
  byte cside;
  byte pn;
};

EasyTransferI2C ET;

//give a name to the group of data
SEND_DATA_STRUCTURE dataToSend;

void setup() {
	Serial.begin(9600);// Start serial for diagnostic output
	Serial.println("Plug in now.");
	Wire.begin(10); // join i2c bus (address optional for master)
	ET.begin(details(dataToSend), &Wire); //start the library, pass in the data details and the name of the serial port. Can be Serial, Serial1, Serial2, etc.
	// ET2.begin(details(returnData), &Wire);
	Wire.onReceive(receiveEvent); // register event

	dataToSend.addy = 10;
	dataToSend.l = 'H';
	dataToSend.cside = 4;
	dataToSend.pn= 5;
	Serial.println("End of Setup()");
	
}

void loop() {

	Serial.println("Sending Data:");
  
  	ET.sendData(9);
  delay(3000);
}

void receiveEvent(int numBytes) {}

Changing the includes thus and putting it on a Teensy 4 shows no output nor warnings:
Code:
//#include <Wire.h>
//#include <i2c_driver_wire.cpp>
#include "i2c_driver.h"
#include "i2c_driver_wire.h"

Unless We also comment out ET.SendData(9).
Code:
//ET.sendData(9);

Then at least the program moves on.

Receiving Code that works Arduino->Arduino:
Code:
#include <Wire.h>
#include <EasyTransferI2C.h>

//create object
EasyTransferI2C ET; 

struct RECEIVE_DATA_STRUCTURE{
  //put your variable definitions here for the data you want to send
  //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
  byte addy;
  char l;
  byte cside;
  byte pn;
};

RECEIVE_DATA_STRUCTURE dataReceived;  

void setup() {
  Wire.begin(9);                // join i2c bus with address #9
  //start the library, pass in the data details and the name of the serial port. Can be Serial, Serial1, Serial2, etc. 
  ET.begin(details(dataReceived), &Wire);
  Wire.onReceive(receiveEvent); // register event
  Serial.begin(9600);           // start serial for output
}

void loop() {

	if(ET.receiveData()){
		//this is how you access the variables. [name of the group].[variable name]
		//since we have data, we will blink it out. 
		Serial.println("Received Data:");
		Serial.println(dataReceived.addy);
		Serial.println(dataReceived.l);
		Serial.println(dataReceived.cside);
		Serial.println(dataReceived.pn);
	}
	delay(100);
}

void receiveEvent(int numBytes) {}
 
Attempted to get 2 teensies going together on i2c using the master-reader and slave-sender test files. Spotty at best for me - but some things did appear to get through. Sometimes getting stuck in the buffer ("hello" all the time even though I'd physically diconnected the wires). And always really a "h hello" ... "monkey" seemed to go through fine. Oy.

Though I did recognize on Teensy1 that I'd hit it with a 5v interrupt on other pins(i2c pins still outputing 3.2V), and then promptly plugged the low side of the level shifter in to the 5V on teensy2 before, you know, just trying them together.

When I get more teensies to test on I'll be able to fill out more details.


Thanks again, Richard, for putting the work into this.
 
Hi Allister,

I've written the Wire implementation to conform to the Arduino documentation. This means that if you call to begin(10) then you start I2C in slave mode. As you've found out you have to call begin() without the port to get a master. (See https://www.arduino.cc/en/Reference/WireBegin) The port number isn't "optional" for a master. Only a master can initiate a data transfer so EasyTransferI2C::sendData() must not be called by a slave.

The problems you're describing with data sometimes getting through and sometimes not sounds like a pullup resistor issue. You need to use 2.2k or 1k external pull up resistors for the Teensy 4. I'd start with 1k. If the Nano/Nano connection is reliable then the pullups on that side are probably enough. If not, try 4.7k on that side.

I don't have a debugger either. I just use the serial output window and lots of Serial.print() statements. :(

The Arduino IDE is pretty crude so I use CLion (https://www.jetbrains.com/clion/). I've combined it with PlatformIO so I can now browse the code more comfortably. It was a pain in the rear to set up but worth it in my case because I've got quite a lot of code to wrangle.

cheers,
Richard
 
Here is some more information. Maybe relevant now or to someone developing the networking later. There are things somewhere on a lower level that aren't quite matching up. I can communicate bytes between the Teensy and an arduino (through the level shifter). BUT I cannot communicate chars! (It turns out I didn't fry my boards).

Re: Data sometimes - I didn't have the negative rail connected properly.

Also, I'm curious about not being able to do a sendData() from a slave. I did this specific test. Having two ET connections between themaster and the slave. ('ET2' in the code above). it did work between nanos at one point, returning code once called. Though I mangled the example for these experiments...

I have tested this code on 2xArduino's and it does work to send chars as well. (After having replaced i2c_driver_ with wire again, of course).

As well as using only the wire library on the teensy since it was master anyway. That worked, too. And really showed me how you'd written it to spec as nothing changed when I swapped from using wire to i2c_driver.

one more experiment to do after a reboot and see if I can get the other teensy running as well. Then I can at least find out if some weird thing with chars being a different size due to architecture or whatnot.

File: Easytranser_TX_Example
Code:
//#include <Wire.h>
#include <Arduino.h>
#include <i2c_driver.h>
#include <i2c_driver_wire.h>
#include <EasyTransferI2C.h>

//create object
EasyTransferI2C ET; 

struct SEND_DATA_STRUCTURE{
  //put your variable definitions here for the data you want to send
  //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
  int16_t blinks;
  int16_t pause;
  char puppy;
};

//give a name to the group of data
SEND_DATA_STRUCTURE mydata;

//define slave i2c address
#define I2C_SLAVE_ADDRESS 9

void setup(){
  Wire.begin();
  //start the library, pass in the data details and the name of the serial port. Can be Serial, Serial1, Serial2, etc.
  ET.begin(details(mydata), &Wire);
  
  pinMode(13, OUTPUT);
  
  randomSeed(analogRead(0));
  
}

void loop(){
  //this is how you access the variables. [name of the group].[variable name]
  mydata.blinks = random(5);
  mydata.pause = random(5);
  mydata.puppy = 'A';
  //send the data
  ET.sendData(I2C_SLAVE_ADDRESS);
  
  //Just for fun, we will blink it out too
   for(int i = mydata.blinks; i>0; i--){
      digitalWrite(13, HIGH);
      delay(mydata.pause * 100);
      digitalWrite(13, LOW);
      delay(mydata.pause * 100);
    }
  
  //delay(5000);
}

File: Easytransfer_RX_Example
Code:
#include <Wire.h>
#include <EasyTransferI2C.h>

//create object
EasyTransferI2C ET; 

struct RECEIVE_DATA_STRUCTURE{
  //put your variable definitions here for the data you want to receive
  //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
  int16_t blinks;
  int16_t pause;
  char puppy;
};

//give a name to the group of data
RECEIVE_DATA_STRUCTURE mydata;

//define slave i2c address
#define I2C_SLAVE_ADDRESS 9

void setup(){

	Serial.begin(9600);
	delay(1000);
	Serial.println("Startup");
  Wire.begin(I2C_SLAVE_ADDRESS);
  //start the library, pass in the data details and the name of the serial port. Can be Serial, Serial1, Serial2, etc. 
  ET.begin(details(mydata), &Wire);
  //define handler function on receiving data
  Wire.onReceive(receive);
  
  pinMode(13, OUTPUT);
  
}

void loop() {
  //check and see if a data packet has come in. 

  if(ET.receiveData()){
	  Serial.print("Character: ");
	  Serial.println(mydata.puppy);
    //this is how you access the variables. [name of the group].[variable name]
    //since we have data, we will blink it out. 
    for(int i = mydata.blinks; i>0; i--){
      digitalWrite(13, HIGH);
      delay(mydata.pause * 100);
      digitalWrite(13, LOW);
      delay(mydata.pause * 100);
    }
  }
}

void receive(int numBytes) {}
 
Here's a good one!

Teensy with i2c_driver as client (address 9)
Code:
//#include <Wire.h>
#include <Arduino.h>
#include <i2c_driver.h>
#include <i2c_driver_wire.h>
#include <EasyTransferI2C.h>
and Arduino with wire as master
Code:
#include <Wire.h>
//#include <Arduino.h>
//#include <i2c_driver.h>
//#include <i2c_driver_wire.h>
#include <EasyTransferI2C.h>
gives an i2c_scanner result of
Code:
09:52:04.777 -> 
09:52:04.777 -> I2C Scanner
09:52:04.777 -> Scanning...
09:52:04.777 -> I2C device found at address 09  !
09:52:04.809 -> done

Teensy with wire as master with i2c_scanner
Code:
#include <Wire.h>
//#include <Arduino.h>
//#include <i2c_driver.h>
//#include <i2c_driver_wire.h>
//#include <EasyTransferI2C.h>

and arduino with wire as client (address 9)
gives an i2c_scanner result of
Code:
10:03:55.468 -> 
10:03:55.468 -> I2C Scanner
10:03:55.468 -> Scanning...
10:03:55.468 -> I2C device found at address 09  !
10:03:55.468 -> done

Then we invert the i2c_driver/wire usage for the teensy running i2c_scanner
Code:
//#include <Wire.h>
#include <Arduino.h>
#include <i2c_driver.h>
#include <i2c_driver_wire.h>
//#include <EasyTransferI2C.h>

We get this:
Code:
10:06:10.276 -> Scanning...
10:06:10.276 -> I2C device found at address 01  !
10:06:10.276 -> I2C device found at address 02  !
10:06:10.276 -> I2C device found at address 03  !
10:06:10.276 -> I2C device found at address 04  !
10:06:10.276 -> I2C device found at address 05  !
10:06:10.276 -> I2C device found at address 06  !
10:06:10.276 -> I2C device found at address 07  !
10:06:10.276 -> I2C device found at address 08  !
10:06:10.276 -> I2C device found at address 09  !
10:06:10.276 -> I2C device found at address 010  !
10:06:10.276 -> I2C device found at address 011  !
10:06:10.276 -> I2C device found at address 012  !
10:06:10.276 -> I2C device found at address 013  !
10:06:10.276 -> I2C device found at address 014  !
10:06:10.276 -> I2C device found at address 015  !
10:06:10.276 -> I2C device found at address 016  !
10:06:10.276 -> I2C device found at address 017  !
10:06:10.276 -> I2C device found at address 018  !
10:06:10.276 -> I2C device found at address 019  !
10:06:10.276 -> I2C device found at address 020  !
10:06:10.276 -> I2C device found at address 021  !
10:06:10.276 -> I2C device found at address 022  !
10:06:10.276 -> I2C device found at address 023  !
10:06:10.276 -> I2C device found at address 024  !
10:06:10.276 -> I2C device found at address 025  !
10:06:10.276 -> I2C device found at address 026  !
10:06:10.276 -> I2C device found at address 027  !
10:06:10.276 -> I2C device found at address 028  !
10:06:10.276 -> I2C device found at address 029  !
10:06:10.276 -> I2C device found at address 030  !
10:06:10.276 -> I2C device found at address 031  !
10:06:10.276 -> I2C device found at address 032  !
10:06:10.276 -> I2C device found at address 033  !
10:06:10.276 -> I2C device found at address 034  !
10:06:10.276 -> I2C device found at address 035  !
10:06:10.276 -> I2C device found at address 036  !
10:06:10.276 -> I2C device found at address 037  !
10:06:10.276 -> I2C device found at address 038  !
10:06:10.276 -> I2C device found at address 039  !
10:06:10.276 -> I2C device found at address 040  !
10:06:10.276 -> I2C device found at address 041  !
10:06:10.276 -> I2C device found at address 042  !
10:06:10.276 -> I2C device found at address 043  !
10:06:10.276 -> I2C device found at address 044  !
10:06:10.276 -> I2C device found at address 045  !
10:06:10.276 -> I2C device found at address 046  !
10:06:10.276 -> I2C device found at address 047  !
10:06:10.276 -> I2C device found at address 048  !
10:06:10.276 -> I2C device found at address 049  !
10:06:10.276 -> I2C device found at address 050  !
10:06:10.276 -> I2C device found at address 051  !
10:06:10.276 -> I2C device found at address 052  !
10:06:10.276 -> I2C device found at address 053  !
10:06:10.276 -> I2C device found at address 054  !
10:06:10.276 -> I2C device found at address 055  !
10:06:10.276 -> I2C device found at address 056  !
10:06:10.276 -> I2C device found at address 057  !
10:06:10.276 -> I2C device found at address 058  !
10:06:10.276 -> I2C device found at address 059  !
10:06:10.276 -> I2C device found at address 060  !
10:06:10.276 -> I2C device found at address 061  !
10:06:10.276 -> I2C device found at address 062  !
10:06:10.276 -> I2C device found at address 063  !
10:06:10.276 -> I2C device found at address 064  !
10:06:10.276 -> I2C device found at address 065  !
10:06:10.276 -> I2C device found at address 066  !
10:06:10.276 -> I2C device found at address 067  !
10:06:10.276 -> I2C device found at address 068  !
10:06:10.276 -> I2C device found at address 069  !
10:06:10.276 -> I2C device found at address 070  !
10:06:10.276 -> I2C device found at address 071  !
10:06:10.276 -> I2C device found at address 072  !
10:06:10.276 -> I2C device found at address 073  !
10:06:10.276 -> I2C device found at address 074  !
10:06:10.276 -> I2C device found at address 075  !
10:06:10.276 -> I2C device found at address 076  !
10:06:10.276 -> I2C device found at address 077  !
10:06:10.276 -> I2C device found at address 078  !
10:06:10.276 -> I2C device found at address 079  !
10:06:10.276 -> I2C device found at address 080  !
10:06:10.276 -> I2C device found at address 081  !
10:06:10.276 -> I2C device found at address 082  !
10:06:10.276 -> I2C device found at address 083  !
10:06:10.276 -> I2C device found at address 084  !
10:06:10.276 -> I2C device found at address 085  !
10:06:10.276 -> I2C device found at address 086  !
10:06:10.276 -> I2C device found at address 087  !
10:06:10.276 -> I2C device found at address 088  !
10:06:10.276 -> I2C device found at address 089  !
10:06:10.276 -> I2C device found at address 090  !
10:06:10.276 -> I2C device found at address 091  !
10:06:10.276 -> I2C device found at address 092  !
10:06:10.276 -> I2C device found at address 093  !
10:06:10.276 -> I2C device found at address 094  !
10:06:10.276 -> I2C device found at address 095  !
10:06:10.276 -> I2C device found at address 096  !
10:06:10.276 -> I2C device found at address 097  !
10:06:10.276 -> I2C device found at address 098  !
10:06:10.276 -> I2C device found at address 099  !
10:06:10.276 -> I2C device found at address 0100  !
10:06:10.276 -> I2C device found at address 0101  !
10:06:10.276 -> I2C device found at address 0102  !
10:06:10.276 -> I2C device found at address 0103  !
10:06:10.276 -> I2C device found at address 0104  !
10:06:10.276 -> I2C device found at address 0105  !
10:06:10.276 -> I2C device found at address 0106  !
10:06:10.276 -> I2C device found at address 0107  !
10:06:10.276 -> I2C device found at address 0108  !
10:06:10.276 -> I2C device found at address 0109  !
10:06:10.276 -> I2C device found at address 0110  !
10:06:10.276 -> I2C device found at address 0111  !
10:06:10.276 -> I2C device found at address 0112  !
10:06:10.276 -> I2C device found at address 0113  !
10:06:10.276 -> I2C device found at address 0114  !
10:06:10.276 -> I2C device found at address 0115  !
10:06:10.276 -> I2C device found at address 0116  !
10:06:10.276 -> I2C device found at address 0117  !
10:06:10.276 -> I2C device found at address 0118  !
10:06:10.276 -> I2C device found at address 0119  !
10:06:10.276 -> I2C device found at address 0120  !
10:06:10.276 -> I2C device found at address 0121  !
10:06:10.276 -> I2C device found at address 0122  !
10:06:10.276 -> I2C device found at address 0123  !
10:06:10.276 -> I2C device found at address 0124  !
10:06:10.276 -> I2C device found at address 0125  !
10:06:10.276 -> I2C device found at address 0126  !
10:06:10.276 -> done

Sanity check, revert to using wire on the teensy result:
Code:
10:14:42.738 -> 
10:14:42.738 -> I2C Scanner
10:14:42.738 -> Scanning...
10:14:42.738 -> I2C device found at address 09  !
10:14:42.738 -> done
10:14:42.738 ->

i2c_scanner implementation: (RX/client code used is as above)
Code:
  // --------------------------------------
// i2c_scanner
//
// Version 1
//    This program (or code that looks like it)
//    can be found in many places.
//    For example on the Arduino.cc forum.
//    The original author is not know.
// Version 2, Juni 2012, Using Arduino 1.0.1
//     Adapted to be as simple as possible by Arduino.cc user Krodal
// Version 3, Feb 26  2013
//    V3 by louarnold
// Version 4, March 3, 2013, Using Arduino 1.0.3
//    by Arduino.cc user Krodal.
//    Changes by louarnold removed.
//    Scanning addresses changed from 0...127 to 1...119,
//    according to the i2c scanner by Nick Gammon
//    https://www.gammon.com.au/forum/?id=10896
// Version 5, March 28, 2013
//    As version 4, but address scans now to 127.
//    A sensor seems to use address 120.
// Version 6, November 27, 2015.
//    Added waiting for the Leonardo serial communication.
//
//
// This sketch tests the standard 7-bit addresses
// Devices with higher bit address might not be seen properly.
//
 
//#include <Wire.h>
#include <Arduino.h>
#include <i2c_driver.h>
#include <i2c_driver_wire.h>
#include <EasyTransferI2C.h>
 
 
void setup()
{
  Wire.begin();
 
  Serial.begin(9600);
  while (!Serial);             // Leonardo: wait for serial monitor
  Serial.println("\nI2C Scanner");
}
 
 
void loop()
{
  byte error, address;
  int nDevices;
 
  Serial.println("Scanning...");
 
  nDevices = 0;
  for(address = 1; address < 127; address++ )
  {
    // The i2c_scanner uses the return value of
    // the Write.endTransmisstion to see if
    // a device did acknowledge to the address.
    Wire.beginTransmission(address);
    error = Wire.endTransmission();
 
    if (error == 0)
    {
      Serial.print("I2C device found at address 0");
      //if (address<16)
        //Serial.print("0");
      Serial.print(address);
      Serial.println("  !");
 
      nDevices++;
    }
    else if (error==4)
    {
      Serial.print("Unknown error at address ");
      //if (address<16)
       // Serial.print("0");
      Serial.println(address);
    }    
  }
  if (nDevices == 0)
    Serial.println("No I2C devices found\n");
  else
    Serial.println("done\n");
 
  delay(5000);           // wait 5 seconds for next scan
}
 
I can't think of any way that I2C would transfer a byte but not a char as they're the same thing IIRC.

The only thing I can think that might affect it is padding. The Teensy is a 32 bit CPU so your SEND_DATA_STRUCTURE struct will have some padding to make it a multiple of 4 bytes. I'm guess the nano is an 8 bit CPU so it won't. The practical upshot of this is that sizeof(SEND_DATA_STRUCTURE) will be different on the 2 systems. This could cause easy transfer to send the wrong number of bytes. The members of the structures might start at different byte indices as well.

The thing with the scanner sounds like it might be bug in i2c_driver_wire.h. I'll check it out.
 
I've figured out why the scanner fails. The problem is that it relies on Wire.endTransmission() talking to the slave even when it doesn't have any data to send. My implementation doesn't support that. I can see the value in being able to scan for slaves though so I'll try to add support but that isn't going to be simple.

In the mean time you can work round it by sending something to the slave. What you write depends on what it's safe to send to the slave unexpectedly.

Write a byte at line 61 like this:
Code:
Wire.beginTransmission(address);
Wire.write(0xFF);
error = Wire.endTransmission();
 
I've tweaked the library and uploaded a new version. The address scanner should now work without having to add the "Wire.write(0xFF);" line.

Thanks for reporting the issue.

cheers,
Richard
 
Status
Not open for further replies.
Back
Top