New I2C library for Teensy3

Hello. I am new to this whole arduino stuff. Made a few cool things. However, I have a 1602 I2C display and a teensy 3.2 and I'm looking for how to make them go together. This seems hopeful, however upon opening the example basic_echo it does not work. I notice in the comments of the code it says for 3.6 and, well. I'd like to ask for help, if possible. All pins could be blank or filled, if required. Thanks, and sorry if this is confusing. Thank you, have a nice day!
 
The basic_echo example is for doing communication tests between the two integrated I2C h/w units of the T3.5 and T3.6, one set up as I2C master, the other one as I2C slave. As you understood already, that can't work on a T3.2.

To check basic communication with EXTERNAL I2C devices like displays and other, use File -> Examples -> i2c_t3 -> basic_scanner and, if needed, fix your wiring of the display until it properly responds at its i2c base address.
 
On the Teensy, you typically need to add two pull-up resistors, one between pin 18/A4 to 3.3v and the other between pin 19/A5 and 3.3v. These resistors are needed to enable i2c to work. Note, some devices have resistors already. As Theremingenieur says, run the scanner first, and if finds the device, then it should work. The typical resistor needed for Teensy's i2c is 2.2K resistors, but you can use other resistors between 2.2K and 4.7K (4.7K is the appropriate value for 5v systems). If you use a higher resistor, it will prevent you from using higher i2c bus speeds (the default speed should work).

When I still used the 16x2 displays, one of my displays worked with 5v. With that display, I needed to use level shifters. In general, if you need a level shifter, it is probably simpler to get a new display that runs on 3.3v. At the moment, you can get a 128x64 OLED display from a US seller on ebay for under $6:

You would use the Adafruit_SSD1306 driver for this, and look at the ssd1306_128x64_i2c example. While the Adafruit driver is open source, if you buy the official Adafruit device, it funds other device/driver development:
 
Hi nox771

