Couple of days ago, I picked a pair or Xbees at Sparkfun. The plan is to build low power sensor platform using PICs, Zigbee radios, and Linux. In this article, I’m sharing my experience in building with Xbees.
The BOM includes two Xbee 2.5 RPSMA radios, Xbee USB explorer, Xbee breakout, PIC18F4520 microcontroller, and a breadboard. The code is written in C18 and built in MPLAB. Project files are available in download section.
Even though it is (presumably) possible to get two Xbees talk without any configuration, I decided not to do it. First, the firmware was old; second, I’m planning on having more than two radios. In addition to that, new stack supports firmware upgrades over the air. I downloaded X-CTU and upgraded my radios to Xbee ZB; one became a coordinator( pictured on the right ), the other one a router.
I also followed a procedure outlined in “Quick start” guide for 2.5 and performed range test to make sure radios can see each other.
For the purpose of this demonstration, I made PIC part of the setup as simple as possible. The schematic diagram of Xbee to PIC connections contains MCU itself and 10K pull-up resistor. The MCU is clocked by 8MHz internal oscillator. The USART speed is set to 9600bps. The second radio is left installed in USB explorer board and connected to Linux machine. On my Ubuntu 8.04 server I can see it as /dev/ttyUSB0. To communicate to this device I use minicom terminal emulator. Similar setup can be made on Windows; my favorite terminal emulator for this platform is Putty.
Xbee terminates a line with single CR (carriage return, or return cursor to position number one). By default, terminal emulator program won’t add LF ( linefeed, or move cursor to the next line); as a result, new output gets printed over an old one. Most terminal emulator programs has a setting for this very case called “Add linefeed” in minicom or “implicit CR in every LF” in Putty. Output of my code is also formatted this way – no linefeeds.
Flow control is essential. Xbee’s serial engine is slower than PIC; it is very easy to fill Xbee’s receive buffer and start losing data. Usual approach is to pace the transmission rate using delays; however, this also increases time when MCU is awake and, consequently, power consumption. On the other hand, using flow control signals it is possible to put MCU to sleep during CTS high (as you can see on this logic analyzer trace, given constant trasmission, CTS goes high for quite some time and we can make waiting time even longer relative to sending time by increasing the port speed).
Let’s talk about pins. I always define PIC pins that I use in a header file called project_config.h. Later I use functional names given to pins instead of pin names. For example, CTS is defined in project_config.h as:
#define CTS PORTDbits.RD0 #define CTS_TRIS TRISDbits.TRISD0
This way, if you decide to use some other pin as CTS, you only need to change these two lines and recompile.
The code consists of initialization, 1ms timer interrupt, serial functions and interrupts, and CLI task, which runs in endless loop. Initialization is one-time hardware setup – configuring port direction, timer3, CCP2, USART, and interrupts. Serial routines are borrowed from Fred Eady’s numerous publications in “Circuit Cellar” and his 2 microcontroller communications books. I just ported them from Hi-Tech C to C18 and added CTS flow control. They are described in great detail in Networking and Internetworking with Microcontrollersbook. Here, I just want to point to one detail. There are two places where we can check on CTS. The first place is a sendchar() function on line 8:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /* send character */ /* blocks program execution while txbuf is full or CTS is high */ /* use with caution */ BYTE sendchar(BYTE data) { BYTE tmphead; tmphead = ( USART_Tx_head + 1 ) & USART_TX_BUF_MASK; // calculate buffer index while ( CTS ); // wait for CTS to go low while ( tmphead == USART_Tx_tail ); // wait for free space in buffer USART_Tx_buf[tmphead] = data; // store data in buffer USART_Tx_head = tmphead; // store new index PIE1bits.TXIE = 1; // enable TX interrupt return data; } |
This works well if program is not transmitting a lot of data all the time.
The second place where CTS can be checked is serial transmit interrupt routine in line 16 (commented out). Since transmit interrupt flag won’t be cleared, the program will constantly go in and out of interrupt routine until CTS de-asserts.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | #pragma interruptlow lowPriorityISR void lowPriorityISR(void) { BYTE data,tmphead,tmptail; //USART vars /* USART handler start */ if(PIR1bits.RCIF) { data = RCREG; // read the received data tmphead = ( USART_Rx_head + 1 ) & USART_RX_BUF_MASK; // calculate buffer index USART_Rx_head = tmphead; // store new index if ( tmphead == USART_Rx_tail ) { // ERROR! Receive buffer overflow } USART_Rx_buf[ tmphead ] = data; // store received data in buffer } //? need to check for Tx IF if( TXSTAbits.TRMT /* && ( CTS == 0 ) */) { // check if all data is transmitted if ( USART_Tx_head != USART_Tx_tail ) { tmptail = ( USART_Tx_tail + 1 ) & USART_TX_BUF_MASK; // calculate buffer index USART_Tx_tail = tmptail; // store new index TXREG = USART_Tx_buf[ tmptail ]; // start transmition } else { PIE1bits.TXIE = 0; // disable TX interrupt } } /* USART handler end */ } |
CLI, which runs from the main loop, checks for incoming character. If there is one, CLI branches according to the character. If no character was entered, CLI returns to the main loop and gets started again. CLI is a state machine; it remembers where it last returned from and starts from the same place next time. The menus are empty except for “Show” menu item 1; other menus are just placeholders.
Finally, I’d like to show you how the end result looks like. Build both ends of the link as described, then build and run the PIC software. If everything is correct, you will see the banner and prompt. Press “1″ and prompt will change. Press “1″ again and the software will print current uptime.
I will be building several different sensors based on this setup. They will be posted here as soon as they are operational. In my next article I talk about next steps in Xbee sensor development – taking measurements and switching between Xbee command and data modes.
Good luck with this project. Let me know if you have any questions or issues.
Oleg.
Related posts:



hi, please help me with xbee, i’m also interfacing xbee with 18F, 18f4550.
I’m connected with xbee only with rx, tx and gnd. If i send data from :
pic ->xbee ))))))xbee->pic i got huge lag. i tryed to send 0 to 9(with 1s delay between chars) in a for loop from one pic to another and i receive first 9 chars ok, but after that it stops and receiving 3-4 bytes bulk, and i lose data. i can give you the schematic, code, everything you need, please help me.
Have you tried to use my code? You shouldn’t get much lag – around 0.1sec, and if you insert 1sec delay between symbols, you won’t need any flow control.
I’m having a similar prob – The receiver sometimes saves up all the received bytes and then just releases them in one big lump. The first few are ok though?
There are several things to check. First, the packetization timeout – basically, Xbee accumulates some data before sending. It can be configured to zero, if desired. Second is the distance. I saw similar delays when Xbees were far away from each other. Also, I saw it more often when Xbees are working in AT mode; for some reason, API mode is more robust.
Hmmm, interesting.
I have set packetisation to zero for both the co-ordinator and the end device, and the XBees are within 1m of each other! Typing one character per second works, but any faster and occasionally they get buffered and then spurted out, sometimes with one missing… Am just using two computers.
Have you tried to enable CTS flow control?
Добрый день Олег!
Пишу по русски, чтобы ты не забывал родной язык.
Я живу в Санкт-Петербурге и работаю в Северо-Западном техническом университете. Сейчас стоит задача разработать аппаратуру для съема физиологических и биомеханических параметров (ЭКС, пневмограмма, электромиограмма, ускорения) со спортсменов во время их тренировок и непрерывной передачи их в реальном времени на ПК тренера. Дальность передачи от 100м до 1-го 2-3 км (разные вариаты), возможно с ретрансляторами.
Исходя из твоего опыта для модулей Xbee на какую скорость передачи данных можно рассчитывать. И, очевидно, мы можем воспользоваться твоей программой программирования микроконтроллеров. Сейчас у нас наибольшая проблемма – это прогаммирование микроконтроллеров(PIC или TI).
С уважением, Борис
I had same problem with using AT commands to communicate with Xbee’s After first 7~8 bytes (using PC-to-PC terminal software) I would loose any other bytes sent. Then all sudden out no where Id get the lost bytes, this time delay could be seconds to good solid minute! But it only seem happen one direction, forget if it delayed from Cord. to End point or other way around. In any case API mode is way go, really study the Datasheet/Manual, as what I am doing again today! :) Once you “see” how there examples work, and perhaps try pluging them by hand into a terminal program, you will see the Xbee in API mode come alive.
my two cents anyways…
Sean-
Try to turn flow control on (CTS). Also, PC might simply wait for serial transmit buffer to be filled before sending/receiving the data. Many people report this kind of behavior, however, I’m yet to see one communicating between Xbee series 2 and Linux machine with flow control turned on on both sides.
I use to have the same problem with my xbee. The connection is the same (with no cts) and I operate in AT mode. The problem is that I set XBEE in multicast mode (DH=0, DL=FFFF for both Xbee). It did sent data between the two but there is delay and as though the data being buffered.
My solution is to set XBEE in unicast mode ( DH & DL of the first XBEE is the SH & SL of the other XBEE and vice versa). The data came out as expected and there was no delay. For more info on multicast and unicast plz goto http://www.digi.com.
Hope this will help.
I get same problem with xbee series 2, coordinator in broadcast mode. Very unreliable transmission. Fine once destination addresses are set.
Hi! I’m now at work with ZigBee modules. I’m trying to communicate a PIC16F84A and a PC with XBee pro but with no results. The problem is that the PIC doesn’t have serial input/outputs. I think that I should use your pic, PIC18F452 in order to achieve an easy connection between PIC and ZigBee. The Pic controlls two motors and I ony wanted to send the commands by Hyperterminal or X-CTU for example.
I have read your information about PIC-Programming but I don’t understand why should I programm all that in order to implement a transparent connection (replacement of an Serial cable). Could you help me? Thanks Oleg!
You don’t need to do anything special for transparent connection. The only thing necessary is working serial interface on a PIC.
Thanks, Oleg. Where could I find information to programm a PIC18F452. I want to test it with a short programm in order to implement PIC–>ZigBee–>PC communication but I have no idea about this PIC, only about PIC16F84A.
This post has some basic code as well as link to a zipped MPLAB project.
i got the same buffering problem when using 2 xbee as end point (with dh/dl set to coordinator) and a coordinator( with dh and dl set to ffff). does anyone have a solution for this situation ?
i already tried to set RO to 0 in all of my xbees, and nothing changed.
Have you tried to run your serial routines with RTS/CTS flow control?
yes, it was my last try and i still have this issue. sometimes when sending about 40 bytes packet from coordinator, i have a 2/3 secs receiving delay in end point device. when sending the same 40 bytes packet from end point to coordinator i got no delay. i think it sould be a problem in addressing, because both of end points devices are using coordinator addr as DH and DL, and coordinator is using FF.
do you think that i can solve this using api mode ?
thanks,
API mode may help since in API mode Xbee is not waiting to fill the buffer – packet gets sent as soon as last byte is received from serial.
Ok I think I solved some xbee problem attaching it to atmel avr32
what do you think?
number 3 is relevant but I wrote it all so other may use the implemantaion
1) there is a problem in atmel mcu using usart and RTS. follow this
Quote:
Do not use the hardware handshaking mode of the USART.
If it is necessary to drive the RTS output high
when the Peripheral DMA receive buffer becomes full,
use the normal mode of the USART.
Configure the Peripheral DMA Controller
to signal an interrupt when the receive buffer is full.
In the interrupt handler code,
write a one to the RTSDIS bit in the USART Control Register (CR).
This will drive the RTS output high.
After the next DMA transfer is started and a receive buffer is available,
write a one to the RTSEN bit in the USART CR so that RTS will be driven low.
remmber to pdca_disable_interrupt_transfer_complete/pdca_enable_interrupt_transfer_complete as needed.
2) in XBEE, mcu RTS-> xbee RTS and mcu CTS-> xbee CTS
3) there is also a problem in XBEE RTS (this was the hard one), after RTS is set there is an over head of 6 byte that will pass (if they where in XBEE RX buffer)
so add “overhead” byte to each transmition
in matlab code
s = serial(port,’BaudRate’,baudrate,’DataBits’,dataBits,’Parity’,parity,’StopBits’,stopbits);
data=[ 'some data'];
overhead=[ 0 1 2 3 4 5];
% open serial
fopen(s);
fwrite(s,[ data overhead 1 2 3 4 overhead 5 6 overhead] ,’uint8′);
fclose(s);
this will do the trick
4) as overhead is needed, the MCU RXCHR register will be full
remmber to empty it before next pdca buffer reloading.
if not the last overhead byte will be read
if (usart_test_hit(TEMP_USART))
junk=(TEMP_USART->rhr & AVR32_USART_RHR_RXCHR_MASK) >> AVR32_USART_RHR_RXCHR_OFFSET;
5) this is my interupt function
/*! \USART brief RTS setting
*/
#if __GNUC__
__attribute__((naked))
#elif __ICCAVR32__
#pragma shadow_registers = full // Naked.
#endif
static void pdca_RTS_int_handler( void)
{
/* This ISR can cause a context switch, so the first statement must be a
call to the portENTER_SWITCHING_ISR() macro. This must be BEFORE any
variable declarations. */
portENTER_SWITCHING_ISR();
// prevent re-entering
pdca_disable_interrupt_transfer_complete( PDCA_CHANNEL_RX);
// close !(RTS), stop transfer
TEMP_USART->cr = AVR32_USART_CR_RTSDIS_MASK;
portEXIT_SWITCHING_ISR();
#endif
}
6) this is my reading code
// clean last overhead
if (usart_test_hit(TEMP_USART))
junk=(ZIGBEE_USART->rhr & AVR32_USART_RHR_RXCHR_MASK) >> AVR32_USART_RHR_RXCHR_OFFSET;
// load channel
pdca_reload_channel( PDCA_CHANNEL_RX, (void *) psParametersShadow, sizeof(xParameters));
// Enable PDCA transfer interrupt when completed
pdca_enable_interrupt_transfer_complete( PDCA_CHANNEL_RX);
//set !(RTS), start transfer
TEMP_USART->cr = AVR32_USART_CR_RTSEN_MASK;
// wait read end
while(!(pdca_get_transfer_status(PDCA_CHANNEL_RX) & PDCA_TRANSFER_COMPLETE));
hope this will help someone
guy’s can u help me in preparing code for transmitting some binary sequence
Hello,
I’m trying to use the module zigbee UZ2400 of UBEC with pic18f26k20. The is a problem with the communication and no signal is going out. Can you help me with this?
Thanks in advance
Holger
Your UZ2400 is connected to the PIC via SPI, correct? Have you been able to verify that SPI works correctly without errors – I’ve seen errors on 26k20 when SPI is clocked too fast.
I’m planning to use xbee for a prototype sensor network with 5-6 nodes (with a pic 18F452). I want to be able to program a wireless routing protocol (simplified AODV or DSR) on the PIC to run on xbee. Is this possible?
Hi everyone. V r doin 8th sem proj usin zigbee series 2. V configured one as co ordinator n other as end device. Wit dh dl and sh sl interchanged. Nw how to find out transmission???
Hi, i’m new to xbee and i just want to know if i can connect analog voltages directly to the analog pins of the transmit xbee (without using the USART pins) and expect the voltages to show up in the receive xbee?
I believe that wouldn’t work.