Please note, this page is currently being written. Please check back again soon for updates.
Getting Started
I've had quite a few people getting in contact asking for support with the initial setup and configuration of libxbee.
As a result, I felt that it would be worthwhile writing this guide.
libxbee is very straight forward, once you understand the principals involved.
Contents
Which OS are you Running?
Linux
Great! Me too.
For development I primarily use Linux. Because of this libxbee has been built under Linux from the beginning.
It is a pretty safe bet that everything will work for you too.
You can either download binary packages, or compile it yourself from the master branch of the git repository.
Windows
Support for Windows was one of the first large requests I had.
Unfortunately, because of the Microsoft C Compiler, I have had to make a few changes to the library.
I am not particularly fond of these changes, and as a result the Windows code remains in it's own branch.
You can either download the binary package, or compile it yourself from the win32 branch of the git repository.
So far I have not had to build a 64-bit DLL, and as a result I don't know how to.
FreeBSD
FreeBSD was the next supported OS.
It required some minor changes, and like Linux, all source can be found in the master branch of the git repository.
There is currently no binary release for FreeBSD.
Mac OSX
Over the past few months, requests for support with Mac OSX have been increasing.
I don't own a Mac, and rarely have the opportunity to get hands-on with one.
As a result, it would be fantastic if anyone who has successfully built libxbee for OSX could let me know what tweaks were required (if any).
I have made a start on an 'osx' branch that can be accessed in my personal repository, here.
Please feel free to continue development, currently the Todo list is as follows:
- Implement
sem_timedwait() replacement (OSX doesn't have this function)
- Resolve issues with the
time_t type on compile of sample apps
- Test, and there will probably be more!
Which Modules Should I Use? (S1 vs. S2)
The first thing to do is select which modules you want to use.
People often seem to get the wrong impression, that "XBee Series 2" modules are newer and better.
Though the newer part is correct, the better part really depends on how you will be using the XBee network.
| Series 1 |
- Physically small network (no meshing)
- Frequent broadcasting
- Extremely simple configuration
|
| Series 2 |
- Physically large network (meshed network)
- Infrequent (less than 1 per second) or no broadcasting
|
I use XBee Series 1 modules a lot, because they do the job I need.
I don't need meshing, or the hassle of complex network configuration.
Which libxbee Mode Should I Use?
Unfortunately Digi decided on some spectacularly silly names for the Series 2 XBee modules.
Hopefully this will help to clear up any issues.
Thank you SparkFun for the images used below.
| libxbee mode
| XBee Series
| Image
| Description
|
xbee1 |
1 or 802.15.4 |
Image
|
This is easy.
The newer modules say 'S1' on them, older modules say 'Series 1'.
They come in a standard, and PRO variety.
They come with the option of a 'whip' or PCB antenna, or an RPSMA or u.FL connector.
|
xbee2 |
2, 2.5, ZNet or DigiMesh |
Image
|
These are the first iteration of 'Series 2', and have now been made obsolete.
As far as I know they are no longer manufactured.
Some modules say 'Series 2' on them, it's possible that a newer release has been made that has 'S2' written on them.
They come in a standard, and PRO variety.
They can be found with at least the option of a 'whip' or PCB antenna, or a u.FL connector.
To confuse things further, these can be loaded with the newer ZigBee firmware (libxbee mode xbeeZB).
|
xbeeZB |
2 or ZigBee |
Image
|
These came more recently.
They have 'S2' written on them.
They come in a standard, and PRO variety.
They come with the option of a 'whip' or PCB antenna, or an RPSMA or u.FL connector.
NOTE: The core functionality of this mode is now avaliable in the master branch!
NOTE: Development is ongoing for ZDO support.
The ZDO code can be found on the xbeeZB branch.
|
xbee5 |
5 |
Image
|
This is easy.
I don't own any, but wouldn't be surprised if they have 'S5' written on them somewhere.
As far as I know, they are only available as PRO modules.
They have a chunky RPSMA connector, and an Atmel processor mounted on the top.
|
Running Some Samples
Samples can be found in the sample directory in the repository (here).
Windows users, please use the samples found in the sample.win32 directory (here).
The samples stored in the generic directory give an overview of how to use the mode-independent functionality.
This includes the xbee_getModes() function and how to retrieve other library information such as build time and commit ID.
The samples are ordered, and should be attempted in the given order.
For example 1.simple_at should be run first.
If this is successful, then move onto 2.remote_at, etc...
The reasoning behind this is that the 1.simple_at sample simply communicates between libxbee and the connected XBee module.
It does not require any network configuration in order for it to work.
Please ensure that you modify the xbee_setup() call and any addresses to reflect your system before running the samples.
The Samples
1. Simple AT
This sample will request the NI parameter from the connected XBee module, and then display it on screen.
If an error occurs, it will be reported.
2. Remote AT
This sample will request the NI parameter from a remote XBee module, and display it on screen.
Please ensure that you set the correct remote address, otherwise the request will fail.
You should modify the section of code like below, which in this case represents the 64-bit address 0x0013A200 0x40081826:
address.addr64[0] = 0x00;
address.addr64[1] = 0x13;
address.addr64[2] = 0xA2;
address.addr64[3] = 0x00;
address.addr64[4] = 0x40;
address.addr64[5] = 0x08;
address.addr64[6] = 0x18;
address.addr64[7] = 0x26;
3. Simple Data
This sample will setup a listening connection, and attach a callback.
When it receives data from the configured remote XBee, the callback is triggered.
The callback will print the received data, and respond with the text 'Hello\r\n'.
The Others
At this point, the other sample applications should give you a more in-depth view of how to use libxbee.
Calling xbee_setup()
Now that you have selected your XBee module, and matched it with the correct libxbee mode, you need to call xbee_setup().
This will configure, and start an instance of libxbee.
The arguments are simple, the prototypes are shown below:
xbee_err xbee_setup(struct xbee **retXbee, const char *mode, ...);
xbee_err xbee_vsetup(struct xbee **retXbee, const char *mode, va_list ap);
Using
xbee_vsetup() is similar to using
vprintf().
If you are unaware of this function, it's best to steer clear for now.
The first parameter (retXbee) is simply the address of a pointer that will be used to store the new libxbee instance's handle.
The second parameter (mode) allows you to select which mode libxbee should start in.
For a list of modes, either run the generic/1.get_modes sample, or see the xbee_setup(3) man page.
There is a table above that should help to identify which mode you should use.
The remaining parameters are mode-dependant. Please see the listing below:
xbee_err xbee_setup(struct xbee **retXbee, const char *mode = "xbee1", char *device, int baudrate);
xbee_err xbee_setup(struct xbee **retXbee, const char *mode = "xbee2", char *device, int baudrate);
xbee_err xbee_setup(struct xbee **retXbee, const char *mode = "xbeeZB", char *device, int baudrate);
xbee_err xbee_setup(struct xbee **retXbee, const char *mode = "xbee5", char *device, int baudrate);
xbee_err xbee_setup(struct xbee **retXbee, const char *mode = "net", char *hostname, int port);
Example
struct xbee *handle;
xbee_err retValue;
retValue = xbee_setup(&handle, "xbee1", "/dev/ttyUSB0", 57600);
if (retValue != XBEE_ENONE) {
fprintf(stderr
, "libxbee: xbee_setup() returned %d (%s)\n",
retValue,
xbee_errorToStr(retValue));
/* failure */
}
/* success! */
Note for Windows Users
If you are using windows, and xbee_setup() returns XBEE_EIO then it is quite possible that you need to alter the port notation.
If you are trying to use COM25 for example, please try the following:
retValue = xbee_setup(&handle, "xbee1", "\\\\.\\COM25", 57600);
Creating Connections
Now that you have a libxbee instance running, you are ready to create a connection.
The connection types available vary depending on the mode you are running in.
Below is a table of available modes with their connection types.
| xbee1
| xbee2
| xbeeZB
| xbee5
|
- Modem Status
- Transmit Status
- Local AT
- Remote AT
- 16-bit Data
- 64-bit Data
- 16-bit I/O
- 64-bit I/O
|
- Modem Status
- Transmit Status
- Local AT
- Remote AT
- Data
- Data (explicit)
- I/O
- Sensor
- Identify
|
- Modem Status
- Transmit Status
- Local AT
- Remote AT
- Data
- Data (explicit)
- I/O
- Identify
- Sensor
- OTA Update Status
|
- Modem Status
- Transmit Status
- Local AT
- Remote AT
- Data
- Data (explicit)
- Identify
|
To create a connection you need to call xbee_conNew(), the prototype for this is shown below.
xbee_err xbee_conNew(struct xbee *xbee, struct xbee_con **retCon, const char *type,
struct xbee_conAddress *address);
The first parameter (xbee) should be the handle you retrieved by calling xbee_setup().
The second parameter (retCon) is the address of a pointer that will be used to store the new connection's handle.
The third parameter (type) is a C string describing which connection type you wish to use.
You can use the table above to identify the types available for your chosen mode, or you can use xbee_conGetTypes().
The final parameter (address) points to a structure that contains the addressing information for your new connection.
Some connection types don't require this, in which case you can provide NULL.
Once the call to xbee_conNew() is complete, you may modify the contents of the provided struct without affecting the connection.
Example
struct xbee *handle; /* <-- initialised in the previous example */
struct xbee_con *conHnd;
struct xbee_conAddress address;
xbee_err retValue;
memset(&address
, 0, sizeof(address
));
address.addr64_enabled = 1;
/* address node at 0x0013A200 0x40081826 */
address.addr64[0] = 0x00;
address.addr64[1] = 0x13;
address.addr64[2] = 0xA2;
address.addr64[3] = 0x00;
address.addr64[4] = 0x40;
address.addr64[5] = 0x08;
address.addr64[6] = 0x18;
address.addr64[7] = 0x26;
retValue = xbee_conNew(handle, &conHnd, "64-bit Data", &address);
if (retValue != XBEE_ENONE) {
fprintf(stderr
, "libxbee: xbee_conNew() returned %d (%s)\n",
retValue,
xbee_errorToStr(retValue));
/* failure */
}
/* success! */
Using Connections
Now that you have a connection, you can pass data through it.
Some connection types are only one way, for example the I/O connections are only able to receive data, and not transmit.
To transmit data, use the xbee_conTx() function (or a variant).
To receive data, use the xbee_conRx() function.
The prototypes for these functions are shown below:
xbee_err xbee_conTx(struct xbee_con *con, unsigned char *retVal, const char *format, ...);
xbee_err xbee_convTx(struct xbee_con *con, unsigned char *retVal, const char *format, va_list args);
xbee_err xbee_connTx(struct xbee_con *con, unsigned char *retVal, const unsigned char *buf, int len);
xbee_err xbee_conRx(struct xbee_con *con, struct xbee_pkt **retPkt, int *remainingPackets);
xbee_err xbee_conRxWait(struct xbee_con *con, struct xbee_pkt **retPkt, int *remainingPackets);
When calling these functions, the parameters are as follows.
The first parameter for all (con) should be the handle that you retrieved from xbee_conNew().
Transmission
For the Tx functions, the retVal parameter allows you to retrieve the value returned by the XBee.
This parameter may be given as NULL, in which case you will obviously not be able to inspect the value returned by the XBee.
If a Tx function returns XBEE_ETX, then retVal will have been populated.
It may contain XBEE_ETIMEOUT, which may be mistaken as any of the following when printed: 0xEF (0x%02X - hex), -17 (%d - signed) or 239 (%u - unsigned).
In this case a timeout occurred within libxbee while waiting for the XBee module to provide an update.
Alternatively, retVal will contain the byte provided by the XBee in a 'Tx Status' message.
For more information on this, please see the relevant XBee documentation.
The remaining parameters for the Tx functions are fairly self explanatory.
The xbee_conTx() parameters are identical to that of printf().
Likewise, the xbee_convTx() parameters are identical to that of vprintf().
Calling xbee_connTx() allows you to specify a buffer and it's length instead, which is more friendly to use when transmitting binary data.
Reception
When receiving data, you have the option of two functions.
The Advanced Connections section covers the configuration and use of callbacks.
The Rx functions are called in an identical way, and have virtually identical functionality - the difference being that xbee_conRx() returns immediately if there are no packets available, whereas xbee_conRxWait() will wait for upto one second for a packet to become available (using a busy loop) before returning.
The retPkt parameter allows you to retrieve a packet. This parameter is compulsory and must not be NULL.
The remainingPackets parameter allows you to find out how many packets are queued and ready to be retrieved for the given connection.
This parameter is optional, and can be given as NULL if you do not want this information.
After retrieving a packet it is very important that you free the packet once you are finished with it.
To do this, simply call xbee_pktFree(), passing the packet's pointer as the only parameter.
Example
struct xbee *handle; /* <-- initialised in a previous example */
struct xbee_con *conHnd; /* <-- initialised in the previous example */
xbee_err retValue;
unsigned char xbeeRet;
int i;
struct xbee_pkt *pktHnd;
/* say hello */
if ((retValue = xbee_conTx(conHnd, &xbeeRet, "Hello %s!", "world")) != XBEE_ENONE) {
if (retValue != XBEE_ETX) {
fprintf(stderr
, "xbee_conTx() returned %d (%s)\n",
retValue,
xbee_errorToStr(retValue));
/* failure */
}
if (xbeeRet == XBEE_ETIMEOUT) {
fprintf(stderr
, "xbee_conTx(): The XBee failed to respond in a timely fashion...\n");
}
fprintf(stderr
, "xbee_conTx(): The XBee returned 0x%02X after attempting a transmission...\n");
}
/* wait 10 seconds for a response */
for (i = 10; i > 0; i--) {
if ((retValue = xbee_conRxWait(conHnd, &pktHnd, NULL)) != XBEE_ENONE) {
if (retValue == XBEE_ENOTEXISTS) continue;
fprintf(stderr
, "xbee_conRxWait() returned %d (%s)\n",
retValue,
xbee_errorToStr(retValue));
/* failure */
}
if (!pktHnd) continue;
break;
}
if (i == 0) {
fprintf(stderr
, "xbee_conRxWait(): No packet recieved before timeout\n");
/* failure */
}
/* print out the packet */
printf("Packet Length: %d\n", pktHnd
->dataLen
);
for (i = 0; i < pktHnd->dataLen; i++) {
printf(" %3d: 0x%02X %c\n", i
, pktHnd
->data
[i
],
((pktHnd->data[i] >= ' ' && pktHnd->data[i] <= '~') ? pktHnd->data[i] : '.');
}
/* free the packet, and make it NULL */
xbee_pktFree(pktHnd);
pktHnd = NULL;
/* complete! */
Advanced Connections
Now that you know the basics of sending and receiving data, we can look at the advanced functionality surrounding connections.
Data Associaion
Each connection may have data (a pointer cast to void*) associated with it.
This allows you to store information about the connection, or even maintain the state of a remote device, without keeping your own look-up table.
The data pointer may be accessed by using the following functions:
xbee_err xbee_conDataSet(struct xbee_con *con, void *newData, void **oldData);
xbee_err xbee_conDataGet(struct xbee_con *con, void **curData);
When calling xbee_conDataSet(), the oldData parameter is optional, and allows you to swap two pieces of data.
When calling xbee_conDataGet(), you must provide the address of your pointer.
Example
struct my_data {
int active;
int some_flag;
};
struct xbee *handle; /* <-- initialised in a previous example */
struct xbee_con *conHnd; /* <-- initialised in a previous example */
xbee_err retValue;
struct my_data *md;
struct my_data *md2;
/* get some storage */
if ((md
= malloc(sizeof(*md
))) == NULL
) {
fprintf(stderr
, "malloc() returned NULL\n");
}
printf("malloc'ed md = %p\n", md
);
/* assign the data to the connection */
if ((retValue = xbee_conDataSet(conHnd, md, NULL)) != XBEE_ENONE) {
fprintf(stderr
, "xbee_conDataSet() returned %d (%s)\n",
retValue,
xbee_errorToStr(retValue));
/* failure */
}
/* forget where the data is located */
md = NULL;
printf("forgotten md = %p\n", md
);
/* get the data back from the connection */
if ((retValue = xbee_conDataGet(conHnd, &md)) != XBEE_ENONE) {
fprintf(stderr
, "xbee_conDataGet() returned %d (%s)\n",
retValue,
xbee_errorToStr(retValue));
/* failure */
}
printf("retrieved md = %p\n", md
);
/* forget where the data is located again */
md = NULL;
printf("forgotten 2 md = %p\n", md
);
/* get some more storage */
if ((md2
= malloc(sizeof(*md2
))) == NULL
) {
fprintf(stderr
, "malloc() returned NULL\n");
}
printf("malloc'ed 2 md2 = %p\n", md2
);
/* swap md2 with the currently assigned data (md) */
if ((retValue = xbee_conDataSet(conHnd, md2, &md)) != XBEE_ENONE) {
fprintf(stderr
, "xbee_conDataSet() returned %d (%s)\n",
retValue,
xbee_errorToStr(retValue));
/* failure */
}
printf("swapped md = %p, md2 = %p\n", md
, md2
);
Expected Output
malloc'ed md = 0x8c6a008
forgotten md = (nil)
retrieved md = 0x8c6a008
forgotten 2 md = (nil)
malloc'ed 2 md2 = 0x8c6a00f
swapped md = 0x8c6a008, md2 = 0x8c6a00f
Callback Functions
As you saw earlier in the "Using Connections" section, receiving data through connections requires you to constantly monitor and check for data.
A far more efficient and effective method is to use callback functions.
The majority of the sample code provided with libxbee uses callback functions, and I can only imagine few occasions where it would be beneficial not to.
Callbacks are configured in an almost identical way to associated data.
The difference being that instead of data, you provide a function pointer, and you use different functions.
xbee_err xbee_conCallbackSet(struct xbee_con *con, xbee_t_conCallback newCallback,
xbee_t_conCallback *oldCallback);
xbee_err xbee_conCallbackGet(struct xbee_con *con, xbee_t_conCallback *curCallback);
If the configured function pointer is non-NULL, then callbacks will be considered enabled, and you will NOT be able to retrieve data using the xbee_conRx() functions.
Instead, the configured function will be called once for each packet received.
To disable callbacks and return to using xbee_conRx(), simply provide a NULL value.
It is vitally important that you provide a valid function pointer, or NULL.
Your function must match the following prototype:
void xbee_t_conCallback(struct xbee *xbee, struct xbee_con *con, struct xbee_pkt **pkt, void **data);
The reason that the
pkt and
data parameters are pointers-to-pointers (
**pkt), is so that you may modify the values.
libxbee will automatically call xbee_pktFree() after your callback has completed.
If for any reason you wish to keep the packet in memory (to reference it after the callback has completed), you simply set (*pkt) = NULL.
If you set *pkt to any value other than what was provided, or NULL, libxbee will log an error, but will not do anything with the pointer.
Similarly, you may access and set the connection's associated data by modifying the *data value.
This removes the need to call xbee_conDataSet() or xbee_conDataGet() from within the callback context.
Example
void myCallback(struct xbee *xbee, struct xbee_con *con, struct xbee_pkt **pkt, void **data) {
struct my_data *md;
int i;
printf(" dataLen: %d\n", (*pkt
)->dataLen
);
for (i = 0; i < (*pkt)->dataLen; i++) {
printf(" %3d: 0x%02X %c\n", i
, (*pkt
)->data
[i
],
(((*pkt)->data[i] >= ' ' && (*pkt)->data[i] <= '~') ? (*pkt)->data[i] : '.');
}
md = (struct my_data *)(*data);
if (md != NULL) {
printf(" active: %d\n", md
->active
);
printf(" some_flag: %d\n", md
->some_flag
);
}
}
struct xbee *handle; /* <-- initialised in a previous example */
struct xbee_con *conHnd; /* <-- initialised in a previous example */
xbee_err retValue;
if ((retValue = xbee_conCallbackSet(conHnd, myCallback, NULL)) != XBEE_ENONE) {
fprintf(stderr
, "xbee_conDataSet() returned %d (%s)\n",
retValue,
xbee_errorToStr(retValue));
}
/* from now on:
- you will not be able to call xbee_conRx()
- myCallback() will be executed for you on reception of data */
Tips
It is possible to disable callbacks by providing NULL instead of a function pointer.
It is also possible to temporarily 'redirect' the callback handler by retrieving the current handler into a void* variable, and replacing it after you have done your mischief, but be very careful when doing this (use mutexes etc...).
Sleeping
...