kmark / Bee

The simple DigiMesh XBee library for Arduino
Other
11 stars 9 forks source link

Bidirectional Communication #8

Closed myadollahikhales closed 7 years ago

myadollahikhales commented 7 years ago

Hello! We really appreciate your great library for Digimesh and had a couple of questions for our case. We want to be able to send data/parameters and receive them at both ends. Our setup includes two Xbees in API 2 mode connected to their respective Arduino mini's. We were wondering when and what the callBack function does? and what we should be expecting to be written to the serial? To check if we even enter the void function, we each added a println msg there. while one of the arduinos sent a message and confirmed that it had gone in to the callback method, the other one just sent its own msg and didn't enter its callback method.

Anything will be greatly appreciated :) Thanks in advance

kmark commented 7 years ago

Hi and thanks for giving Bee a try!

The callback function is run when the Arduino receives an incoming broadcast. So that's where you'll deal with incoming data.

Captain is a great (well, in my opinion) example of the Bee library. If you just look at the code related to Bee I believe it's quite straightforward. There are the Base and Ship components. The Base code runs on an Arduino Mega 2560 R3. The Ship code runs on an Arduino Uno R3. Both have XBee-PRO 900HP's connected. The Base is the controller. It receives data from the RC ship and tells the ship where to go by giving it commands to change its rudder position and motor speed. The Ship is tasked with receiving those commands from the controller and may also send back pings (to let the controller know it's connected) and GPS data. So this is a great, simple, bidirectional communication example.

So let's start with the Base.

  1. Here we're creating a new instance of Bee called XBee and feeding it a serial with a configured baud rate. For reference it's a HardwareSerial (Serial3) and 19200 baud.
  2. Then we set the function that will act as our callback and tell Bee to start.
  3. In our loop() we're calling XBee.tick() so the library can check for incoming data. You don't have to run it every single loop() if you keep your loop function fast but it's a good idea to call it as often as possible as I haven't tested it at all with very slow loops.
  4. Here is where we're sending data to the ship. The handleInterrupt() is simply an AVR interrupt that will be called at 1 kHz if I remember correctly. So that's once every millisecond. I used sizeof there because we know the size of cdt at compile time. XBee.sendData(cdt, sizeof cdt) will broadcast all the bytes in cdt to any listening XBees.
  5. Here is our XBee callback function. Which just goes right ahead and calls handleRx(BeePointerFrame *). We check the first byte of that frame's data to see if it's a "GPS" response from the ship. This is completely up to us, Bee does not care what frame->data is and it's completely unaware that the first data byte is a header of some sort. It just knows it's a bunch of bytes. Once I'm sure what kind of data it is I can assume the structure of frame->data and act upon its contents.

This is the BeePointerFrame structure that you'll receive in the callback. It has the frameType which will be one of DigiMeshFrames. I believe this will always be ExpRxIndicator AKA 0x91 simply because that's all Bee supports. It has the packetLength which is just the length of the DigiMesh packet. It has source64 and source16 which are the 64- and 16-bit source addresses respectively. And then, most importantly, data and dataLength. dataLength will tell you how long data is, as expected. And data will just be whatever data actually was sent to you.

What about the Ship?

  1. This time we're creating a Bee instance named XBee but feeding it a SoftwareSerial.
  2. We set the callback and get shakin'.
  3. We send some data every second to the controller. This was supposed to carry the current GPS coordinates but my GPS module sunk (in the water, along with the ship it was contained in) and I didn't have the cash or time to buy another. So it really just serves as an activity ping so the controller knows the Ship is still in comms range.
  4. Our callback which just calls handleRx(BeePointerFrame *). And then we act on that frame. We check if the first data byte is a CDT header. This means Current Direction and Thrust. The second byte (thrust direction) is 'F' or 'R' for forward or reverse. The third byte (direction) is just an 8-bit unsigned integer that tells the rudder servo where it needs to be. And the fourth byte (thrust) is another 8-bit unsigned integer that tells the motor how fast to go.

Sidenote: My GPS data was originally going to be represented with floating point numbers so I had to use memcpy() to write the raw bytes that represent a floating pointer number to the array so I can send it. This is pretty useful and worked well (it's easy to reconstruct the floating point number on the other side) but you need to be certain that both Arduinos are representing floating point numbers the same way.

Hopefully this gives you some insight on how I used Bee in a "real word" application with bidirectional communication happening at relatively regular and frequent intervals. And, yes, it worked!

myadollahikhales commented 7 years ago

Thank you so much for your detailed explanation!!! Your detailed and meticulous code sure did deserve the prize :) Thank you again.

kmark commented 7 years ago

You're welcome! Good luck with your project.