I2C communication from Lego Mindstorm EV3 Robot using XBee

Hello,

I have attached a color sensor on my Lego Mindstorm EV3 Robot from which I'm sending values to Arduino UNO continuously.

After having that I attached an XBee-PRO 900 HP DigiMesh mounted on the Arduino so that it can transmit the color sensor values to second Lego with the help of the Previous Arduino and XBee setup.

Here's the code of what I've written:

#include <XBee.h>
#include <time.h>
#include <stdlib.h>
#include <Wire.h>
#include <Servo.h>

XBee xbee = XBee();
XBeeAddress64 ROUTER1 = XBeeAddress64(0x0013A200,0x4147FE2A);

uint8_t data[4];
int instruction[4] = {1,0,0,0};
byte read_byte = 0x00;
int byte_count = 0;
byte* dataPointer;

void setup()
{
  Serial.begin(9600);           // We will spit it back out on the serial line.
  Wire.begin(0x0A);             // Start I2C on Address 0x0A
  
  xbee.setSerial(Serial);
  Serial.println("StartingUp!");
}

void loop()
{
  delay(500);
  Wire.onReceive(receiveI2C);   // Receive Event from Master
  
}

// When data is received, this function is called.
void receiveI2C(int bytesIn)
{
  while(0 < Wire.available()) // loop through all but the last
  {
    read_byte = Wire.read();     // Receive the incoming byte
    instruction[byte_count] = read_byte;
    byte_count++;
    Serial.print(read_byte);          // print the incoming byte as a character on the Serial line.
  }
  dataPointer = reinterpret_cast<byte *>(data);
  for(int n =0; n < byte_count; n++)
  {
    *dataPointer = instruction[n];
    dataPointer += 1;
  }
  Serial.println();          // print the incoming byte
  ZBTxRequest zbTx1 = ZBTxRequest(ROUTER1, data, sizeof(data));
  Serial.print("Data from I2C: ");          // print the incoming byte
  for (byte n = 0; n < byte_count; n++) 
  {
    Serial.print(data[n]);
  }
  Serial.println();
  xbee.send(zbTx1);
  Serial.println("To Router 1");
  //transmitXBee();
  byte_count = '\0';
}

Now, when I used Serial in the above code:

xbee.setSerial(Serial);

I'm getting values only once. But, as soon as I change my code to Wire:

xbee.setSerial(Wire);

I'm getting values continuously.

Now, my question is that, if I want to transmit to second XBee, I have to make a serial communication. So, I have to keep my code to Serial. But, doing that i'm not able to receive the output on the second XBee.

Here's the code for the receiver part:

#include <XBee.h>
#include <time.h>
#include <stdlib.h>
#include <Wire.h>
#include <Servo.h>

XBee xbee = XBee();
XBeeResponse response = XBeeResponse();
ZBRxResponse rx = ZBRxResponse();
//XBeeAddress64 ROUTER1 = XBeeAddress64(0x0013A200,0x4147FE2A);
XBeeAddress64 ROUTER5 = XBeeAddress64(0x0013A200,0x414E65AE);

uint8_t instruction[4] = {1,0,0,0};
uint8_t count = 0;
uint8_t data[4] = {1,0,0,0};
uint8_t receivedData[4];
byte* dataPointer;
uint8_t counter[6][4];
uint8_t dist[6] = {0,0,0,0,0,0};
uint8_t n = 0;
byte read_byte = 0x00;
uint8_t byte_count = 0;

void setup() 
{
  Serial.begin(9600);
  Wire.begin(0x0A);             // Start I2C on Address 0x0A
  xbee.setSerial(Serial);
  Serial.println("Starting Up!");
}

void loop() 
{
  receiveXBee();
  delay(1000);
}

