I2c between Arduinos

Hi, i am trying to implement I2C communication between two Arduinos.
The master would send the following strings

A1,10,20,30
B1,25,60,-10.4
C1,50.5,2,55

and so on…

Can some one help me with slave code to break up the incoming data, like A1 should be converted to string and next three should be converted to number format?

All the four parameters will be input to a function to do the comparison and math.

Thank you.

This involves three discrete steps. First, the entire packet has to be read into a character array that is properly NULL terminated. Accomplishing this will not be a trivial task, because the packets are not well delimited. Something like <A1,10,20,30> would be better, since there are clear start and end of packet markers.

The next step is to parse the string. Since the first letter is a single token, it can be extracted easily, and replaced with a space. Then, strtok can be used in a loop, until it returns a NULL, to extract each token (“1”, “10”, “20”, “30”).

Finally, each token needs to be converted to a float. This is the easiest step, since atof() is already available and does exactly what is needed. Converting from float to integer, where required, is trivial.

As PaulS says there's no obvious delimiter between the data packets.

Are you stuck with that format? Or can you add delimiters?

Are A, B and C the only options for the first character?

If not is the first character always an alpha and the rest always numeric?

How critical is the app, are you sending settings to a heart-lung machine, or controlling your garden sprinklers?


Rob

@ PaulS

Thanks for the reply. I will add the delimiters and try to make changes to my Serial data to Integer code. It does the same thing except it returns an integer.

@ Graynomad

Yes, I can add the delimiters.

All the packets follow the data pattern <Alphanumeric,Number,Number,Number>

The application is for the leg mechanism of a hexapod.
The Alphanumeric ‘A1’ refers to a particular leg and the Number,Number,Number refers to (x,y,z) co-ordinates of where the foot of the A1 leg needs to be.

I am just starting of with testing and trying out Inverse Kinematics first :slight_smile:

Yes, I can add the delimiters.

Then I'd add them and parse the data like PaulS says.


Rob

Well, was trying it out using ','as a delimiter.

The Master Arduino receives a number from serial data, the same number is transferred to the slave Arduino using I@C. The slave has to blink the led based on the number it receives.

I'm in kind of fix :(. I believe i am able to send the serial number over I2c but am not able to get the LED to blink I am posting both the I2C mater and I2C slave codes below. Please help.

Thank you.

I2C Master:

const char seperator= ‘,’;
int count;
#include <Wire.h>

void setup()
{
Serial.begin(9600);
pinMode(ledPin, OUTPUT);
Wire.begin();
}

void loop() {

if (Serial.available()) {

Wire.beginTransmission(4);
count =SerialReadInt();
Wire.send(count);
Wire.send(’,’);
Wire.endTransmission();
}
}

int SerialReadInt() {
char str[32];
str[0] = ‘\0’;
int c=0;
while(true) {
if (Serial.available() > 0) {
str

 = Serial.read();
      if (str[c] == seperator || str[c] == '\0') {
        str[c] = '\0';
        break;
      }
      else
        c++;
    }
  }
  return(atoi(str));
}

I2C Slave:

#include <Wire.h>

int ledPin = 13;
const char seperator= ‘,’;
int x;

void setup()
{
pinMode(ledPin, OUTPUT);
Wire.begin(4);
Wire.onReceive(receiveEvent);
}

void loop()
{

}

void receiveEvent(int howMany)
{
x = WireReadInt();
for(int i=1;i<=x;i++)
{
delay(500);
digitalWrite(ledPin, HIGH);
delay(500);
digitalWrite(ledPin, LOW);
}
}

