help understanding I2c and arduino

First off let me say i am a noob.. No doubt about it.

I am attempting to build a robot.. got that part covered. Mechanics are pretty much taken care of. The programming not so much.

I am using the Motor Shield from Ladyada, and I must say that i love it. Easy to use. Easy to understand. The only thing i can say negative about it is that i ran out of pins. It uses a lot of the pins up and i am left with mainly my analog pins and a couple of digital pins. When trying to control a serial lcd, multiple distance and obstacle detectors, I am out of pins.

The thought came to me of setting up i2c and using multiple arduinos to control my bots functions, offloading the motor control to one board with the motor shield attached, then being able to add my lcd as another board that would also control my lighting, and a few other small circuits, and be able to send navigation information from my main arduino to both of the others.

I got the i2c communication up with very little problems using the wire example in the arduino 10 sketchbook. The problem i have starts when i begin modifying the code. I am getting some strange things back from my slave arduino when it prints out my data.

The code that follows is code that uses the IR.h library available in the arduino playground, and wire.h. The code is supposed to receive a signal from a universal remote, and then transmit the proper action to be taken to the slave arduino.

The slave arduino then receives this data, and translates it to motion. ( in this example i have it just printing the results to the serial monitor, later it will actually control the motor shield)

When you run this code, and use a standard ir receiver like the ones available from radio sh*** and a universal son* remote, it will print in the serial monitor the direction i want to travel, but it is also adding some numbers to the end of the data :

ahead32
ahead32
ahead32
rever115
rever115
right32
left 32
left 32

Can someone please tell me what is happening? I totally don't understand, and i have done it in several different codes, and i always get those odd numbers.

Here is the master sender code i am using:

#include <IR.h>
#include <Wire.h>

void setup()
{
  Serial.begin(38400);
  Wire.begin();  // join i2c bus as master address not required
  IR::initialise(0);// IR receiver hardware is on pin2.
}

void loop()
{
  if (IR::queueIsEmpty())
  {
    // Do something more interesting
  }
  else
  {
    IR_COMMAND_TYPE code;
    while (IR::queueRead(code))
    {
            if (code == 16){
        forward();
      } else if (code == 17){
        reverse();
      } else if (code == 18){
       right();
      } else if (code == 19){
       left();
      } else if (code == 20){
       stopall();
      }
      
    }
  }
}


void forward(){         
  Wire.beginTransmission(4); // transmit to device #4
  Wire.send("ahead ");        // sends six bytes
  Wire.endTransmission();    // stop transmitting
  }

void reverse(){         
  Wire.beginTransmission(4); // transmit to device #4
  Wire.send("revers");        // sends six bytes
  Wire.endTransmission();    // stop transmitting
  }

void left(){             
  Wire.beginTransmission(4); // transmit to device #4
  Wire.send("left  ");        // sends six bytes
  Wire.endTransmission();    // stop transmitting
}

void right(){            
  Wire.beginTransmission(4); // transmit to device #4
  Wire.send("right ");        // sends six bytes
  Wire.endTransmission();    // stop transmitting
}

void stopall(){
  Wire.beginTransmission(4); // transmit to device #4
  Wire.send("stop!");        // sends six bytes
  Wire.endTransmission();    // stop transmitting
}

Here is the code i am using on the slave (which is actually the example code from the wire library):

// Wire Slave Receiver
// by Nicholas Zambetti <http://www.zambetti.com>

// Demonstrates use of the Wire library
// Receives data as an I2C/TWI slave device
// Refer to the "Wire Master Writer" example for use with this

// Created 29 March 2006

#include <Wire.h>

void setup()
{
  Wire.begin(4);                // join i2c bus with address #4
  Wire.onReceive(receiveEvent); // register event
  Serial.begin(9600);           // start serial for output
}

void loop()
{
  delay(100);
}

// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany)
{
  while(1 < Wire.available()) // loop through all but the last
  {
    char c = Wire.receive(); // receive byte as a character
    Serial.print(c);         // print the character
  }
  int x = Wire.receive();    // receive byte as an integer
  Serial.println(x);         // print the integer
}

Any help would be greatly appreciated, i have been beating this horse for about three days now.

You're getting a number at the end, because you used the code example that says to take the last byte received and convert it into an int type.

Replace:

void receiveEvent(int howMany)
{
  while(1 < Wire.available()) // loop through all but the last
  {
    char c = Wire.receive(); // receive byte as a character
    Serial.print(c);         // print the character
  }
  int x = Wire.receive();    // receive byte as an integer
  Serial.println(x);         // print the integer
}

