I2c get stuck with daisy chained Attiny85

Status
Not open for further replies.

esapode688

Well-known member
Following this tutorial: http://projectsfromtech.blogspot.co.uk/2014/01/i2c-hc-sr04-sonar-module-attiny85-i2c.html
; i created a series of boards to read the distance from HcSr04 sensors.

PRecisely: 12 sensors.

I bootloaded each attiny 85 @ 16mhz and Daisychained 12 of them On the same I2c line; all powered by a 5 volt 10 amp PSU.

Each tiny board has his own 4.7k pullups and a 10k resistor between reset and 5 volt.

On each one runs this code:

Code:
/*
 This is the firmware for every I2c Sensor
 NOTE: Every board should have his own unique I2c address
 Define it differently for every board
 
 This program sends just the distance value over I2c

 Based on the original New Ping implementation
*/
#include <TinyWireS.h>              // Requires fork by Rambo with onRequest support
#include <TinyNewPing.h>            // NewPing library modified for ATtiny

const byte SensorOnePin = 3;        // Sensor 1 is connected to PB3
const int I2CSlaveAddress = 12;      // I2C Address.

byte Distance;                              // Where the Distance is stored (8 bit unsigned)

#define MAX_DISTANCE 200 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.
NewPing SensorOne (SensorOnePin, SensorOnePin, MAX_DISTANCE);       // Define the Sensor
 
void setup()
{
  delay(290); //We join the Bus with a delay based on actual channel number to avoid crossfading

  TinyWireS.begin(I2CSlaveAddress);      // Begin I2C Communication
  TinyWireS.onRequest(transmit);         // When requested, call function transmit()

}


void loop()
{
    Distance = SensorOne.ping_cm();        // Get distance in cm. Could be changed to 
    //Distance = SensorOne.ping_median(5)/US_ROUNDTRIP_CM;       // Take the median of 5 readings

    delay(30);                             // Delay to avoid interference from last ping

}  //end loop()



void transmit()
{
  TinyWireS.send(Distance);                 // Send last recorded distance for current sensor
}

Note that every Tiny has his own address and join the I2c Bus with a delay of 10 ms respect the previous.

I read all of them using this other code:

Code:
#include <Wire.h>

byte Distance[11];                              // Where the Distance is stored (8 bit unsigned)
int i,p,x;

void setup()
{
  Wire.begin();
  Serial.begin(115200);
  Serial.println("Setup");
  pinMode(13, OUTPUT);
}



void loop()
{
  digitalWrite(13, HIGH);
  //loop throug all the sensors and store their values in an array
  p=0;
  for (i=12;i<25;i++)
   {
     digitalWrite(13, LOW);
     //if (i==19) i=20;
     Wire.requestFrom(i, 1);                  // The TinyWire library only allows for one byte to be requested at a time
     while (Wire.available() == 0)  ;         // Wait until there is data in the I2C buffer
     Distance[p] = Wire.read();                  // Read the first (and hopefully only) byte in the I2C buffer
     p++;
     delay(2);
   }
  delay(68);
 
  for (x=0;x<3;x++)
   {
     Serial.print("S");
     Serial.print(x);
     Serial.print("  ");
     Serial.print(Distance[x]);
     Serial.print("   ");
                                   // The sensor only updates every 30 ms. Asking for new data quicker than that is useless
   }
 
  Serial.println("  ");
}

The problem is that all the line randomly hangs after a while and data stop flowing until i reset the arduino.

I tried it both on arduino and teensy 3 and i get the same problem.

Anyone got an idea on what could be?
 
...

I bootloaded each attiny 85 @ 16mhz and Daisychained 12 of them On the same I2c line; all powered by a 5 volt 10 amp PSU.

Each tiny board has his own 4.7k pullups and a 10k resistor between reset and 5 volt.

...

The problem is that all the line randomly hangs after a while and data stop flowing until i reset the arduino.

I tried it both on arduino and teensy 3 and i get the same problem.

Anyone got an idea on what could be?

without reading any of the code very much I spot that you have pullups on each board - if your wire/track lengths are not longer than recommended for I2C then all those 4.7K resistors in parallel will be making the overall pullup on your I2C lines about 391 Ohms if I haven't forgotten how to calculate resistors in parallel; 1/((1/4700)+(1/4700)+(1/4700)+(1/4700)+(1/4700)+(1/4700)+(1/4700)+(1/4700)+(1/4700)+(1/4700)+(1/4700)+(1/4700)) = 391.666666666667

