Pages: [1] 2   Go Down
Author Topic: I2C Problem, Communicating Between Two Arduinos  (Read 4744 times)
0 Members and 1 Guest are viewing this topic.
West Lafayette
Offline Offline
Newbie
*
Karma: 0
Posts: 6
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
#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:
#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.
Logged

Quebec, Canada
Offline Offline
Newbie
*
Karma: 0
Posts: 39
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

West Lafayette
Offline Offline
Newbie
*
Karma: 0
Posts: 6
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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)


* Error.JPG (28.4 KB, 1069x122 - viewed 53 times.)

* Error2.JPG (35.84 KB, 1065x121 - viewed 49 times.)
« Last Edit: June 09, 2012, 08:42:38 pm by DrShenanigans » Logged

Detroit, MI
Offline Offline
Full Member
***
Karma: 0
Posts: 117
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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
« Last Edit: June 09, 2012, 11:45:12 pm by johnnyonthespot » Logged

Detroit, MI
Offline Offline
Full Member
***
Karma: 0
Posts: 117
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

Detroit, MI
Offline Offline
Full Member
***
Karma: 0
Posts: 117
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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).
Logged

Quebec, Canada
Offline Offline
Newbie
*
Karma: 0
Posts: 39
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.



the connection is like this:

button -> arduino 1 -> i2c bus -> arduino 2 -> serial communication -> computer
Logged

West Lafayette
Offline Offline
Newbie
*
Karma: 0
Posts: 6
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
#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.
Logged

Offline Offline
Newbie
*
Karma: 1
Posts: 6
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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?!
Logged

West Lafayette
Offline Offline
Newbie
*
Karma: 0
Posts: 6
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Offline Offline
Edison Member
*
Karma: 31
Posts: 1417
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

Where are the Nick Gammons of yesteryear?

West Lafayette
Offline Offline
Newbie
*
Karma: 0
Posts: 6
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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).
Logged

Offline Offline
Edison Member
*
Karma: 31
Posts: 1417
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

Where are the Nick Gammons of yesteryear?

Offline Offline
Newbie
*
Karma: 0
Posts: 5
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
#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:
#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
Logged

Seattle, WA USA
Online Online
Brattain Member
*****
Karma: 547
Posts: 45962
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
   for (int i = 0; i < kolik; i++) {
kolik? Where is that defined?

Code:
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:
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?
Logged

Pages: [1] 2   Go Up
Jump to: