Using XBee 868 as R/C


After having second thoughts about whether a XBee 868 is suitable for a RC, seeing as it is dubious if it is suitable for video transmission, see XBee and video streaming, I thought that it would be wise to check to see if they are appropriate for R/C control. This search surprisingly throw up some issues with the XBees devices, namely:

  • groundplane issues and;
  • packet freeze and the need to reset (due to a 10% duty cycle).

See also RC Transmitters – DIY – XBee

A general note on XBees for full R/C

This article, Xbee for full remote control, recommends against using any XBee for full remote control, due to latency, and reliability (periodic loss of comms) issues. This comment

I build the BreezeUAV project without radio system and now that the project become good enough to fly, I regret it.

Firstable XBee are great, but sometime the communication is lost and my failsafe capability is not good enough.

Secondly, it’s always better to have two links of communication and be able to switch from one of them automatically.

In my project, I don’t control the plane directly. A joystick is used to define the pitch roll yaw and speed, those data are send to the UAV which try to achieve that attitude and speed goals.

I’m updating data from ground station to UAV at 30 Hz, while telemetry from UAV to GS is download at 1 Hz. Using a radio, I’ll be able to increase the telemetry data rate.

It feels good to build cheap system, but when they crash … Safety is always better 🙂

and this comment

Generally I think some sort of graceful recovery from a comms failure (either permanent of intermittent) should be built into every UAV flightplan. Either a return to base and loiter or in the case of ‘copter craft , a hover and descend mode. That said, If you think you are going to have significant latency, then an alternative coms method should be used. The idea of using zigbee was to save having to buy the radio hardware (and I bet that digital comms is the way forward anayway) . By using a zigbee for the comms, an onboard micro is needed in the UAV , so loads of different failure modes are possible.

In my development, the biggest latency was between the UI and the serial code, not the actual transmission stuff. But that was because I was trying to abstract the UI from the comms method and that didn’t work for RT control.

say it all, really. It also points to this article, Death of an XBee:

Jack Crossfire has done some testing with them for applications like this. Latency and reliability were indeed issues. See his comments here, for example.


Case studies:

A thread on Digi’s forums points out the issue with the 10% duty cycle:

I seem to be running into the / some kind of duty cycle problem: I have two XBee 868 PRO’s (S5) connected to two Dev-Boards. Range Test works perfectly fine, and data transfer also works well without significant packet loss.

However, even when sending only a constant data stream (one direction) of 270bit/s (without any RF-overhead / retransmits) over the X-Bees, the connection breaks down after 30-40 minutes. As the 868-PRO manual states, the Duty-Cycle limitation should only kick in for continuous RF data streams of >2400bps (10% duty cycle on the 24Kbps), and I am well below that value! If I hard-reset the transmitting X-Bee, then it re-begins transmitting, and if I wait long enough then it also begins again. When the connection is broken, it by the way still receives data from the UART (transmit LED on dev-board blinking), but the receiving X-Bee does not receive this). To investigate this behaviour, I performed some logging of the messages that I receive over time: The first image is with 272bps transfer rate and the second one with 880bps, with the x-axis being the time in milliseconds. It is pretty obvious that the x-bees begin the retransmit after pretty exactly one hour again…

The question, which was posted in 2013, remains unanswered…

The duty cycle issue is eloquently explained on 868MHz Issues


ETSI requirements as specified in the ETSI EN 300 220-1 define the requirements for the 868-870MHz bands. For a conducted output of 180mW and radiated outputs up to 500mW, the maximum allowed duty cycle is <10%. This duty cycle is measured as the amount of TX time on, monitored over one hour and relative to a one hour period.

868 MHz

To comply with regulations, 868MHz modems with more than 5mW are limited to a 10% DUTY CYCLE. As 868MHz modems have an exceptional range, it is important not to block the frequency for a radius of 40km.

Both AeroComm and XBee comply with the 10% duty cycle rule.

A pair of disparaging threads can be found on the DIY drones website, with the first thread linking to the second:

But please think twice before getting the 868 modules. I have them and I feel their limited throughput/duty cycle is quite an handicap, unless you need them for very limited data streams. You may (re)read the discussion we once had around the Xbee Pro 868 here.

My problem is following. After a few minutes (about 1500 groundstation data strings consisting of about 120 bytes), the receiver Xbee stops receiving the RF stream. When I manually reset the adapter board of the sending Xbee, the data stream starts again. But stops again after about the same time as to first hanging.
I don’t know if I am creating an overflow in the Xbee buffer, maybe on Tx side. And I do not know either if and how I could send a reset to the Xbee to avoid those hangings.
To see if the Xbees could handle the data fast enough, I tried to shut the GPS data during transfer and see if Xbees had to go on with buffered data for some time. It is not the case (groundstation data stops immediately when I cut the GPS data, and starts immediately upon plugging GPS data again).

If anybody experienced similar RF transfer hangings with their Xbee, I would be glad to know how they solved it.

An explanation (also on page three) states:

I’m not sure if this helps but a quote I found somwhere else on this site may be the answer: ” Permalink Reply by Jack Crossfire on February 11, 2009 at 3:32pm…..You have to flash configuration with an ATWR command or they’ll randomly lock up. Lock up means they either stop sending or receiving until being power cycled. There’s no explanation other than Digi couldn’t figure out why they locked up & after months of experimenting, ATWR was the command that fixed it.”.

Unfortunately, the post to which he refers has been deleted, although this was able to be salvaged (page 4 of the thread):

” Q:…so do i get that right i need to send +++ATWR but how often ? 1 once on setup or every second, minute ??
A: (Jack C)
What the ATWR command does is take the configuration you did with the AT commands & stores it in a piece of memory that doesn’t get erased during power cycles. It only needs to be run once. From then on, no matter how many times you reset it, it always recalls the stored configuration from flash.

API mode is slower. You should forget about it & start flying.

One other thing. You’re probably still in resend mode. You need to get out of resend mode & use broadcasting mode. If you fly in resend mode, whenever you lose contact or change batteries, the sender will buffer data until the receiver comes back. When contact is regained, the sender will transmit several seconds of buffered data & cause your trirotor to crash.

To enter broadcasting mode, use

Digi provided an official response to the issue, which was:

“Thank you for contacting Digi. The XBee-PRO 868 radio is limited to a 10% duty cycle. This means that you are unable to stream data across an 868 MHz product. To resolve you issue, I would recommend decreasing the amount of data being sent and enable hardware flow control.”

Jumping ahead to page 8, a comment mentions this, which was found on the Digi forums

The 10% duty cycle exists in order to comply with ETSI European Telecommunications Standard for 868 MHz. In compliance with the law, emmitters cannot transmit for more than 6 minutes total in each hour. If you transmit for 6 minutes continuously then the radio could only receive (not transmit) for the next 54 minutes. This duty cycle is not based on temperature.

The register is cleared upon a power cycle of the radio.

Testing the Freeze conditions

Still on Page 4, the conditions under which the freeze is encountered is:

the Xbee transmission hung at 9600 bauds sending 120 bytes every second (1Hz). I had set up my GPS to 1 Hz and 9600 bauds for this test and it sends only RMC and GGA sentences to keep data flow minimal. I ran for 10-15 minutes before hanging.

Or put another way:

When it crashed, I was sending a 120 bytes string at a 1Hz rate at 9600 bauds from PC to PC ! it lasted 10 minutes or so.
As per Xbee 868 references, as long as the temperature of the chip is below 60°C, an one hour average duty cycle of 10% has to be respected. When the chip temp reaches 60°C, a ONE SECOND average duty cycle of 10% IS ENFORCED until temperature goes below threshold temperature.

Excerpts from the reference PDF:

Duty Cycle
The duty cycle of this radio is 10% averaged over the period of 1 hour. Meaning, if the next
transmission will push the running average duty cycle over the 10% limit, the module will not
transmit until enough time has elapsed to stay under the duty cycle.
Because of heat restraints of the module, a 10% duty cycle over the period of 1 second will be
enforced after the measured temperature of the module rises above 60°C.

CTS flow control:
Note: It is recommended to monitor the CTS line because of the duty cycle. If the radio cannot
transmit because it will exceed the duty cycle, the UART buffer could be filled up with data and
trigger the CTS line to de-assert.

More testing strategies:

Current average duty cycle reading:
Duty cycle can be read with API command: ATDC. It returns the percent used of the 10% duty cycle, i.e. if the average duty cycle is 2.5%, the result is 25.
As well, the temperature of the module can be read with ATTP. Result is in degrees Celsius.

In my test loop, I tried to integrate such command mode readings after every 10 loop iterations, trying to read the result in some vars for monitoring. But since I am a neewbie, the reading keeps empty, probably because of some delay missing. While in debuger, in Python command mode I can read the AT command results though. I’ll try to tweak my code until I get something. But overall I know entering command mode periodically slows down transmission. Ideally, one should keep track of temp and/or duty cycle while staying in “transparent mode”, using the DC result as a parameter for optimal RF transmission (for example slowing down Tx when DC result is reaching 100, increasing Tx when it is below 50 or so).

If anyone knows how to implement DC or TP reading while in “transparent mode”, it would be perfect. I read something like that in a forum yesterday, but didn’t bookmark… The guy could read RSSI WITHOUT going in command mode and estimated the distance between two Xbees based on RSSI results. This too could be interesting for our UAV purpose, for example having direct command from the ground and switching to AutoPilot or RTL when RSSI gets too bad for Xbee downlink and/or uplink. This in complement of GPS fixed distance threshold or as a nice RSSI variable threshold replacement.

On page 5, it is stated that these devices are not really meant for R/C control

Please excuse me i am misunderstanding what is being said, but is it actually technically possible to use these Xbees at 315mW transmitting NEMA sentances at 1+Hz without them locking?

To which the answer was

It’s what we want to find out. The RF power is not an issue for now (though it could become too, because higher power consumption means higher module temperature). It is the data quantity and rate the module can send while keeping under the 10% duty cycle and 60°C temperature that seems limited. The modules are not meant for sending continuous streams of data, but rather to send data at regular frequency or when triggered by some sensor.

Staying on page five, the 10% duty cycle is dealt with

As per the specifications table in the PDF the max throughput of the modules is 2.4kbps.
RF Data rate is 24kbps, 10% duty cycle indicates that you can put a maximum of 2.4kbps serial data through it over a one hour average.
Incidentially this is about 307 bytes / second so should still be useful for moving map etc

which was answered by

Well, that should be right. Thus (if I remember well) 2400bps = 2400 bits/sec = 300 bytes/sec including stop bits = 266 bytes/sec without stops.
I just started a new “long run” test:
– Xbees at 2400 bauds
– 120 bytes from GPS every second to the Xbees (less than half theoretical max serial throughput as calculated above)
I think it will last for a while, but not holding the long run. But let’s wait…

1604 sentences x 127 bytes = 203’708 bytes were sent and received through RF before Tx HUNG !
The RF transmission ran for 1604 sentences / 60 sec = 26.7 minutes before it HUNG.

More test results from page 6

Both modems set to 9600bps serial data rate
Both modems set to 1mW power output
1Hz modified NMEA string approx 81bytes long injected to the remote modem

Perfect data transfer
310KB transferred over approx 1.5 hours, data link working perfectly when I shut it down

I’m reprogramming them now and will try again with a higher power level

While page 7 deals with RSSI mainly (see section below), there are some interesting temperature/cooling tests