I think I've seen successful projects (or experiments at least) where 200 Ohms wasn't a problem for them so before strongly advising you to remove all those resistors from each board to only put one pair of 2K resistors on one board I think I had better ask you what the distance from the 'master' to each 'slave' is in your design - is everything on the one PCB? (can you measure the tracks easily enough?) or is everything breadboarded? or have you some rats nest of wires in some other situation?

Pictures are cool - hook it up to your Teensy 3.x and snap off a couple of pics for us to look at please :)
 
So, I re-read your post and actually looked at your code, waiting 'while (Wire.available() == 0);' is a very likely culprit for the apparent lock up - the following should be more revealing for you, please try it in the Teensy 3.x

Code:
#include <Wire.h>

byte Distance[11];                              // Where the Distance is stored (8 bit unsigned)
int i,p,x;

void setup()
{
  Wire.begin();
  Serial.begin(115200);
  Serial.println("Setup");
  pinMode(13, OUTPUT);
}



void loop()
{
  digitalWrite(13, HIGH);
  //loop throug all the sensors and store their values in an array
  p=0;
  for (i=12;i<25;i++)
   {
     digitalWrite(13, LOW);
     //if (i==19) i=20;
     if (Wire.requestFrom(i, 1)==1) {

       Distance[p] = Wire.read();
     } else {
       Distance[p]=0; // don't have to set this but if setting it then use any number you like.
       Serial.printf("Reading slave %i failed\n",i); // now we can see which unit has trouble, probably too far away from master.
     }
     p++;
     delay(2);
   }
  delay(68);
 
  for (x=0;x<3;x++)
   {
     Serial.print("S");
     Serial.print(x);
     Serial.print("  ");
     Serial.print(Distance[x]);
     Serial.print("   ");
                                   // The sensor only updates every 30 ms. Asking for new data quicker than that is useless
   }
 
  Serial.println("  ");
}
 
+1 what robsoles said. Also there is no good reason for a delay to add any of the slaves to the bus. IIRC all salves need to be on the bus before the master starts talking to any of them. Once you have the electrical things figured out it may be easiest to run an I2C scanner sketch and see what I2C addresses it reports. This HERE is an excellent article about the effects of different pullup resistors.
 
Pictures are cool - hook it up to your Teensy 3.x and snap off a couple of pics for us to look at please :)


Here there are

Close up of a board


and a pic of the entire chain connected to teensy 3.1




In the pic you don't see the ultrasonic sensors I'm using : http://wooo.altervista.org/wp-content/uploads/2013/09/hc-sr04.jpg (one on each board)

and the 5 volt powersupply (Dc/Dc converter) : http://www.mini-box.com/DCDC-USB

As you see each board is connected to the next using 4 30 cm jumpers. They carry both power and signal.

I measured the voltage at the end of the chain and its still 5 volt.

The chain is approxximately 5 meter long.
 
Does running my edit of your sketch report failed read with the furthest members of the I2C bus the most?

Have you got an oscilloscope? Preferably one you can capture as images like they did in the article Headroom linked to; http://dsscircuits.com/index.php/articles/47-effects-of-varying-i2c-pull-up-resistors

If you have a two channel scope you can probe at the master SCL pin with channel A and probe an SCL pin at the far end of the chain to observe if the signal is ok or not.
 
The chain is approxximately 5 meter long.

There's the culprit! I say that with such confidence because with the first prototype for my LED lighting system (TippyLighting ling in my signature) I had the same issue. I had connected 5 LED driver boards per I2C with generic cable to an Arduino Uno over around 4 meters. On each of the LED drivers had 2 I2C devices on board so there were 11 I2C devices on the bus. After running for a while, sometimes 30 min, sometimes several hours, the system came to a halt. It took me a while to investigate the issue.

Pullup resistors

The first thing were the number pull-up resistors on the I2C bus. Each of the LED driver boards (the second link in my signature ) had their own set of 1.8K pull-up resistors. As they are in parallel that calculates to a 360ohm resistance overall. This may still work but is really totally out of I2C specifications. for a 5V I2C system 1.8 K is about as low was the spec allows. The I2C spec allows the I2C pins to only sink about 3mA current. According to ohms law at 5V a currnt of ~2.8mA flows through a 1.8K resistor. It just shows how much headroom is designed into some chips. So
I removed these from 4 of the 5 LED driver boards which left only 1.8K per SDA/SCL and that significantly improved performance.

Unfortunately that still did not eliminate the entire problem as it still became stuck occasionally. I started using an alternative I2C library that had a timeout-reset feature. That feature eliminated the symptom (the occasional stop) but of course not the root cause. In your case that is not an option I believe as you are using a wire library specifically for the Tiny's

