Full-Duplex Serial

Full-duplex serial communication allows both outgoing and incoming messages to be sent and received at the same time.  Examples of devices where full-duplex serial communication can be useful include XBee radios, other microcontrollers, and even the SimpleIDE Terminal.  Although the Propeller uses half-duplex serial communication with the SimpleIDE Terminal by default, that can be disabled and replaced with full-duplex serial communication.  This example demonstrates how to use full-duplex serial communication with XBee radios, and also with the SimpleIDE Terminal.



Before you start, let’s make sure that the Propeller Chip is not sending an output signal to the XBee module’s DO output pin.  If one is trying to send a high signal and the other is trying to send a low signal at opposite ends of a wire, it could damage either the Propeller I/O pin or the XBee.

  • Use SimpleIDE’s Load EEPROM & Run button to load this program into the Propeller Chip’s EEPROM:
int main()

Now, we can safely build the circuit, without having to worry about some old program in the Propeller chip’s EEPROM sending signals to the XBee module’s DO pin. 


This activity uses two XBee radios and an XBee to USB adapter.

  • Set the Activity Board’s PWR switch to 0.
  • Use jumper wires to connect XBee DO to Propeller P9 and XBee DI to Propeller P8.

  • Make sure the USB cable is UNPLUGGED from the XBee/USB adapter.
  • Socket the XBee as shown in the adapter’s documentation.
  • Connect the XBee adapter to your computer with a USB A/Mini-B cable.
  • Socket the second XBee in the Propeller Activity Board as shown in the picture below.
  • Connect your Activity Board to the PC with another USB A/Mini-B cable.

If you haven't already installed the latest USB driver, SimpleIDE, or Learn folder, go to Propeller C - Set up SimpleIDE and Propeller C - Start Simple.


Test Code

The test code is a loopback connection, meaning it sends back what it receives.  After setting up the SimpleIDE terminal to talk with the XBee adapter, you’ll be able to type characters in to send them to the XBee you connected to the adapter.  That XBee will broadcast those characters on its radio frequency.  The XBee on the Propeller will receive those characters from the airwaves, and send them to the Propeller as serial messages.  The Propeller program then sends “You typed: + the character” back to the Activity Board’s XBee, which broadcasts that message to the XBee connected to the USB adapter.  The Xbee on the adapter will receive that broadcast and send it to the PC, which then displays it on the SimpleIDE terminal.

  • Open XBee UART Loopback.side from ...Documents\SimpleIDE\Learn\Examples\Protocols
  • The COM port dropdown is at the top-right of the button bar.  Click it to see your port list, and set it to your Activity Board’s port.  (If you don’t know the COM port number, check the list once, then unplug your Activity Board and check again.  Whichever port disappeared must be your Activity Board.  Plug back in and select the port that should have reappeared.) 
  • Click the Load EEPROM & Run button
  • Next, set your COM port dropdown to your XBee/USB adapter’s port.  
  • Click the Program menu and select Open Terminal.
  • Change the Baud rate dropdown from 115200 to 9600.
  • Press/release the RST button on the Activity Board.
  • Start typing characters and verify that they are sent back prepended with “You typed: h“, “You typed :e”, etc.

If you want to modify and re-run your program, follow these steps:

  • Close the SimpleIDE Terminal.
  • Set your SimpleIDE COM dropdown back to the Activity Board.
  • Use the Load EEPROM & Run button to load the modified program into EEPROM.
  • Set the COM back to the XBee adapter port.
  • Click Program -> Open Terminal.
  • IMPORTANT:  If you have SimpleIDE 0.9.44 or older, you will have to manually set the baud rate from 9600 to a different value, and then back to 9600.
  • Press and release the Activity Board’s RST button, and you should see the Click this terminal, and type on keyboard… prompt.


How it Works

The library for full-duplex serial communication is called fdserial, and #include "fdserial.h" makes its functions available to the application.  Then, fdserial *xbee sets up a full-duplex serial device identifier.  In the main function, xbee = fdserial_open(9, 8, 0, 9600) configures the serial connection to receive messages on the P9 I/O pin, send with the P8 pin, set the mode to 0, and the baud rate to 9600 bits per second.  The fdserial_open function also returns a memory address to the xbee pointer variable.  Now that xbee has that pointer, it can be used as a device identifier with any fdserial function with fdserial *term parameters as well as any simpletext function that has a text_t *device parameters.  Keep in mind that there could be more than one full-duplex serial device connected to the Propeller, so we use the device identifier to tell functions like dprint and fdserial_rxChar which serial device they are communicating with.  That’s why we see xbee in function calls like dprint(xbee, "Click this terminal, \n"), and  c = fdserial_rxChar(xbee).

  XBee UART Loopback.c

#include "simpletools.h"
#include "fdserial.h"

fdserial *xbee;

int main()
  xbee = fdserial_open(9, 8, 0, 9600);

  writeChar(xbee, CLS);
  dprint(xbee, "Click this terminal, \n");
  dprint(xbee, "and type on keyboard...\n\n");

  char c;
    c = fdserial_rxChar(xbee);
    if(c != -1)
      dprint(xbee, "You typed: %c\n", c);