int WireReadInt() {
char str[32];
str[0] = ‘\0’;
int c=0;
while(true) {
if (Wire.available() > 0) {
str

 = Wire.receive();
      if (str[c] == seperator || str[c] == '\0') {
        str[c] = '\0';
        break;
      }
      else
        c++;
    }
  }
  return(atoi(str));
}
if (Serial.available() > 0) {
     str[c] = Serial.read();
     if (str[c] == seperator || str[c] == '\0') {

str

 will never be NULL, so this check is unnecessary.

[code]     Wire.send(count);[/code]
From [url]http://arduino.cc/en/Reference/WireSend[/url]
[quote]value: a byte to send (byte) [/quote]
Does the value you are sending fit in a byte (0 to 255)? How do you handle cases where it does not?

The value is sent as a byte, but you are reading it as though it was a string. It's no wonder you are having problems.

Hi Paul,

Thanks for the reply. After reply I made some changes to the code and it works partially. I am able to send whole strings “<A,10,20,30>” from Master to Slave. :slight_smile: Working on the receiving part.

I am reconstructing the string from single bytes received from I2C.

Here is the code for the slave:

#include <Wire.h>
char str[32];
int position=0;

void setup()
{
Wire.begin(4);
Wire.onReceive(receiveEvent);
Serial.begin(9600);
}

void loop()
{
delay(100);
}

void receiveEvent(int howMany)
{
str[0] = ‘\0’;

while(0 < Wire.available())
{
str[position] = Wire.receive();

if(str[position] == ‘>’)
{
str[position]=’\0’;
position=0;
break;
}
else
{
position++;
}
Serial.println(str);
}
}

The Issue is when the Slave receives ‘>’ it stops and the serial-output is as follows:

<
<A
<A,
<A,1
<A,10
<A,10,
<A,10,2
<A,10,20
<A,10,20,
<A,10,20,3
<A,10,20,30

And it stops there :frowning: . Please help.
Thank you.

This is the code for the Master and seems to work fine :).

#include <Wire.h>

void setup()
{
Wire.begin();
Serial.begin(9600);
}

void loop()
{
Wire.beginTransmission(4); // transmit to device #4
Wire.send("<A,10,20,30>"); // sends five bytes
Wire.endTransmission(); // stop transmitting
Serial.println("<A,10,20,30>");
delay(500);
}

please use the # button to tag code

The > sign is replaced by the \0 so it wont appear on your output.

if(str[position] == '>')
   {
     str[position]='\0';

Furthermore the break statement will jump you out of the while loop while there may still be character to read.

// the comment is incorrect, you sent 12 bytes!
Wire.send("<A,10,20,30>");        // sends five bytes

Finally the array is 32 bytes in size, there is nothing that prevents addressing beyond byte 32 in receiveEvent. It might overwrite something in memory not intended …

Question does it generate this output only once ? or does this repeat?
Can you add a Serial.println(howmany) at the begin of the receiveEvent?

The Issue is when the Slave receives ‘>’ it stops and the serial-output is as follows

You have this code which reads the data.

while(0 < Wire.available())
 {
   str[position] = Wire.receive();

   if(str[position] == '>')
   {
     str[position]='\0';
     position=0;
     break;
   }
   else
   {
   position++;
   }
   Serial.println(str);  
 }

It breaks out of this loop when the > is read, as you told it to.

And it stops there :(. Please help.

You haven’t provided anything for the receiver to do when it gets the complete string. So, of course, the receive event handler ends.

You need to write some more code, to go after the while loop. Some error handling in the while loop, as robtilaart points out, would not be amiss, either.

Hi Paul and Robtillaart,

Thanks for your replies. I have made some changes to the code. Its quite a mess but it works. I am able to separate “A,10,20,30” from Master to Char A,integer 10,integer 20,integer 30 in slave.

Here is the code:

#include <Wire.h>
int position=0;
String X;
String Y;
String Z;
String receivedMessage;
int startStringPosition;
int endStringPosition;
char str[32];
char item;
char LEG;
int x;
int y;
int z;

void setup()
{
  Wire.begin(4);                
  Wire.onReceive(receiveEvent); 
  Serial.begin(9600);          
}

void loop()
{
  delay(100);
} 

void receiveEvent(int howMany)
{
  str[0] = '\0';
  while(0 < Wire.available())
  {
    str[position] = Wire.receive();
    if(str[position] == '>')
    {
      str[position+1]='\0';
      position=0;
      break;
    }
    else
    {
    position++;
    }    
  }

  receivedMessage = String(str);
  getLEG();
  getX();
  getY();
  getZ();

}

void getLEG()
{
  LEG=receivedMessage.charAt(1);
  Serial.println(LEG);
}

void getX()
{
 startStringPosition= receivedMessage.indexOf(',')+1; 
 endStringPosition= receivedMessage.indexOf(',',startStringPosition+1);
 X=receivedMessage.substring(startStringPosition,endStringPosition);
 Serial.println(X);
}

void getY()
{
  startStringPosition=endStringPosition+1;
  endStringPosition= receivedMessage.indexOf(',',startStringPosition+1);
  Y =receivedMessage.substring(startStringPosition,endStringPosition);
  char Ychar[Y.length() +1];
  Y.toCharArray(Ychar,sizeof(Ychar));
  y=atoi(Ychar);
  Serial.println(y);
}

void getZ()
{
  startStringPosition=endStringPosition+1;
  endStringPosition= receivedMessage.indexOf('>',startStringPosition+1);
  Z =receivedMessage.substring(startStringPosition,endStringPosition);
  char Zchar[Z.length() +1];
  Z.toCharArray(Zchar,sizeof(Zchar));
  z=atoi(Zchar);
  Serial.println(z);
}

The Slave runs without breaking for the above posted Mater code.

Now Sending two strings is not working:
<A,10,20,30>
<B,20,30,40>

Any suggestions with sending multiple strings and cleaning up the code a bit?

Thank you.

void loop()
{
  delay(100);
}

Why is there a delay here? All it does is cause the event handler to be sluggish in responding.

Any suggestions with sending multiple strings and cleaning up the code a bit?

The variable names are horrendously long, and there is no error checking, but, otherwise it is fine.

Where does position get reset to 0? It doesn't. So, where do the characters get written? That's right. Not starting at 0. So, the NULL in position 0 never gets removed, so the string length never changes. Should be an easy fix.

Hi Paul,

Where does position get reset to 0? It doesn't.

I dont understand.The position is getting reset to 0 when ‘>’ character occurs, and the position+1 character in the string is made null.

while(0 < Wire.available())
  {
    str[position] = Wire.receive();
    if(str[position] == '>')
    {
      str[position+1]='\0';
      position=0;
      break;
    }

The position is getting reset to 0 when '>' character occurs, and the position+1 character in the string is made null.

I missed that. I would have put that statement at the start of loop, right before or after:

  str[0] = '\0';

since they are logically related.

What does happen when you enter the second string? "Now Sending two strings is not working" doesn't really tell us much.