I2C Bus capacitance
The limiting factor on the I2C bus for cable length is the overall bus capacitance with the cable capacitance being the biggest factor. The longer the cable and the more devices are on the bus the higher the bus capacitance is. IIRC the limit is 400pF with is in fact pretty little. 3 mA are needed to discharge that bus capacitance. If the bus capacitance is too large the 3mA are not enough to discharge the bus capacitance and the signal quickly degrades to the point where the voltage does rise enough to indicate logic high.
  • You could re-run your boards, integrate little Ethernet sockets and use pre-configured Ethernet Cables (these can be had cheaply at $1 a piece with RJ45 plugs at each end) to connect.
  • You could also try to lower the I2C bus frequency from the standard 100KHz to, say 50KHz or even less. I am not sure the tiny wire library allows that.

I2C buffer chip
This is the last resort, which I have used for future systems. I am using a I2C buffer chip which allows 30mA/4000pF of bus capacitance. I am using a PCA9600, which allows me to use the Teesny3.x at 1MHz and more of I2C bus frequency even on 5m of Cat5 Ethernet cable. Works like a charm!

In general I believe an Oscilloscope is need to really troubleshoot I2C bus problems, however, as you've already got the chips talking but "just" got the bus stuck I believe my advice covers 98% of the problem.
 
Last edited:
This is interesting. I'm gonna remove the resistors and see what happen.

Unluckly i don't have neither an oscilloscope nor a bus analyzer.


I'm starting to consider on moving to RS485 in the next version of the boards in order to avoid troubles.

How easy is to create a daisychain with rs485. is there any library?
 
First the disclaimer: I have no practical experience with RS485 ;-)
But I have also been very interested in the optimal wired system for my purposes and have researched and read many forum entries here and on the EEVblog and elsewhere.

RS485 is less a protocol but a brilliant electrical specification. It uses differential signals and as such is very immune to electrical noise. If you try to connect sub-systems that have sufficient own processing power such as connection several MCU over long distances then it's the way to go.
However:
  • RS485 it will require additional hardware alas, it's not much. Somewhat similar, maybe a little more than to what you'd have to use to extend a multi point I2C bus.
  • As opposed to I2C, which is implemented in hardware in most MCUs and does not require processing power, RS485 is a little more demanding. The I2C bus spec is also a protocol whereas with RS485 you'd have to implement your own or use an existing library.

In your case it depends on what else needs to be processed with your ATtinys.
The neat thing with I2C is that if you already have established communication and functioning code then extending the I2C bus is dead simple.

Either way, I'd use of-the-shelf Cat5 (unshielded) or depending on the application CAT6(shielded) Ethernet Cable to connect everything as the spec on ethernet Cable in terms of capacitance etc. is very tight. You can use two pairs GND/SCL and GND/SDa for the I2C signals and still have two pairs left to power the individual boards similar to PoE.

Hee i a little schematic of how to extend the I2C bus (Only SCL or SDA shown):
I2C-Bus extender.png

I have an oscilloscope and have been able to verify that the signal still looks crisp with the Teensy 3 and two of my LED shields connected on 20Ft of CAT5 Ethernet cable at 1.5MHz!
 
Last edited:
OP says they tried it with Teensy 3 in their OP, that's not really a strong indication that they will finalise their project utilising any PJRC stuff tho - If it is within scope of what can be done with any PJRC product and the majority of required response is as applicable to a PJRC product as it is to whatever foreign product I think it would be a pity to discourage much.
 
EUREKAAAAAAAAAAAAAAAAAA

I solved it.

The problem was quite mixed.

First of all i removed all the pullup resistors keeping a 4.7 k pullup on the first and last board of the chain.

It started to work much better but still getting stuck.

Bootloading the tinys at 8mhz instead of 16 solved the problem on arduino but still kept it on teensy.


The best thing was discovering the i2c library for teensy 3. It works amazingly well.

It solved every problem. I even tried with sensors and works perfectly on teensy 3.1 but only with the new wire library wrote for t3

Thank you to everyone for the help :D
 
so you did not shorten your I2C bus and you did not add buffering devices or any other electrical change other than removing the extra resistors it never needed?

I've always thought I2C should be possible over longer distances on good conductors, 5 meters is a new maximum I've now heard of - cool.
 
I'd like to see some code. Based on my experience I have a hard time believing that a signal integrity problem can be reliably solved with software only.

Bootloading the tinys at 8mhz instead of 16 solved the problem on arduino but still kept it on teensy.

How did you determine that this worked on the Attiny but left the problem at the Teensy ?

I don't doubt that it works now, but the methodology you used seems a little questionable.
 
Status
Not open for further replies.
Back
Top