Arduino Forum

Using Arduino => Networking, Protocols, and Devices => Topic started by: DrShenanigans on Jun 10, 2012, 01:38 am

Title: I2C Problem, Communicating Between Two Arduinos
Post by: DrShenanigans on Jun 10, 2012, 01:38 am
Well, I have been working on a project that has me using several Arduinos as slaves, each acting as a servo. I wanted them to be able to communicate via I2C and receive and send information to a master Arduino. However, I am having a bit of an issue in sending information between the slave to the master. The master Arduino only seems to be picking up the last byte sent, and thinks all the other bytes sent are 255 or 0xff.

Here is a very bare bones (relatively speaking in comparison to what I had initially) version of the code that demonstrates what is going wrong.

Master Code:
Code: [Select]

#include <Wire.h>

unsigned char data_byte_0;
unsigned char data_byte_1;
unsigned char data_byte_2;
unsigned char data_byte_3;

long number;
long new_number;

void setup(){
  Wire.begin();
  digitalWrite(12,HIGH);
  Serial.begin(9600);
}

byte n;

void loop(){
  if(digitalRead(12)==0){
    if(n == 0){       
      byte state = 0;
      Wire.requestFrom(0x40,4);
      while(Wire.available()){
        switch(state){
        case 0:
          data_byte_0 = Wire.read();
          state = 1;
          Serial.print(data_byte_0,DEC);
          Serial.print("     ");
          break;
        case 1:
          data_byte_1 = Wire.read();
          state = 2;
          Serial.print(data_byte_1,DEC);
          Serial.print("     ");
          break;
        case 2:
          data_byte_2 = Wire.read();
          state = 3;
          Serial.print(data_byte_2,DEC);
          Serial.print("     ");
          break;
        case 3:
          data_byte_3 = Wire.read();
          state = 0;
          Serial.print(data_byte_3,DEC);
          Serial.print("     ");
          break;
        }
      }
      Serial.println("");
      n = 1;
    }
    else{
      n = 1;
    }
  }
  else{
    n = 0;
  }



Slave Code:
Code: [Select]

#include <Wire.h>

byte data_byte_0;
byte data_byte_1;
byte data_byte_2;
byte data_byte_3;

long number = 0;

void setup(){
  Wire.begin(0x40);
  Wire.onRequest(dataWrite); 
  Serial.begin(9600);
  number = 0x04030201;
}

void dataWrite(){
  data_byte_0 = number;
  data_byte_1 = number>>8;
  data_byte_2 = number>>16;
  data_byte_3 = number>>24;
  Wire.write(data_byte_0);
  Serial.print(data_byte_0,DEC);
  Serial.print("     ");
  Wire.write(data_byte_1);
  Serial.print(data_byte_1,DEC);
  Serial.print("     ");
  Wire.write(data_byte_2);
  Serial.print(data_byte_2,DEC);
  Serial.print("     ");
  Wire.write(data_byte_3);
  Serial.print(data_byte_3,DEC);
  Serial.print("     ");
  Serial.println("");
}

void loop(){
}



Basically I have a button on Pin12 on the master, and when the button is pressed, the master requests a 4-bit number from the slave, which is sent one bite at a time. The options there are for debugging purposes, and can be moved around or deleted while still retaining the issue. When a button is pressed the serial monitor of the slave will read "1     2     3     4"--so I know the interrupt routine is being called, however the master's monitor reads "4     255     255     255". The issue persists even if I reduce the number of bytes sent to 3 or 2, with the master board only picking up the last byte sent.

There is no issue in the master sending code and the slave reading it. This happens only when I am using the slave to send information.
Title: Re: I2C Problem, Communicating Between Two Arduinos
Post by: micnossub on Jun 10, 2012, 02:12 am
a couple of month ago i have made i2c com between 2 arduino, and i had a problem with it. the data were not read correctly, and i put a delay of (10ms) after the data detection on the slave arduino and everything was working fine after.
Title: Re: I2C Problem, Communicating Between Two Arduinos
Post by: DrShenanigans on Jun 10, 2012, 03:16 am
Tried adding a delay (first to the Slave, then to the master), but I didn't have much luck. Also, adding delays to my code isn't much of an option, as I will be using the communication inside a code with a 500 Hz timer interrupt. If possible I would prefer to run the I2C line at the 800kHz max that is allowable in the ATMega, or as high as permissible with the setup I have. The I2C is being used as a feedback line between a single micro and 4 others, so I would prefer to have as minimum propagation delay in the line as possible.

I ran my code and hooked a logic analyzer to the two lines; the result is attached. There seems to be a large delay between the Master's send request and the slave's response, and the slave seems to be just not sending out the correct data which seems to be cause of the error.

The master also seems to be sending the read request to the wrong address. It is sending it to 129, when it should be sending it to 64. This is shown in the second attachment.

(Edit to combine replies)
Title: Re: I2C Problem, Communicating Between Two Arduinos
Post by: johnnyonthespot on Jun 10, 2012, 06:42 am
Hmm..Looking at your Logic Analyzer images...from the bit pattern, what should be seen is 7bit slave address 0x40 + a Read bit '1' which becomes the byte 0x81. I'm afraid your logic analyzer is not interpretting the bit stream properly. I'm not sure where it gets 129 which is 0x79...a read request to 7bit address 0x3C.

I would like to see logic analyzer screenshots of the communication when taking all calls to serial.print() out of the slave code. Also try not to build your data within the onRequest routine. In I2C, before a read request is made the master usually writes a 'memory address' to the slave to prepare it for a read request. I'm not sure how much overhead is created by manipulating long integers when the slave should be immediatly pushing data out.

I haven't had too much trouble with I2C on the arduino...I'm sure you'll get it working
Title: Re: I2C Problem, Communicating Between Two Arduinos
Post by: johnnyonthespot on Jun 10, 2012, 06:52 am
Also for sending multiple bytes...how about using the function as so:

size_t TwoWire::write(const uint8_t *data, size_t quantity)

Just send the data array and byte length. This way no extra overhead is created by calling the wire.write() function as many times as there are bytes.

Good luck
Title: Re: I2C Problem, Communicating Between Two Arduinos
Post by: johnnyonthespot on Jun 10, 2012, 07:00 am
By the way, I just realized why your logic analyzer saw 129...The single quotes around 129 ie '129' means you have "display as ascii" selected in the i2c settings. There's nothing wrong with your logic analyzer, its seeing the 7bit addres + read_bit for 0x81 (meaning your master is sending the right read address to slave 0x40).
Title: Re: I2C Problem, Communicating Between Two Arduinos
Post by: micnossub on Jun 10, 2012, 05:31 pm
i'm surprised because i used a delay in this video to show a little i2c communication between two arduino, and the answer at the computer screen is instant.

http://www.youtube.com/watch?v=h_E7h2iQA9I

the connection is like this:

button -> arduino 1 -> i2c bus -> arduino 2 -> serial communication -> computer
Title: Re: I2C Problem, Communicating Between Two Arduinos
Post by: DrShenanigans on Jun 12, 2012, 05:01 am
I finally got things to work, and things seem to be going smoothly with a SDCL of 400kHz. Thanks for the help and suggestion johnnyonthespot. It seems that when the slave is writing, the arduino libraries hate using multiple writes.

Here is my new slave code that is working:
Code: [Select]

#include <Wire.h>

long number;

unsigned char data[4];

void setup(){
  Wire.begin(0x40);
  Wire.onRequest(dataWrite); 
  Serial.begin(9600);
  number = 0x04030201;
}

void dataWrite(){
  data[0] = number;
  data[1] = number>>8;
  data[2] = number>>16;
  data[3] = number>>24;
  Wire.write(data,4);
  Serial.print(data[0],DEC);
  Serial.print("     ");
  Serial.print(data[1],DEC);
  Serial.print("     ");
  Serial.print(data[2],DEC);
  Serial.print("     ");
  Serial.print(data[3],DEC);
  Serial.print("     ");
  Serial.println("");
}

void loop(){
}



The only main difference between this and the previous code is that the write command is only called once and writes a byte array instead of one byte. I wonder if anyone else can corroborate this (multiple write calls from a slave), or if it is just something unique to me.

On a different note the 800µs delay between the I2C address call and the data write bytes from the slave doesn't seem to be affected much by the numerical operation I have in the code. However, if I send a read command to the slave and then call a write command, the delay reduces drastically to around 60µs. This is both after a stop command is sent from the master, and then the address is resent again. I don't have my logic analyzer set up currently otherwise I would show comparison pictures, but it is another bit of strange behavior that kinda has me wondering what is going on in the Wire library.

Also micnossub, it may seem like an instant to you, but that is because human visual feedback operates at around 10-100Hz. So, a 10 ms delay or so will seem instantaneous. However, millisecond delays have no place being anywhere near timer interrupts, especially those like mine which is at 500Hz (2 ms period). I appreciate the attempt though.
Title: Re: I2C Problem, Communicating Between Two Arduinos
Post by: joshblake on Jun 12, 2012, 09:40 pm
I wonder if it's because you're requesting data from 0x40 (as an 8bit address) when you should really be requesting from 0x40>>1 ...

Just an idea?!
Title: Re: I2C Problem, Communicating Between Two Arduinos
Post by: DrShenanigans on Jun 13, 2012, 12:54 am
0x40 is 64, which is less than 128, so it is still a 7-bit number.  So far as I know, the bit-shifting is only used when Arduino needs to connect to a device that uses a 8-bit address.
Title: Re: I2C Problem, Communicating Between Two Arduinos
Post by: el_supremo on Jun 13, 2012, 01:25 am
AFAIK, When you specify an address to the I2C library it has to be the 7-bit address. Internally, the library shifts this up one bit which sets the low order bit to zero (which is good for a write). If you have asked for a read, the library sets the low order bit to one.

So, specifying 0x40 to the library means that internally this will be shifted to become 0x80 and when writing it will use 0x80. For reading it will use 0x81.
As long as you specify the address in the same way when writing and reading, it should work.

Pete
Title: Re: I2C Problem, Communicating Between Two Arduinos
Post by: DrShenanigans on Jun 13, 2012, 03:05 am
All I was saying is that the 0x40 is the 7-bit address used for the I2C register on the slave (The seven MSbs of the TWAR register). I wasn't trying to argue what it was which the I2C sends out (7bit address, 1 bit R/W, 1 bit Ack). Some slaves use a 8-bit address, and for that, there are tricks that are used. Sorry if there was any confusion. Anyways all that is besides the problem of what seems to be going on here, which seems to be caused by using multiple writes functions from a slave. What I am curious about is that if this is a general property of the Wire library, or if it is something unique to me. If it is only me, then I have more debugging to do, if not, I shrug and move on.

The other issue I am curious about is why is there such a large delay (0.8 ms) between the address+W byte and the data bytes, when the master sends only a write request, but barely any delay (60µs) when the master first sends address+R & data & stop, then adress+W & data & stop (between address+R/W & data).
Title: Re: I2C Problem, Communicating Between Two Arduinos
Post by: el_supremo on Jun 13, 2012, 03:27 am
Quote
Sorry if there was any confusion.

Nah, probably all at my end anyway :-)