just a short question : is it correct that each bus has its own callback functions (onTransmitDone, onReqFromDone etc ? I think so but want to be sure.
 
I am about 99% sure that each wire object (buss) has it's own callback function, which is stored in the Wire object and is called from the ISR for the specific Wire buss.
 
All, I uploaded a v10.1 release on GitHub and the top-post.

This has a fix for a subtle priority escalation bug involving nested Wire calls inside callbacks (refer to: https://github.com/nox771/i2c_t3/issues/14). There is now a user #define (I2C_DISABLE_PRIORITY_CHECK) to disable the checks if anyone has further problems with it (in that case set priorities manually). No other major change.
 
Hi nox 771

implemented the latest version tested especially the onError() callback and resetBus(). Works really nice. Is it a big deal to provide information about the I2C adress that
was involved in the error occurence - something like onError(uint8_t i2caddress) ?
 
Is it a big deal to provide information about the I2C adress that was involved in the error occurence - something like onError(uint8_t i2caddress) ?

The library error tracking functions do not log addresses (it is more of an error "counting" system instead of an error "logging" system). This is really the only way to do it since a true error log could use an indeterminate amount of space.

User code could augment the onError() callback to create an actual error log. This would be something like the Master storing the Slave address in some variable, and then grabbing it if onError() occurs, then writing the output to a SD card or similar (with relevant space available checks and all that). If there was an RTC it could also include a timestamp.
 
The library error tracking functions do not log addresses (it is more of an error "counting" system instead of an error "logging" system). This is really the only way to do it since a true error log could use an indeterminate amount of space.

User code could augment the onError() callback to create an actual error log. This would be something like the Master storing the Slave address in some variable, and then grabbing it if onError() occurs, then writing the output to a SD card or similar (with relevant space available checks and all that). If there was an RTC it could also include a timestamp.

Hi

I did not mean having a logging system implemented. I had a quick look at the code and had the impression that someting like :

if(i2c->user_onError != nullptr) i2c->user_onError(addr); // run Error callback if
instead of
if(i2c->user_onError != nullptr) i2c->user_onError(); // run Error callback if

might be helpfull for erro-tracking on busses with multiple slaves and not require a big change in the coding.
 
Slave blocks bus

I have a Teensy 3.2 operating as a slave to a Raspberry Pi. It works just fine, often for several minutes, and then everything stops. Putting a scope on the bus shows that both the SDA an SCL are being held low. Powering down the Teensy brings everything back and after resetting, the Teensy joins the bus again until the next time it stops. The initialization and operation are pretty standard:

Wire.begin(I2C_SLAVE, 0X1c, I2C_PINS_18_19, I2C_PULLUP_EXT, 400000);
Wire.onReceive(receiveEvent);
Wire.onRequest(requestEvent);

The callbacks are just like the basicSlave example. There is an IMU on the same bus but it is completely synchronous with the Teensy so there is unlikely to be any bus contention. Any help on debugging this would be greatly appreciated.

By the way, it is probably irrelevant but I will add this piece of information in case it is related. In trying to get this to work I tried to get the standard Wire library to work. It appeared to work but the high bit on many of the bytes seemed to be wrong so that I would get 128 when the the value should have been 0.
 
In trying to get this to work I tried to get the standard Wire library to work. It appeared to work but the high bit on many of the bytes seemed to be wrong so that I would get 128 when the the value should have been 0.

Would you like me to look into that issue?

If so, please start a new thread about this problem using RPi and the normal Wire lib. Follow the "forum rule" with the complete code to be used on both sides. Please be specific about exactly what needs to be done in the RPi side. Usually when people have posted issues involving Raspberry Pi, setting up a Pi and getting everything working to the point of actually reproducing the problem has required far too much time. On the Teensy side, I can just copy & paste into Auduino and upload. If there's some way to quickly set up the RPi side, please be specific and please understand I won't spend more than about 20 minutes if the code or instructions don't work when I try them on the RPi side.
 
Teensy3.2-RPi issue

Would you like me to look into that issue?

If so, please start a new thread about this problem using RPi and the normal Wire lib. Follow the "forum rule" with the complete code to be used on both sides. Please be specific about exactly what needs to be done in the RPi side. Usually when people have posted issues involving Raspberry Pi, setting up a Pi and getting everything working to the point of actually reproducing the problem has required far too much time. On the Teensy side, I can just copy & paste into Auduino and upload. If there's some way to quickly set up the RPi side, please be specific and please understand I won't spend more than about 20 minutes if the code or instructions don't work when I try them on the RPi side.

The code is too large and complex to post. I will spend some time extracting out the critical parts to reproduce the problem and then post it to a new thread as you suggest.
 
Teensy3.2-RPi issue

While preparing the code for Paul, I discovered a bug in the compile environment: I had the CPU speed set to "96 (overclock)". After setting this correctly, the Wire library works just fine and I am happy but a little embarrassed. However, the "New i2c library still locks up the bus from time-to-time. If anyone thinks this is a problem and wants to look into it, I am willing to help out in any way I can.
 
Larger Buffer Size Doesn't Work?

I'm trying to send RGB led data from one Teensy to another. I have 360 leds to send so I'm sending a 2 byte LED address and 3 bytes for R, G, and B. Only 258 bytes will come through a single transmission which I discovered is due to the default buffer lengths of 259. However, setting the buffer lengths to 1801 in the header file does not seem to fix this issue, I still only get 258 bytes. Does something else need to be changed in order to send larger payloads?
 
I'm trying to send RGB led data from one Teensy to another. I have 360 leds to send so I'm sending a 2 byte LED address and 3 bytes for R, G, and B. Only 258 bytes will come through a single transmission which I discovered is due to the default buffer lengths of 259. However, setting the buffer lengths to 1801 in the header file does not seem to fix this issue, I still only get 258 bytes. Does something else need to be changed in order to send larger payloads?

Increasing the buffer sizes should work. 258 bytes sounds correct for the default buffer size, as 1st byte is used for slave address. Are you using size_t as your index? If you use uint8_t as a count (perhaps elsewhere in code) it will limit at 255 (due to 8bits).

You will have to provide more information, something like an example of the send/receive code.
 
I'm getting 258 bytes, not 255 so it would seem that it should be reading the Buffer Length setting correctly but doesn't seem to change when I edit the header.

NUM_LEDS is 720 and I'm sending the second half (360 LEDs) of the FastLED array to the slave.
The LED array on the slave is of size 360.
Using this code I only receive through the red byte of leds[51] which is the 258th byte, and Serial.print(len) in the receive code gives 258.

From i2c_t3.h:
Code:
#define I2C_TX_BUFFER_LENGTH 1801
#define I2C_RX_BUFFER_LENGTH 1801

Master code for sending (runs in main loop every 5 seconds):
Code:
Wire.beginTransmission(8); // transmit to device #8
for (int i = NUM_LEDS / 2; i < NUM_LEDS; i++) {
    
  byte addr[2];
  addr[0] = (i >> 8) & 0xFF;  // Bit shift 16 bit LED address into two bytes
  addr[1] = i & 0xFF;
  
  Wire.write(addr, 2);        // Send LED address
  Wire.write(leds[i].r);      // Send LED data
  Wire.write(leds[i].g);
  Wire.write(leds[i].b);
}
Wire.endTransmission();    // stop transmitting

Slave code for recieving:
Code:
void receiveEvent(size_t len) {
  Serial.println("Data Received");
  Serial.print("Package Size: "); Serial.println(len);
  
  while (Wire.available() > 0) {
    int addr;
    byte addr_MSB = Wire.read();  // Get MSB of address
    byte addr_LSB = Wire.read();  // Get LSB of address

    addr = addr_MSB;
    addr = addr << 8 | addr_LSB;  // Bit shift to get 16 bit address
    
    addr -= 360;                  // Shift address for smaller slave array

    leds[addr].r = Wire.read();   // Assign LED data received to above address
    leds[addr].g = Wire.read();
    leds[addr].b = Wire.read();
  }
}

I can get all the data over if I change the code to do 8 transmissions of 45 LEDs each (so the buffer length is only 225), but this isn't as clean or efficient and I'd really like to get a single transmission working.
 
I'm getting 258 bytes, not 255 so it would seem that it should be reading the Buffer Length setting correctly but doesn't seem to change when I edit the header.

NUM_LEDS is 720 and I'm sending the second half (360 LEDs) of the FastLED array to the slave.
The LED array on the slave is of size 360.
Using this code I only receive through the red byte of leds[51] which is the 258th byte, and Serial.print(len) in the receive code gives 258.

There seems to be some conversions between signed and unsigned types going on. Try first working exclusively with unsigned types, and avoid int if possible. The original Arduino Wire lib annoyingly uses int, which is signed 16-bit, for what is fundamentally an unsigned 8-bit operation (uint8_t).

I'm not sure if this will change anything, but I'll try to code the examples below this way. I don't have my setup with me at the moment, so hopefully this code is working.

I also added more diagnostic prints to both routines, so Master and Slave prints can be compared with each other. Master should print LED data it is sending with a final buffer length. Slave should print buffer length received with LED data.

Master code:
Code:
Wire.beginTransmission(8); // transmit to device #8
for(size_t i = NUM_LEDS/2; i < NUM_LEDS; i++) {
  Wire.write((i>>8) & 0xFF);
  Wire.write(i & 0xFF);
  Wire.write(leds[i].r);      // Send LED data
  Wire.write(leds[i].g);
  Wire.write(leds[i].b);
  Serial.printf("LED Addr: %d   RGB = %02X %02X %02X\n", i, leds[i].r, leds[i].g, leds[i].b);
}
Serial.printf("Length to send: %d\n", Wire.i2c->txBufferLength); // check buffer size before sending
Wire.endTransmission();    // send data

Slave code:
Code:
void receiveEvent(size_t len) {
  Serial.printf("Data receive size: %d\n", len);
  
  while (Wire.available()) {
    uint8_t msb = Wire.readByte();
    size_t addr = ((msb << 8) | Wire.readByte()) - 360;
    leds[addr].r = Wire.read();   // Assign LED data received to above address
    leds[addr].g = Wire.read();
    leds[addr].b = Wire.read();
    Serial.printf("LED Addr: %d   RGB = %02X %02X %02X\n", addr, leds[addr].r, leds[addr].g, leds[addr].b);
  }
}

Also, just to check, you are changing the Tx/Rx buffer lengths on both the Master and Slave devices right? What you describe could happen if one or the other is compiled at 259 bytes Tx/Rx.
 
Thanks! I'll give that a try when I get home tonight.

Also, just to check, you are changing the Tx/Rx buffer lengths on both the Master and Slave devices right? What you describe could happen if one or the other is compiled at 259 bytes Tx/Rx.

I'm changing the defines for I2C_TX_BUFFER_LENGTH and I2C_RX_BUFFER_LENGTH in your library's header file (in ~\Arduino\libraries\i2c_t3-master). That should be pulled in for both the master and the slave when each are compiled right? Or is there a different way to edit libraries that I'm not aware of?
 
Thanks! I'll give that a try when I get home tonight.



I'm changing the defines for I2C_TX_BUFFER_LENGTH and I2C_RX_BUFFER_LENGTH in your library's header file (in ~\Arduino\libraries\i2c_t3-master). That should be pulled in for both the master and the slave when each are compiled right? Or is there a different way to edit libraries that I'm not aware of?

Ah, I have an idea of what it might be. Change your downloaded dir name from "i2c_t3-master" to "i2c_t3". Teensyduino ships with i2c_t3 by default, so it will pick that one first unless you have one in libraries (like you do). But it will only take the one from libraries if the dir name matches. If the dir name is "i2c_t3-master" it likely won't use it (at least that is the way the Arduino build system used to work, I haven't checked recently).
 
Ahhhh I bet that's it! I didn't realize i2c_t3 was included by default as I found it by searching around the Googles. Thank you so much for your quick and helpful support! If that doesn't do the trick when I get home, I'll be back, otherwise have a lovely day!
 
I would like to use this library for a commercial project but it is published using a LGPL license. Because microcontroller binaries are linked statically from my understanding I would have to publish our proprietary source code or "object files" to satisfy the license. Was this intended by choosing the LGPL? Is there a variant of this library available under BSD or a commercial license? Thanks!
 
I would like to use this library for a commercial project but it is published using a LGPL license. Because microcontroller binaries are linked statically from my understanding I would have to publish our proprietary source code or "object files" to satisfy the license. Was this intended by choosing the LGPL? Is there a variant of this library available under BSD or a commercial license? Thanks!

Back when I first started the lib the code was based on whatever Wire was in Teensyduino at the time, which was listed as LGPL, that is why it uses LGPL (IIRC Arduino Wire has a LGPL header). However since then about 95% of it has been rewritten, so it could maybe be changed. I would have to search out what the differences are between the latest version and the pre-version1 code. I'm not an expert in this area so I don't know at what point the original license becomes meaningless. If anyone knows maybe they can let me know. I agree LGPL on an embedded project is a little odd, as the norm is for everything to static compile.

Overall this is a hobby project for me and the whole point is to share it and let it get used. In my opinion people can use it however they want, it doesn't matter to me. From my searches some LGPL has "static compile allowed" clauses, maybe that is an easier change?
 
Back
Top