With:

void receiveEvent(int howMany)
{
  while(Wire.available()) // loop through all
  {
    char c = Wire.receive(); // receive byte as a character
    Serial.print(c);         // print the character
  }
}

!c

Ahhhhh So i was.

See.. told you i was a noob... i missed that totally.

Ahhhhh So i was.

See.. told you i was a noob... i missed that totally.

Oh, don't sweat it - most of us are noob's, just in varying degrees - you'll see me asking for plenty of help on here grin

!c

Well, i got that all working with no problems now.. i can transmit a message with no problem.

But, I guess i don't really understand what's happening here.. I thought that if i hooked my lcd up to the slave, and sent a message, i could format it just like i was with the example, and get the lcd to print the message, So far, no message has appeared. In one of my tests, i did actually get one solid white box to show up, but nothing else.

Basically i am wanting to send a basic text message to my slave, and have the lcd print it out. I can see everything in the serial monitor, and it will only show me the first character received, then nothing else, the lcd never shows anything.

Here is my master write code:

#include <Wire.h>
#include <IR.h>

void setup()
{
  Wire.begin(); // join i2c bus (address optional for master)
  IR::initialise(0);// IR receiver hardware is on pin2.
}

void loop()
{
if (IR::queueIsEmpty())
  {
    // Do something more interesting
  }
  else
  {
    IR_COMMAND_TYPE code;
    while (IR::queueRead(code))
    {
      Serial.println(code, DEC);
      
      if (code == 16){
        forward();
      } else if (code == 17){
        reverse();
      } else if (code == 18){
       right();
      } else if (code == 19){
       left();
      } else if (code == 20){
       stopall();
      }
    }
  }
}
void forward(){         
  Wire.beginTransmission(4); // transmit to device #4
  Wire.send("Moving Forward");        // sends five bytes
  Wire.endTransmission();    // stop transmitting
  delay(500);
  }

void reverse(){         
  Wire.beginTransmission(4); // transmit to device #4
  Wire.send("Moving Backwards");        // sends five bytes
  Wire.endTransmission();    // stop transmitting
  delay(500);
  }

void left(){             
  Wire.beginTransmission(4); // transmit to device #4
  Wire.send("Turning Left");        // sends five bytes
  Wire.endTransmission();    // stop transmitting
  delay(500);
}

void right(){            
  Wire.beginTransmission(4); // transmit to device #4
  Wire.send("Turning Right");        // sends five bytes
  Wire.endTransmission();    // stop transmitting
  delay(500);
}

void stopall(){
  Wire.beginTransmission(4); // transmit to device #4
  Wire.send("Stopping All");        // sends five bytes
  Wire.endTransmission();    // stop transmitting
  delay(500);
}

and here is my slave receiver code:

#include <LCD4Bit.h> 
#include <Wire.h>
LCD4Bit lcd = LCD4Bit(1); 
void setup()
{
  Wire.begin(4);                // join i2c bus with address #4
  Wire.onReceive(receiveEvent); // register event
  Serial.begin(9600);           // start serial for output
  lcd.init();
}

void loop()
{
  delay(100);
}

// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany)
{
  while(Wire.available()) // loop through all but the last
  {
    char c = Wire.receive(); // receive byte as a character
    Serial.print(c);         // print the character
    lcd.print(c);
  }

}

I have the lcd wired correctly, contrast adjusted, and tested it first to be sure it worked. My i2c is communicating perfectly, and my master sender works right as far as i can tell.

Anyone that can offer advice, it would be appreciated, and remember i am a noob, and not afraid to admit it.

Thanks in advance.

Did you test your LCD printing capabilities before trying to tie it to the i2c communication?

E.g., if you change you setup() function to:

void setup()
{
  Wire.begin(4);                // join i2c bus with address #4
  Wire.onReceive(receiveEvent); // register event
  Serial.begin(9600);           // start serial for output
  lcd.init();
  delay(5);
  lcd.clear();
  lcd.printIn("Testing 123..");
}

Does the text show up properly?

If so, it may be an issue with the delay introduced by the lcd.print() function inside of your interrupt handler. Not terribly versed in the ways of the interrupts, but I've had issues where doing things and introducing delays in them caused all sorts of strange behaviors. (Perhaps someone more knowledgeable on that front can help?)

Try setting a flag to say you have data ready to be printed inside of the handler, and putting the chars into a buffer inside of your handler, then printing them from your loop().