As with half-duplex serial, full-duplex serial identifiers can be used with dprint, which gives you formatting options that are much like print with the SimpleIDE Terminal.  For example, dprint(xbee, "Click this terminal, \n") sends the message followed by the \n newline character.  You can also use %d for sending the text representation of integer values, and even %f for floating point representations.

The fdserial library runs code in another cog that accumulates bytes it receives into a buffer array.  If there’s nothing in it, the fdserial_rxReady function returns -1.  If there are one or more characters in the buffer, fdserial_rxReady returns the oldest character.  After repeated calls to fetch all the characters, the function returns -1 again to indicate that there’s nothing in the buffer. 

Inside the while(1) loop, c = fdserial_rxChar(xbee) either gets -1, or the oldest character in the buffer.  When it gets -1, the code inside the if(c != -1) block gets skipped because c is equal to -1.  When c contains a character, it’s not equal to -1, and in that case, the dprint(xbee, "You typed: %c\n", c) code does get executed.


Did You Know?

The XBee manual is available from digi.com.

  • Go to www.digi.com.
  • Click Support -> Documentation
  • Scroll down the list and select Xbee / XBee-PRO 802.15.4 Modules.
  • Click Product Manual: XBee / XBee-PRO 802.15.4 RF Modules.

The serial *xbee declaration is global since it is made before any functions.  This is useful because it allows any function in the application to pass Xbee device nicknames in calls to simpletext functions like dprint and putChar.

Any simpletext function with a text_t *device parameter can be used with full-duplex serial peripherals like the XBee.  For a complete list of simpletext functions, see …Documents\SimpleIDE\Learn\Simple Libraries\Text Devices\libsimpletext\Documentation simpletext Library.html.

The fdserial_open function is part of the fdserial library.  For a full list of (half-duplex) serial functions, see Documents\SimpleIDE\Learn\Simple Libraries\Text Devices\libfdserial\Documentation fdserial Library.html.

The fdserial library’s receive buffer is considered FIFO.  That’s an abbreviation of first-in-first-out, meaning the oldest byte that was received is the first one to be returned by fdserial_rxChar(xbee).

The fdserial_open function’s mode parameters expects a value with binary 1s and 0s.  The mode 0 in xbee = fdserial_open(9, 8, 0, 9600) is the most common one, but other variations include:

  • 0b0000  // Same as 0, true serial communication, nothing special
  • 0b0001  // Treat incoming signal as inverted (1 is 0, and 0 is 1)
  • 0b0010  // Invert signal for outgoing messages
  • 0b0100  // Ignores echoes created by a circuit that copies the signal the propeller transmits to its receive line

You can even set the multiple mode bits.  For example, you could pass a mode of 0b0011 to invert both the transmit and receive signals.


Try This

You might expect to see a function like xbcmd in the code below in an XBee library.  This function simplifies putting the XBee into command mode for configuration.  In its default mode, the XBee radio broadcasts the serial data it receives for other xbees.  When it is switched to command mode, it treats the serial data it receives as configuration data.  You can use this mode to set its network address, baud rate, and many other features.

In the XBee Command Mode example (below), the main function sends +++ to the XBee module to put it into command mode.  So long as the XBee doesn’t receive anything else from the Propeller for a couple seconds, it switches to command mode and replies with OK.  If the Propeller gets the OK message, it then sends ATBD to find out what baud rate the XBee is set to.  According to the XBee manual from Digi, since it’s at 9600, the XBee will reply with 3.  Last, but not least, it sends XBCN to exit command mode, and the XBee replies with OK.  After exiting command mode, the XBee returns to its normal mode of radio broadcasting the serial messages it receives.

  • Set the COM port dropdown to your Activity Board.
  • Open XBee UART Command Mode Example.side from ...Documents\SimpleIDE\Learn\Examples\Protocols. Click Run with Terminal.
  • Verify that the conversation with your XBee matches the one in the terminal capture above.
  • There is no such thing as an ATBc command.  What do you think will happen if you change ATBD to ATBc in xbcmd("ATBD\r", response, 10, 20).

Does the reply make sense to you?  Try adding this code right below the xbcmd you just modified: if(!strcmp(“ERROR”, response)) print(“The XBee didn’t understand!”);


Your Turn

To drive home the point that you can have multiple full-duplex serial connections, let’s update the Try This example so that it uses fdserial to communicate with SimpleIDE terminal, and another instance of fdserial running in another cog to communicate with the XBee.  Here is a start on modifications to the code from Try This:

We created another fdserial identifier named term, which is short for terminal.  The simpleterm_close function call shuts down the default half-duplex communication, which will cause print calls to stop working.  But that’s fine because we can use full-duplex serial. 

Note the last command in this example was modified from print(“cmd = +++”) to dprint(term, “cmd = +++”).  If you do that with all the print statements, it’ll work the same as before, but will be running a full-duplex serial connection (instead of half-duplex serial) under the hood.