I tried 50 bytes, 9600bps, 25mW tx on PC side, 300mW tx on loopback.. With rather poor results until I put my finger on the loopback chip (the PC side was not really warm) to test if it was hot.

3sec max after I put my finger on the chip it started pushing 100% link, and every packet went through :P.

I really think cooling should help them work better than without.


I know this is an old discussion now but to anyone using it for info I thought I would just add my findings. Cooling them makes a big difference to the run time at higher power levels. I’m running mine at 300mW on both sides @ 9600bps and have attached a small heat sink to the module on the EZ* which is mounted near the tail. The base unit has a larger heat sink and fan. both are cold to the touch and have run for 2 hours today so far dropping only 3 packets. 9600 bps is enough for me but I could not get anywhere near that runtime before cooling them.


Page 7 deals with RSSI mainly

RSSI blah

and there is some non-functioning code on page 8

#include "Message.h"

int greenLedPin = 11;
int redLedPin =10;
long connectionSpeed = 9600;
Message incomming;
Message outgoing;
long R_status=0;
long G_status = 0;
int tmp;
int RSSI =0;
* This is the callback function that is called whenever the data is updated.
void dataUpdate(SerializationData *data)
    RSSI = pulseIn(9,HIGH);               // ** Issues
    outgoing.setTitle("signal_r");        // ** Issues
    outgoing.setData(RSSI);               // ** Issues
    SimpleSerialization.write(&outgoing); // ** Issues
    delay(10);                            // ** Issues
    if(strcmp(((Message *)data)->getTitle(),"red_Set") ==0)
        if (((Message *)data)->getData() == 1)
            digitalWrite(redLedPin, HIGH);
            R_status = 1;
            digitalWrite(redLedPin, LOW);
            R_status = 0;
//      outgoing.setTitle("red_Status");
//      outgoing.setData(R_status);
    else if (strcmp(((Message *)data)->getTitle(),"green_Set") ==0)

        if (((Message *)data)->getData() == 1)
            digitalWrite(greenLedPin, HIGH);
            G_status = 1;
            digitalWrite(greenLedPin, LOW);
            G_status = 0;

void setup()
    Serial.print("+++");       // ** Issues
    delay(1100);               // ** Issues
//  Serial.println("ATRE");    // ** Issues
//  Serial.println("ATD0 2");  // ** Issues
//  Serial.println("ATD5 2");  // ** Issues
    Serial.println("ATRP 40"); // ** Issues
    Serial.println("ATWR");    // ** Issues
    Serial.println("ATCN");    // ** Issues
    delay(2000);               // ** Issues
    pinMode(9,INPUT);           // ** Issues
    pinMode(greenLedPin, OUTPUT);
    pinMode(redLedPin, OUTPUT);
    attachInterrupt(0, readSer, CHANGE);