I've only used slave devices with the Arduino as master. I haven't tried writing code for an Arduino to act as a slave. Maybe I'll try that out sometime but it won't be soon because my PC has to go back for repairs.

Pete
Title: Re: I2C Problem, Communicating Between Two Arduinos
Post by: matoushybl on Jul 13, 2012, 05:29 pm
Hello,
I've the same problem with two arduinos communicating over i2c. I tried to use your solutions but I haven't got any progess with them. I am  getting only the last byte and then only 255 too. Can you please help me with it?

Master code:
Code: [Select]
#include <Wire.h>
int wire_buffer[5]={-1,-1,-1,-1,-1};
void setup(){
  Wire.begin();
  Serial.begin(115200);
}
void loop(){
  uno_r(2);
  for(int i;i<5;i++){
  Serial.print(wire_buffer[i],DEC);
  Serial.print("    ");
 
  }
  Serial.println();
  delay(100);
}

void uno_r(int howMany){
Wire.requestFrom(2,howMany);
   for (int i = 0; i < kolik; i++) {
    if (Wire.available() > 0) {
      wire_buffer[i]=Wire.read();
    }
}
}

Slave code:
Code: [Select]
#include <Wire.h>
#include <Max3421e.h>
#include <Usb.h>
#include <AndroidAccessory.h>
byte msg[2];
AndroidAccessory acc("Google, Inc.",
     "DemoKit",
     "DemoKit Arduino Board",
     "1.0",
     "http://www.android.com",
     "0000000012345678");