e.g.:

  // note - you'll only get 16 chars! (need null-terminator at byte 17) Don't overflow this buffer.  size it properly =)

char lcd_buffer[17];
bool do_lcd_print = false;

void loop() {

 // put all of your other code here, and then...

 if( do_lcd_print == true )
  {

    lcd.printIn(lcd_buffer);
    do_lcd_print = false;
  }

}


void receiveEvent(int howMany)
{

  byte buf_pos = 0;

  memset(lcd_buffer, 0, sizeof(char) * 17); // clear out our previous buffer

  while(Wire.available()) // loop through all but the last
  {
    char c = Wire.receive(); // receive byte as a character
    Serial.print(c);         // print the character
    lcd_buffer[buf_pos] = c;  // put into buffer
    buf_pos++; //increment buffer position
  }

  do_lcd_print = true;

}

Hope that helps...

!c

Drone you are now my hero.

Ok.. so that worked perfect.. seems you were right. Interrupts I am still attempting to learn. Just started learning them with this project.. just started learning a lot of this crap when i started this project.

So here is the finished code in case anyone else is interested in this thread. I only changed two things in the code.. i removed the lcd test print from the setup and added an lcd.clear to the loop. Works flawlessly, just what i wanted it to do.

Master Sender code:

#include <Wire.h> // include wire library
#include <IR.h> // include IR_decode library

void setup()
{
  Wire.begin(); // join i2c bus (address optional for master)
  IR::initialise(0);// IR receiver hardware is on pin2.
}

void loop()
{
if (IR::queueIsEmpty())
  {
    // Do something more interesting here eventually, code in progress...
  }
  else
  {
    IR_COMMAND_TYPE code;
    while (IR::queueRead(code))
    {
      Serial.println(code, DEC);
      
      if (code == 16){
        forward();
      } else if (code == 17){
        reverse();
      } else if (code == 18){
       right();
      } else if (code == 19){
       left();
      } else if (code == 20){
       stopall();
      }
    }
  }
}
/* i will eventually optimize and change the code below, as i stated, i am a noob, and am still learning.  I plan to have these transmissions going 
to two seperate slaves in the future, one will handle the lcd and some sensor inputs,the other will strictly handle the 4 motors my bot has along 
with the ping servo, and ping sensor.  That same shield will possible have the ir bump sensors on it as well.*/

void forward(){         
  Wire.beginTransmission(4); // transmit to device #4
  Wire.send("Moving Forward");       // send forward direction.
  Wire.endTransmission();    // stop transmitting
  delay(500);
  }

void reverse(){         
  Wire.beginTransmission(4); // transmit to device #4
  Wire.send("Moving Backwards");        // sends reverse direction
  Wire.endTransmission();    // stop transmitting
  delay(500);
  }

void left(){             
  Wire.beginTransmission(4); // transmit to device #4
  Wire.send("Turning Left");        // send left turn
  Wire.endTransmission();    // stop transmitting
  delay(500);
}

void right(){            
  Wire.beginTransmission(4); // transmit to device #4
  Wire.send("Turning Right");        // send right turn
  Wire.endTransmission();    // stop transmitting
  delay(500);
}

void stopall(){
  Wire.beginTransmission(4); // transmit to device #4
  Wire.send("Stopping All");        // send stop all
  Wire.endTransmission();    // stop transmitting
  delay(500);
}

And here is the Slave Receiver code:

#include <LCD4Bit.h>  // include the lcd4bit library
#include <Wire.h>  // include the wire library

LCD4Bit lcd = LCD4Bit(1); // initialize the lcd for one line.
void setup()
{
  Wire.begin(4);                // join i2c bus with address #4
  Wire.onReceive(receiveEvent); // register event
  Serial.begin(9600);           // start serial for output
  lcd.init();                  // initialise the lcd
  
  delay(5);                      // test lcd to make sure it's functioning
  lcd.clear();
  lcd.printIn("Testing 123..");
  delay(1000);
  lcd.clear();

}
char lcd_buffer[17];
bool do_lcd_print = false;

void loop() {

 // put all of your other code here, and then...

 if( do_lcd_print == true )
  {
    lcd.clear();
    lcd.printIn(lcd_buffer);
    do_lcd_print = false;
  }

}


void receiveEvent(int howMany)
{

  byte buf_pos = 0;

  memset(lcd_buffer, 0, sizeof(char) * 17); // clear out our previous buffer

  while(Wire.available()) // loop through all but the last
  {
    char c = Wire.receive(); // receive byte as a character
    Serial.print(c);         // print the character
    lcd_buffer[buf_pos] = c;  // put into buffer
    buf_pos++; //increment buffer position
  }

  do_lcd_print = true;

}