void receiveXBee()
{
  xbee.readPacket(500);
  Serial.println("Reading for Packet");
  if(xbee.getResponse().isAvailable())
  {
    Serial.print("Packet Available: ");
    Serial.println(count);
    //delay(500);
    if(xbee.getResponse().getApiId() == ZB_RX_RESPONSE)
    {
      xbee.getResponse().getZBRxResponse(rx);
      //delay(500);
      dataPointer = reinterpret_cast<byte *>(receivedData);
      for(int n =0; n < rx.getDataLength(); n++)
      {
        *dataPointer = rx.getData(n);
        dataPointer += 1;
      }
      Serial.print("Packet Received: ");
      //for(uint8_t j = 0; j<6; j++)
      //{
        for (byte n = 0; n < 4; n++) 
        {
          Serial.print(receivedData[n]);
          delay(500);
Serial.println();
      Serial.print("count: ");
      count++;
      Serial.println(count);
      //delay(500);
    }
  }
  else if(xbee.getResponse().isError())
  {
    Serial.print("Error Reading Packet. Error Code: ");
    Serial.println(xbee.getResponse().getErrorCode());
    delay(500);
  }
  else
    Serial.println("No data received"); 
}

Can anyone suggest any way of how to receive data continuously on the second Router, because I shall be extracting values from the receiver end of the Arduino and feed it into the second Lego Robot to do the further actions.

Hello there!

I’ve used XBee modules before, but not your specific model. A problem with my models was that they needed a host in order to operate. Simply connecting them to a serial port on an Arduino didn’t work. Is this the case with your models as well?

but not your specific model.

I don't require a suggestion specifically for this model of XBee, any model of XBee would be good.

I've made the communication between the Arduino and XBee possible using some constant values and was working absolutely fine. But, I got stuck when I added the Lego Robot and tried to extract values from it.

Simply connecting them to a serial port on an Arduino didn't work. Is this the case with your models as well?

No, I've not connected the XBee directly to the Arduino. I'm using an XBee-Arduino Shield to make the serial communication possible between the Arduino and XBee.

    read_byte = Wire.read();     // Receive the incoming byte
    instruction[byte_count] = read_byte;

And store it in an int array. Why?

    Serial.print(read_byte);          // print the incoming byte as a character on the Serial line.

From within an interrupt service routine? Wrong!

  dataPointer = reinterpret_cast<byte *>(data);

It would be a lot simpler to make dataPointer the right kind of pointer to point to the data.

    *dataPointer = instruction[n];

It would have been far simpler to just store the bytes in the byte array in the first place.

You just don't have a clue what you can, and what you can NOT do in an ISR.

When you get that clue, your problems will disappear.

And store it in an int array. Why?

Because, the value which is coming from Lego Robot is an integer so I stored that value byte wise in an array.

From within an interrupt service routine? Wrong!

this was written just to ensure that the output received from the Lego is matching on the serial monitor of the Arduino or not. At the time of actual code, it will be removed.

It would be a lot simpler to make dataPointer the right kind of pointer to point to the data.

Actually, the I'm using data pointer because i want to type cast it into "uint8_t" datatype which is character array and at the time of transmission, only uint8_t data can be transmitted.

It would have been far simpler to just store the bytes in the byte array in the first place.

Yeah, we can do that but how to save an integer data and then store it and then transmit it in form of a character array. Because not doing that it gives an error stating that "incompatible datatypes can not convert int to int*(aka character array)"

I hope I answered the questions, and know that I'm doing a mistake somewhere because of which I'm facing an issue that's why I consulted here to resolve it.

Also, I wanted to ask the change in the line:

xbee.setSerial(Wire) or xbee.setSerial(Serial)

As, when changed to Wire it receives the data continuously on Arduino and transmits it as well but don't receive any data on the second XBee. But, when changed to Serial, it receives the data on Arduino and transmit it only once but maybe the the packet is lost on the receiver side as only one packet is transmitted and in that time the receiver was not able to get the data. Can you help me with it too.

Because, the value which is coming from Lego Robot is an integer

It is NOT. I2C does NOT transmit integers. It transmits bytes.

Actually, the I'm using data pointer because i want to type cast it into "uint8_t" datatype which is character array

uint8_t is NOT a character. char is a character, and on the Arduino is (God only know why) signed. uint8_t is a byte.

But none of that matters. What matters is that the pointer type should match the type pointed to. Using a float * to point to uint8_t data doesn't make sense. Neither does using a byte * to point to unint8_t data, where a cast is required. Make the pointer type uint8_t *, so the cast is not needed.

Yeah, we can do that but how to save an integer data

You do NOT have integer data. You have byte data. If the sender sent an int as two bytes, you have to recombine them manually. Storing the bytes in an int array is NOT going to make ints of the pairs of bytes.

I hope I answered the questions

You did, but the answers were all wrong.

and know that I'm doing a mistake somewhere

You are. You are trying to use functions that require that interrupts be enabled, while in an ISR, where interrupts are disabled.

As, when changed to Wire it receives the data continuously on Arduino

"it" is a pronoun with no referent. Your English teacher would be appalled. What does "it" refer to, in that sentence (fragment)?

Using a Wire instance as the Stream for the XBee to read from/write to makes no sense, because the XBee is NOT an I2C device. It is a serial device.

Thank you Paul, after the suggestions you gave I look into my code, and you are correct regarding the incoming data as a byte from the Lego Robot through I2C communication. So, I changed my code to received the data and store it in a byte array so that when this byte array s transmitted through XBee, it sends the entire data in form of packet. You can see that in the modified code:

#include <XBee.h>
#include <time.h>
#include <stdlib.h>
#include <Wire.h>
#include <Servo.h>

XBee xbee = XBee();
XBeeAddress64 ROUTER1 = XBeeAddress64(0x0013A200,0x4147FE2A);

uint8_t data[4];
byte instruction[4] = {1,0,0,0};
byte read_byte = 0x00;
int byte_count = 0;
uint8_t* dataPointer;

ZBTxRequest zbTx1 = ZBTxRequest(ROUTER1, data, sizeof(data));
ZBTxStatusResponse txStatus = ZBTxStatusResponse();

void setup()
{
  Serial.begin(9600);           // We will spit it back out on the serial line.
  Wire.begin(0x0A);             // Start I2C on Address 0x0A
  
  xbee.setSerial(Wire);
  Serial.println("StartingUp!");
}

void loop()
{
  delay(500);
  Wire.onReceive(receiveI2C);   // Receive Event from Master
  //transmitXBee();
}

// When data is received, this function is called.
void receiveI2C(int bytesIn)
{
  while(0 < Wire.available()) // loop through all but the last
  {
    read_byte = Wire.read();     // Receive the incoming byte
    instruction[byte_count] = read_byte;
    byte_count++;
    Serial.print(read_byte);          // print the incoming byte as a character on the Serial line.
  }
  dataPointer = (data);
  for(int n =0; n < byte_count; n++)
  {
    *dataPointer = instruction[n];
    dataPointer += 1;
  }
  Serial.println();          // print the incoming byte
  //transmitXBee();
  
  Serial.print("Data from I2C: ");          // print the incoming byte
  for (byte n = 0; n < byte_count; n++) 
  {
    Serial.print(data[n]);
  }
  Serial.println();

  xbee.send(zbTx1);
  Serial.println("To Router 1");
  byte_count = '\0';
}

Please tell me in any line I made any mistake as I tried modifying my code based on your suggestions.

"it" is a pronoun with no referent. Your English teacher would be appalled. What does "it" refer to, in that sentence (fragment)?

Using a Wire instance as the Stream for the XBee to read from/write to makes no sense, because the XBee is NOT an I2C device. It is a serial device.

Oh Sorry, I didn't proofread the text I wrote in this line. Basically, i was telling that when i write the Wire instance, the data can be seen coming on the Serial Monitor continuously, which means that the incoming data of the sensor values matches with the data I am receiving on the Serial Monitor.

But, when i change this Wire instance to Serial instance as I want to do the Serial Communication between two XBee(s), I receive the data only once and then it don't receive anything.

I can provide you images of what I receive on Serial Monitor.

When wire is given as an instance: Wire_instance

When serial is given as an instance: Serial_instance

Now, what should I do so that I can receive data continuously because then only I'm able to receive on the other end.

void loop()
{
  delay(500);
  Wire.onReceive(receiveI2C);   // Receive Event from Master
  //transmitXBee();
}

This is wrong. The onReceive() method is called when I2C data arrives. You should NOT be calling it directly.

You SHOULD be registering a function to be called when I2C data arrives. That function will then be called at the proper time. ALL that that method should do is:

  1. set a flag indicating that data has arrived
  2. read all the data, and store it in a volatile global array

Then, loop() should test whether the flag is set. If it is, it should use the data and clear the flag. Using the data means sending it via the XBee.

I really don't see the purpose of using the XBee library/API mode. Just use AT mode and xxxx.write() where xxxx is the hardware or software serial instance that the XBee is connected to.

You are still storing the incoming data in one array and then uselessly copying it to another array. Just use ONE array.

  byte_count = '\0';

A variable with count in the name should be assigned a value of 0, not a NULL. The variable should be initialized before you use it as an index, not after you are done with it, IMHO.

This is wrong. The onReceive() method is called when I2C data arrives. You should NOT be calling it directly.

Actually, I took this example directly from the Dexter Industries website who build EV3 Lego Robots and they were using this example by directly calling it: Arduino-Lego Interface. So, yoou can see I just replace '1' in the code with '0' as per my code. Tell me if i'm wrong, I can try doing by the way you suggested.

I really don't see the purpose of using the XBee library/API mode. Just use AT mode and xxxx.write() where xxxx is the hardware or software serial instance that the XBee is connected to

I'm using API Mode, because in the final implementation stage I need to communicate 6 XBee(s) with each other, each transmitting and receiving the data. Right now, I'm just testing with one as the transmitter and other as the receiver.

You are still storing the incoming data in one array and then uselessly copying it to another array. Just use ONE array

Yeah, I've changed to one array only.

A variable with count in the name should be assigned a value of 0, not a NULL. The variable should be initialized before you use it as an index, not after you are done with it, IMHO.

I was making it null so that next time loop initiates, there is no garbage value taken. And also. I've initialized it in the starting above the setup function as a global variable and then I'm making it null at the end. Though when I removed it, I'm not getting values after first iteration.

I sought out a way by declaring it as a local variable instead of global variable and removed it from the very end, the code still works just like before.