void setup()
{
  Wire.begin(2);                // join i2c bus with address #2
  Wire.onRequest(requestEvent); // register event
  acc.powerOn();
  Serial.begin(115200);
}

void loop()
{
  if (acc.isConnected()) {
                Serial.print("Accessory connected. ");
int len = acc.read(msg, sizeof(msg), 1);
                Serial.print("Message value: ");
                Serial.print(msg[0],DEC);
                Serial.print("  ");
                Serial.println(msg[1],DEC);                       
        }
delay(20);
}
void requestEvent()
{
  delay(10);
  char buff[2]={(byte)msg[0],(byte)msg[1]};
  Wire.write(buff);
}

Thank you
Title: Re: I2C Problem, Communicating Between Two Arduinos
Post by: PaulS on Jul 13, 2012, 08:06 pm
Code: [Select]
   for (int i = 0; i < kolik; i++) {
kolik? Where is that defined?

Code: [Select]
AndroidAccessory acc("Google, Inc.",
     "DemoKit",
     "DemoKit Arduino Board",
     "1.0",
     "http://www.android.com",
     "0000000012345678");

What does this have to do with the two Arduinos communicating via I2C?

Code: [Select]
void requestEvent()
{
  delay(10);
  char buff[2]={(byte)msg[0],(byte)msg[1]};
  Wire.write(buff);
}

When the master requests data, send an array of NULLs. How is that supposed to accomplish anything? How many bytes do you think are actually going to be sent?
Title: Re: I2C Problem, Communicating Between Two Arduinos
Post by: matoushybl on Jul 13, 2012, 08:30 pm
I'm sorry about the variable kolik- it is the variable howMany. This mistake was caused by translating code from Czech to English.
The Arduino accessory is used to set value of the msg array. I think that two bytes are going to be send and if there are no data from accessory it's going to send -1s there.

Thanks for your reply
Title: Re: I2C Problem, Communicating Between Two Arduinos
Post by: Constantin on Jul 13, 2012, 09:08 pm
May I suggest using Bill Porters Easy Transfer I2C edition for this sort of problem? May make your life a lot easier.

He also has a serial edition. I used the serial version at 1MBit/s between two Atmels on one board.
Title: Re: I2C Problem, Communicating Between Two Arduinos
Post by: matoushybl on Jul 13, 2012, 09:14 pm
Thanks for your reply. I'll definitely try it. But I'd rather use I2C because in my project there'll be more than one slave.
Title: Re: I2C Problem, Communicating Between Two Arduinos
Post by: Constantin on Jul 13, 2012, 09:25 pm

Thanks for your reply. I'll definitely try it. But I'd rather use I2C because in my project there'll be more than one slave.


Should work great. His library includes examples, so hopefully it won't take much to get it all implemented quickly. I2C is a great bus, but be aware of the limitations re: bus length - Phillips originally designed it to be used only on one PCB. That's not to say that it won't work, but buses longer what than the OEM planned for may not be as reliable as you would like.

It's one reason I went to RS485-based buses between my Arduinos. Very good resistance against interference and the possibility of running shielded cables if necessary. But my distances are likely larger than yours...
Title: Re: I2C Problem, Communicating Between Two Arduinos
Post by: DrShenanigans on Jul 13, 2012, 10:42 pm
I've had little to no issue with the I2C bus since I got my stuff working, and I'm running it from an mBed to 4 seperate atmega chips, a gyro, and an accelerometer all on the same line. So long as you are routing signals over short distances (within a couple of feet or so) you should be fine. My rule of thumb is to always have a 10k pullup resistor on the SDA and SCL lines for each device you have, so the resistance will decrease as the capacitance added increases. I would also recommend you stick to the Wire library rather than going to an external one as there is more support here on the forum, and outside little issues like this, it is quite robust.

Anyways, I don't know if you are still having trouble with your code but here is my 2 cents on it. Your main problem is that you did not include your array length in your Wire.write command. The syntax is void write(*char data, int length). So you are only sending 1 byte, thus the reason why only the last byte is sent as that is what the index pulls up first. Double check the reference page: http://arduino.cc/en/Reference/WireWrite

Also why do you have a 10ms delay in the receiver? Essentially what is going to happen to your I2C line will be a really long pull-down of the SCL then a very rapid stream of data, I wouldn't be surprised if things got lost in that. The i2c request and receive functions are both run through interrupts, and generally you do not want to stick delays (especially of the ms variety, you could get away with micro delays) into interrupts as you want them to run as fast as possible.

There are other quirks and shenanigans in your code that you probably will have to debug out, but I think the issue related to the I2C bus is that you are sending a data array without specifying the data array length as the library wants.

Title: Re: I2C Problem, Communicating Between Two Arduinos
Post by: matoushybl on Jul 13, 2012, 11:10 pm
Thank you for your response, it's finally working.
Title: Re: I2C Problem, Communicating Between Two Arduinos
Post by: PaulS on Jul 14, 2012, 02:54 am
Quote
Thank you for your response, it's finally working.

Great. So, what was the problem? How did you correct it?
Title: Re: I2C Problem, Communicating Between Two Arduinos
Post by: matoushybl on Jul 14, 2012, 01:34 pm
The problem was in using Wire.write() without buffer length in the slave code. Correct command in this case is Wire.write(msg,2);
Title: Re: I2C Problem, Communicating Between Two Arduinos
Post by: lacanau on Jan 11, 2016, 06:06 pm
Just wanted to confirm that this solved my problem.

I was dealing with a .onRequest event on my slave arduino and my response was 2x Wire.write(), and for some reason I was only receiving the first byte.

Adding .beginTransmission and .endTransmission around the .writes cause my arduino to hang.

Building and sending an array sans Transmission functions fixed my problem.

Many thanks :)
Title: Re: I2C Problem, Communicating Between Two Arduinos
Post by: arutun on Aug 29, 2016, 12:03 am
For those who need to call write( ) multiple times in a slave response, I was able to modify twi_transmit( ) in twi.c to the following code and this works fine:

Code: [Select]

uint8_t twi_transmit(const uint8_t* data, uint8_t length)
{
 uint8_t i;
   // ensure data will fit into buffer
   if(TWI_BUFFER_LENGTH < twi_txBufferLength+length){
       return 1;
   }
   
   // ensure we are currently a slave transmitter
   if(TWI_STX != twi_state){
       return 2;
   }
   
   // set length and copy data into tx buffer
   for(i = 0; i < length; ++i){
       twi_txBuffer[i+twi_txBufferLength] = data[i];
   }
   twi_txBufferLength += length;
   
 return 0;
}