works pretty good with a standard sony remote control, the forward, back, left, and right controls will print out the direction the bot travels on the lcd connected to another arduino with parallel lcd in 4 bit mode.

Thanks for the help drone!

Awesome! Glad it worked for you - always happy to help out when I can :wink:

!c

Ok.. now a new problem.

My code seems to be working ok, at least while i have both arduinos connected to my pc via usb.

The i2c communication works well.. soon as i unplug either one of my usb cables, i lose all communication. Anyone have an idea why that is? Shouldn't the two arduino's be able to communicate without being plugged in to my pc via a usb cable? I am using two power supplys, and verified that they are fully charged, so i am pretty sure it's not a power issue, and I can actually watch the communication breakdown.. via the lcd connected to the slave, while sending commands via remote, you can see the output on the lcd, soon as you pull either cable, no more communication. I don't understand that at all.

Here is the current code i am working with, same codes as above, with the addition of the motor functions in master sender code.

Master code (Robo_Network_node1.pde):

#include <AFMotor.h>
#include <Wire.h> // include wire library
#include <IR.h> // include IR_decode library

AF_DCMotor FL(1, MOTOR12_64KHZ); // create motor #2, 64KHz pwm
AF_DCMotor FR(2, MOTOR12_64KHZ); // create motor #2, 64KHz pwm
AF_DCMotor RL(3, MOTOR12_64KHZ); // create motor #2, 64KHz pwm
AF_DCMotor RR(4, MOTOR12_64KHZ); // create motor #2, 64KHz pwm


void setup()
{

  Wire.begin(); // join i2c bus (address optional for master)
  IR::initialise(0);// IR receiver hardware is on pin2.
  
  FL.setSpeed(255);
  FR.setSpeed(255);
  RL.setSpeed(255);
  RR.setSpeed(255);
  
}

void loop()
{
if (IR::queueIsEmpty())
  {
    // Do something more interesting here eventually, code in progress...
  }
  else
  {
    IR_COMMAND_TYPE code;
    while (IR::queueRead(code))
    {

      
      if (code == 16){
        forward();
      } else if (code == 17){
        reverse();
      } else if (code == 18){
       right();
      } else if (code == 19){
       left();
      } else if (code == 20){
       stopall();
      }
    }
  }
}
/* i will eventually optimize and change the code below, as i stated, i am a noob, and am still learning.  I plan to have these transmissions going 
to two seperate slaves in the future, one will handle the lcd and some sensor inputs,the other will strictly handle the 4 motors my bot has along 
with the ping servo, and ping sensor.  That same shield will possible have the ir bump sensors on it as well.*/

void forward(){         
  Wire.beginTransmission(4); // transmit to device #4
  Wire.send("Moving Forward");       // send forward direction.
  Wire.endTransmission();    // stop transmitting
  delay(500);
  
  FL.run(FORWARD);
  FR.run(FORWARD);
  RL.run(FORWARD);
  RR.run(FORWARD);
  }

void reverse(){         
  Wire.beginTransmission(4); // transmit to device #4
  Wire.send("Moving Backwards");        // sends reverse direction
  Wire.endTransmission();    // stop transmitting
  delay(500);
  
  FL.run(BACKWARD);
  FR.run(BACKWARD);
  RL.run(BACKWARD);
  RR.run(BACKWARD);
  }

void left(){             
  Wire.beginTransmission(4); // transmit to device #4
  Wire.send("Turning Left");        // send left turn
  Wire.endTransmission();    // stop transmitting
  delay(500);
  
  FL.run(BACKWARD);
  FR.run(FORWARD);
  RL.run(BACKWARD);
  RR.run(FORWARD);
}

void right(){            
  Wire.beginTransmission(4); // transmit to device #4
  Wire.send("Turning Right");        // send right turn
  Wire.endTransmission();    // stop transmitting
  delay(500);
  
  FL.run(FORWARD);
  FR.run(BACKWARD);
  RL.run(FORWARD);
  RR.run(BACKWARD);
}

void stopall(){
  Wire.beginTransmission(4); // transmit to device #4
  Wire.send("Stopping All");        // send stop all
  Wire.endTransmission();    // stop transmitting
  delay(500);
  
  FL.run(RELEASE);
  FR.run(RELEASE);
  RL.run(RELEASE);
  RR.run(RELEASE);
}