void loop() {
    tmp = analogRead(5);
    tmp = analogRead(0);

void readSer()


Need to Reset

The need to reset XBees after a certain number of packets have been received is mentioned in a couple of articles:

I finally did some more testing with this with quite surprising results:
1mw loopback test, 4800bps, standard X-CTU test string, 3365packets went through until it hit a wall. Resetting only one or the other XBee did not suffice, both had to be reset.
100mw loopback test, otherwise same thing with settings, tried with both firmware 1025 and 1027 and both stop at exacly 3465packets, same issue there, resetting one does not solve the issue, resetting both does….
I am testing 300mw now, and there seems to be no difference at all here, same 100% link until it drops…
Has anyone else encountered these same (exact??!) numbers?
I am thinking this should either way not be hard to overcome seeing that the unit works again after reset without cooling it or anything, so I think I’ll stick some code in the arduino to do this for me every so often…
Anyways surface temp on the units stays within ~45 degrees give or take a few so the heat should not be the issue.

Noth666 then writes, on page 9

I’ve now found a “solution” for this, but it will kick up the duty cycle above the allowed 10% (not a bad thing in everyones eyes).

I wrote a small program which just basically simulates sending servo position update packets via the XBee 868 to a receiver unit. What I tried out now is to simply reset the modem and wait for a small moment before retransmitting data, between every full update packet.
Of course this is not optimal, it should not reset so often etc, but I did it like this to test the theory with minimal work.

And it works like clockwork, but it does have a delay between payloads and thus is slower than it would be without the reset and delay. So I will work on an optimal transmit loop count between resets based on my payload, and anyone else could do the same.

To reset the modem connect a cable between the RST pin on the Adafruit module and one of the digital pins on the Arduino/ArduPilot.

Then just simply add this:
in void setup()

digitalWrite(resetPin,HIGH);//set the RST pin high, low will reset the module

And then in the loop code with appropriate counter (you can find this out easily if you transmit a same size payload every time, otherwise make it low enough to be safe – otherwise use response ACK packet timeout to activate)

delay(TXdelay);//wait for data to transmit
digitalWrite(resetPin,LOW);//press reset
delay(RSTactiveDelay);//spec minimum is 100us to register
delay(RSTrecoverDelay);//wait for XBee to initialize

Of course you’ll need to tune your variables for your particular application, and define them.

From  Xbee for RC Quadcopter, whilst not a particularly enlightening thread it did mention this

I don’t think this is good option to use Xbee. It is queuing data and then packetize and sending, so you will have there some delay. But you can send it also to send each character when you receive it. And have bad experience with Xbee PRO 868, it is behaving strange and freeze after a while. Would be critical if it freeze.
If I do hw reset of Xbee, it will turn off Serial line, and my virtual COM port disappear, don’t understand how this could be possible.

From the same link, the freeze is explained

The Freeze is not strange, that’s because the EU regulation-authorities only allow a “duty-cycle” of 10 percent of the full bandwith, measured over a period of one hour. That means you can send 6 minutes at 24kBit/s and then the module blocks (freeze) for 54 Minutes. If you will send over a longer period than 6 minutes, then you have to lower you bandwith.
So it’s not strange, its just a regulation issue.

Other Workarounds

A workaround and code is provided on Xbee 868 revived?

After a great deal of tests, this option for telemetry did not work for our us, I now have a working telemerty kit option which can be seen here, and you can buy the Telemetry kit from


The Xbee 868 how ever an older module it still has a great deal to offer and as some of you might remember this post as the original fix there has been a lot of other fixes that came to light hardware (Quick_and_Dirty) and software thanks to James and Noth666 but in A recent post of mine, we discussed the options for the UK and I came upon a new fix that seems simple yet effective enough to get the advantage of the offered 40km range as stated Here.

What you must do is reset your Xbee through the UART, modify the telemetry output to stop for a (guard time) amount of time, send the “escape sequence (+++), wait for another “guard time” and then send the reset command (ATFR). In this case the escape sequence is changed from the default (+++) to @@@ because the telemetry for Ardustation for example contains the +++ sequence and because of this there is great risk of putting the XBee in command mode when you don’t want to.

I made contact with Marcus Fahlen and he was kind enough to supply a great deal of information on his work, and work around, the following information & code is the work of Marcus Fahlen, and with his permission I have added it below, I’m at this point where I need help on where and how to implement the code and from which pins, just to make sure.

// XBee control functions by Marcus Fahlén
// sends a reset command (ATFR) through the
// UDB telemetry UART.
void xbee_reset( void) ; // This routine runs from the "servoPrepare.c" module in MatrixPilot
// main code directory. There is a flag i "options.h" for enabling or
// disabling the use of this code.
// This setup is for the 868 XBee PRO module which has a duty cycle
// of 10% (6minutes continuous transmission). Because of this I have written
// this piece of code that is meant to reset the XBee module at a choosen
// interval. I have done a lot of experimenting with different settings but
// I still have to find one that works 100% (count up to the guard time,
// send the escape characters, count up another guard time and then send
// the reset command. I don't bother to listen for any "OK" to check if
// the procedure was successful. That would demand bi-directional
// control code which I'm not capable of producing at the moment.
#define XBEE_PRE_GUARDTIME 25000
// XBEE_GUARDTIME : use approx 50000 for the "guard time counter"
// if your XBee is set up fo 50ms guard time.
// I have had greatest success with 50ms guardtime. I'm not sure why,
// but the shorter guardtime the more critical the timing becomes.
// Then again, longer guard times often results in a state where the
// XBee is in command mode but never recieved the reset command?
// This highly unwanted state ends when the XBee command mode "times-out"
// before it returns to "normal" operation. This time-out period is settable in
// the XBee and should of course be as short as possible.

// XBee control functions by Marcus Fahlén
// As for noe it only sends a reset command (ATFR) through the UDB telemetry UART.
// Hopefully it will be able to perform more sophisticated control functions later
// like output power management based on distance to base station and other circumstances 
// that calls for a variation in output power.
void xbee_reset (void) ;
unsigned int xbee_skip = 0 ;
long guard_time = XBEE_PRE_GUARDTIME ; // XBEE_GUARDTIME : approx nnn for 10ms guard time 
void xbee_reset( void)
 ////// Skip every other run through this function. 
 if (++xbee_skip < XBEE_RESETPERIOD) return ; 
     unsigned int delay;
     for(delay=0;delay<guard_time;delay++);     // pre escape sequence guard_time  
         serial_output( "@@@" ) ;     // send escape sequence
       for(delay=0;delay<guard_time;delay++);     // post escape sequence guard_time
     serial_output("ATFR\r\n") ;
     xbee_skip = 0 ;
     return ;
// Maybe the reason for the reset routine missing is that it becomes
// "disturbed" by the interrupts running on the UDB? If an interrupt occurs between the
// the time where the "ATTENTION" command is being sent, but before the "reset" command is
// sent, I think causes the "hang state" that I have a lot of trouble with.
// The only thing to do is to calculate the timing need carefully, and maybe disable the
// interrupts all together while the "reset" command is being sent. That would assure that the
// reset is carried out as fast as possible. (50ms guard time times two + the time it takes to
// send the command characters)
// Use a command mode timeout of 200ms (ATCT2) which is the shortest possible.
// As described above, there is a chance that the routine "misses" and leaves
// the XBee in a non-transmitting state (comand mode), but it will only last for 0.2sec
// if setup as described. Then the XBee exits the command mode and resume normal operation.
// Also change the default escape character to "@" since the the telemtry
// output uses lots of "+++" in its data.
#define XBEE_POSTCOMMAND 10000



Some pointers:

“Calibrated differently” It doesn’t sound likely – it sort of suggests changing the output impedance to match the antenna – I think that unlikely – the wire “groundplane” and external (via SMA) dipole are similar enough in impedance not to worry about. Perhaps it refers to adjusting the wire antenna length?


Any performance difference will depend on how you set up the antennas – I’d imagine typically a SMA fed dipole would give the best results – but there could be very little in it. As far as I’m aware the RM SMA antenna in the box is a dipole. Theoretically a ground plane antenna (which is what the wire antenna will form with the surrounding circuitry as a ground) has slightly more gain than a dipole.

If your a dab hand with a soldering iron it would be easy enough to unsolder the wire and solder on a socket – as as far as I can see thats the only difference between the modules.


Unfortunately, it does seem that a traditional method is best for flight or land based control of drones/robots, as prescribed in REMOTE CONTROL ROBOTS.

Although, having said that, the featured image comes from DIY Wireless Robot Remote Controller Using XBee or Ciesco XRF, so maybe if you can live with the issues of latency, and reliability, and you are controlling a land based robot, and not a drone, then it certainly is feasible.

2 thoughts on “Using XBee 868 as R/C”

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s