Here is the slave code (Robo_network_node2.pde):

#include <LCD4Bit.h>  // include the lcd4bit library
#include <Wire.h>  // include the wire library

LCD4Bit lcd = LCD4Bit(1); // initialize the lcd for one line.
void setup()
{
  Wire.begin(4);                // join i2c bus with address #4
  Wire.onReceive(receiveEvent); // register event
  
  lcd.init();                  // initialise the lcd
  
  delay(5);                      // test lcd to make sure it's functioning
  lcd.clear();
  lcd.printIn("Testing 123..");
  delay(1000);
  lcd.clear();

}
char lcd_buffer[17];
bool do_lcd_print = false;

void loop() {

 // put all of your other code here, and then...

 if( do_lcd_print == true )
  {
    lcd.clear();
    lcd.printIn(lcd_buffer);
    delay(2000);
    lcd.clear();
    do_lcd_print = false;
  }

}


void receiveEvent(int howMany)
{

  byte buf_pos = 0;

  memset(lcd_buffer, 0, sizeof(char) * 17); // clear out our previous buffer

  while(Wire.available()) // loop through all but the last
  {
    char c = Wire.receive(); // receive byte as a character
   
    lcd_buffer[buf_pos] = c;  // put into buffer
    buf_pos++; //increment buffer position
    
  }

  do_lcd_print = true;

}

Does the arduino know it's supposed to be externally powered? With the NG/Decimilla there is a jumper that must be moved to switch from USB to external power.

-j

Yes, I did have both jumpers on the arduinos on external power.. that was not the problem.

I did finally figure out that having a common ground is important. One i placed a jumper wire connecting both grounds, i had no more problems.

That's important to remember.

Hi,

I've set up a I2C with 2 arduino. Seems to be OK, messages are received but they are read with strange characters. Instead of reeiving 'hello ' i receive 6 characters looking like a quare. my grounds are connected each other diretly.

Any idea where the pb comes from ?

once this will be solve, if I have 20 arduino sending messages all together, will there be any trouble to get them ALL on the master receiver ?

Thanks

Seb

Hi seb,

I ran into that a few times with my research/experimenting, check the baud rate on both of your sketchs, make sure that you are looking at the serial monitor with the same baud rate as the arduino is broadcasting it's transmission on.

I can't remember anything else that caused that.

try checking your baudrates for serial communication.. i have a feeling that may be causing a problem.

Hi grinan,

I checked that, it is fine. Pb is somewhere else :frowning:

Here is a pic of my wiring :

http://s140694374.onlinehome.fr/P1000171.PDF

. Anything wrong ?

Thanks

Seb

No ground connection. You would probably get one through USB if they happen to be plugged in to the same computer, but you always need a common ground.

-j

well...there is a common ground...!! a cable exit between ground port of the 2 arduino...

Is that wrong ?

Ok, I would do this..

Power your breadboard from the power and ground of one arduino, Power the actual + and - rails, Attach two pull up resistor to two nodes on the breadboard, and run your i2c connections from both arduino's to these two nodes, analog pin 4 to 4 and 5 to 5, just connecting them to the two pullups.

On the second arduino, power it from a battery pack, wall wart or some other form of connection other than usb, and then attach a ground from that arduino to the - rail that you connected earlier.

That should be a perfect connection.

You can also look at the little article I wrote on the uchobby website, here: https://web.archive.org/web/20190609170522/http://www.uchobby.com/index.php/2008/09/16/introduction-to-i2c/ look at those schematics, and even the pics, and you can see the setup i am using.

I hope this helps.

Thanks ! Really appreciated. This works now!

I have now to see what happends if 10 arduino are ending message all together. Did you had any experience for that ? no data collision ?

Seb

never tried more than three.. i had no problems with data collision, and then too, the data i was sending was small and basic.. i did it simply to see if i could do it.

Would love to know how it turns out for you and see some code examples, very interested in i2c myself.. i see a lot of potential.

Glad I was able to help.

Ooops, sorry, the angle fooled me and I didn't see the ground wire the first time I looked at the photo.

After a quick glance at the code, it seems you are polling the slaves from the master, in which case you should never have a collision. You could get the theoretical max of 127 devices communicating this way.

Also, the ATmega has internal pullup resistors, so technically you don't need them. I've successfully used I2C with 2 Arduinos, and with one Arduino an 4 slave devices, without external pullups. You may need them if you have lots of devices and/or relatively long wire runs.

-j