Labels

Saturday, July 27, 2013

Getting RSSI from Xbee Series 2

This has been one of the most obnoxious endeavors I've taken on so far with Xbees. For those of you who might be familiar with the API mode of the Xbee series 1, each packet includes the RSSI (Received Signal Strength Indicator) value in it. In series 2 that is not the case. I'm not sure what the reasoning is behind excluding that byte from the packet, but I suspect that it has something to do with only reflecting the strength of the last hop, and in a multihop network that may be considered "useless", but whatever.

I scoured the internet looking for other solutions. Most sites I found mentioned that you can turn on the PWM (Pulse Width Modulation) output on pin6 to reflect the RSSI value. This was mostly designed with the intention of hooking up a cute little LED that glows brighter according to the signal strength. When the RSSI PWM is enabled, a signal is output on pin 6 which has a duty cycle that ranges from 24% to 100%.

According to the documentation:

 Zero percent means PWM output is inactive. One to 24% percent means the received RF signal is at or below the published sensitivity level of the module. The following table shows levels above sensitivity and PWM values.

dB above Sensitivity - Dutycycle 
10   -  41%
20   -  58%
30   -  75%


The total period of the PWM output is 64 μs. Because there are 445 steps in the PWM output, the
minimum step size is 144 ns.

A non-zero value defines the time that the PWM output will be active with the RSSI value of the
last received RF packet. After the set time when no RF packets are received, the PWM output will
be set low (0 percent PWM) until another RF packet is received. The PWM output will also be set
low at power-up until the first RF packet is received. A parameter value of 0xFF permanently
enables the PWM output and it will always reflect the value of the last received RF packet.

The next step was to find a legitimate conversion rate from this stupid PWM output to dB. I stumbled upon the following excerpt from some random forum:
DB parameter is used to read the received signal strength (in dBm) of the last RF packet received. Reported values are accurate between -40 dBm and the RF module's receiver sensitivity.

Parameter Range [read-only]: 0x17-0x5C (XBee), 0x24-0x64 (XBee-PRO)
Absolute values are reported. For example: 0x58 = -88 dBm (decimal). If no packets have been received (since last reset, power cycle or sleep event), “0” will be reported.

So I suppose I could map the sensitivity range to 24% to 100% duty cycle, but I came across a much better solution, the DB AT command!

Seems like a very obvious solution, but what I was reading at most websites was that it takes at least 2 seconds to retrieve the value. To enter "command mode" on most Xbees, you must wait 2 seconds, send "+++", the command to enter command mode, then send "ATDB\r" and wait for the response. I was used to working with the Xbee S1 modules in API mode, which issues AT commands the same way, by send the "+++" code and junk, so I assumed S2 is the same way and immediately dismissed that as a viable option. Who has 2 seconds to sit around and do nothing but miss packets!?

Turns out, if you are using Xbee series 2 API firmware, AT commands are not accessed by sending the "+++" command. You send an API frame that is structured for AT commands. This means you don't have to wait 2 seconds to receive the dB! However, this does mean I needed to seriously modify my code, because now we are using another type of API packet! The following function was written:

void AT_Command(char frameid, char command1, char command2, char *options, int len)
{
char buff[5]; //temporary buffer for transmitting
int count;
buff[0] = 0x08; // API ID for AT Commands
buff[1] = frameid; // Frame ID; set to 0 for no response
buff[2] = command1;
buff[3] = command2;
for(count = 1; count <= len; count++)
buff[3 + count] = options;
send_Msg(buff, 4 + len);
}

In this function you pass the frame ID, the two characters for the AT command (e.g., 'D' and 'B'),  and any options that may go along with that in the form of an array, and the length of that array. Those values are then sent to our send_Msg() function we discussed last post.

We also had to make a few modifications to receive the packets from AT commands if they return information such as the DB command. To hold this, our data structure for Xbee frames was modified:

typedef struct{
int len;
char api_identifier;
long DH;
long DL;
int source_addr_16bit;
char options;
char frame_id;
char AT_com[2];
char status;
char data[20]; // also stores the "value" for at command response
char checksum;
} RxPacket;

We've added the frame_id, AT_com[], status, and options for receiving AT returned frames. Inside the switch statement of our receive_Msg() function we've added the following case:

case 0x88: // AT Command
for(count = 1; count < rx_data->len; count++)
{
while ((UCSR0A & (1 << RXC0)) == 0) {}; // Do nothing until data have been received and is ready to be read from UDR
if(count == 1)
rx_data->frame_id = UDR0;
else if(count == 2)
rx_data->AT_com[0] = UDR0;
else if(count == 3)
rx_data->AT_com[1] = UDR0;
else if(count == 4)
rx_data->status = UDR0;
else
rx_data->data[count - 5] = UDR0;
}
while ((UCSR0A & (1 << RXC0)) == 0) {}; // Do nothing until data have been received and is ready to be read from UDR
rx_data->checksum = UDR0; //store checksum
break;

Now we can receive AT Commands! Now to look at the function I've written to specifically request the dB:

char getRSSI(void)
{
RxPacket pkt;

AT_Command(0x01, 'D', 'B', 0, 0); // Send DB AT Command
cli();

int count, len;
char temp, checksum;

while ((UCSR0A & (1 << RXC0)) == 0) {}; // Do nothing until data have been received and is ready to be read from UDR
temp = UDR0;

while ((UCSR0A & (1 << RXC0)) == 0) {}; // Do nothing until data have been received and is ready to be read from UDR
temp = UDR0; //next incoming byte is the MSB of the data size
while ((UCSR0A & (1 << RXC0)) == 0) {}; // Do nothing until data have been received and is ready to be read from UDR

pkt.len = (temp << 8) | UDR0; //merge LSB and MSB to obtain data length
while ((UCSR0A & (1 << RXC0)) == 0) {}; // Do nothing until data have been received and is ready to be read from UDR
pkt.api_identifier = UDR0;

for(count = 1; count < pkt.len; count++)
{
while ((UCSR0A & (1 << RXC0)) == 0) {}; // Do nothing until data have been received and is ready to be read from UDR
if(count == 1)
pkt.frame_id = UDR0;
else if(count == 2)
pkt.AT_com[0] = UDR0;
else if(count == 3)
pkt.AT_com[1] = UDR0;
else if(count == 4)
pkt.status = UDR0;
else
pkt.data[count - 5] = UDR0;
}
while ((UCSR0A & (1 << RXC0)) == 0) {}; // Do nothing until data have been received and is ready to be read from UDR
pkt.checksum = UDR0; //store checksum

//printf("RSSI is %d\n", pkt.data[0]);
sei();

return pkt.data[0]; // Return RSSI value
}

I didn't actually end up using the receive_Msg() function for acquiring the dB, but the functionality is still there in case it's needed later. This getRSSI() function simply returns the dB value, just remember that the value is actually negative because it is the dB of the signal loss!



Wednesday, July 17, 2013

Message Interface for Xbee Series 2

The first step to building a sensor network is establishing the wireless interface for communication. Xbees can operate in two different modes: Transparent and API. Transparency mode causes the modules to act as a "cable replacement", so each byte sent to an Xbee is received by all Xbees (unless the destination address has been modified in the AT parameters, but by default messages are broadcast to all modules). In API mode, we construct custom packets ourselves, in which we can modify various options, specify a destination address, request acknowledgement, or limit the number of hops a message can take in a multihop network.

If you aren't familiar with Xbee API mode, take a look at Chapter 6 (page 35)  in the documentation here:
ftp://ftp1.digi.com/support/documentation/90000866_A.pdf

The following code is snippets from what will soon be the Xbee avr-gcc library. I've tested the functions used here, and as far as I can tell, everything works just fine. A side note: all USART communications are currently set for 9600 baud, which I plan to increase later, but it's a convenient rate for developing.

Currently in my header file I have a structure that I store all the incoming packet data into:

typedef struct{
int len;
char api_identifier;
long DH;
long DL;
int source_addr_16bit;
char options;
char data[20];
char checksum;
} RxPacket;

I am only using the Zigbee Tx request (API Identifier: 0x10) packet for sending information currently, so this structure has everything I need to store the Zigbee Rx packet (API Identifier: 0x90) that is received.

The following code is used to send an array constructed in the form of an API frame:

/* Routine to send a byte through USART0
 *
 * This routine polls the UCSR0A register until it indicates it is ready
 * for a new byte to be transmitted, then loads the new byte into the UDR0
 * register for transmission.
 */
void USART_vSendByte(char Data)
{
// Wait if a byte is being transmitted
while ((UCSR0A & (1 << UDRE0)) == 0) {}; // Do nothing until UDR is ready for more data to be written to it 
// Transmit data
UDR0 = Data;
}

void send_Msg(char *data, int len)
{
cli();  //disable interrupts
//Generate checksum
char checksum;
int counter = 0, sum = 0;
for(counter = 0; counter <= len - 1; counter++)
sum += data[counter];
//Checksum is calculated by adding the data values together, and subtracting the 
//last 8 bits from 0xFF.
checksum = 0xFF - (sum & 0x00FF); 
//Transmit data
USART_vSendByte(0x7E);  //Start delimiter
USART_vSendByte(8 >> len);  //Length MSB
USART_vSendByte(len); //Length LSB
for(counter = 0; counter <= len - 1; counter++)  //Transmit data
USART_vSendByte(data[counter]);
USART_vSendByte(checksum);  //Transmit checksum
sei(); //enable interrupts
}

The USART_Sendbyte() routine simply writes a character to USART0 and checks to make sure that it doesn't overwrite a previous character being sent. Obviously the USART needs to be initialized before this function will work.

The send_Msg() routine expects an array containing a valid API frame and the length of the array to be passed to it. It then tacks on the start delimeter, the length of the entire packet, and checksum at the end. The checksum is initially calculated according the Xbee documentation.


Sometimes for testing I will pass a pre-constructed array such as 

char test[] = {0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 'T', 'E', 'S', 'T' };
to send_Msg(), but for actual applications we will use a routine to construct this array for us.

The following function constructs an API frame according to the specifications of API Identifier 0x10 and passes the array to our send_Msg() routine:


void ZigBee_TX_Request(char Frame_ID, long DH, long DL, int _16bitAddr, char Hops, char Options, char *RF_Data, int len )
{
int i; // counting variable
char buff[30]; //temporary buffer for transmitting
// ZigBee Transmit Request API Identifier
buff[0] = 0x10;
// Identifies the UART data frame for the host to correlate with a 
// subsequent ACK (acknowledgement). Setting Frame ID to ‘0' will disable response frame.
buff[1] = Frame_ID;
// MSB first, LSB last. Broadcast = 0x000000000000FFFF
buff[2] = (DH >> 24);
buff[3] = (DH >> 16);
buff[4] = (DH >> 8);
buff[5] = DH;
buff[6] = (DL >> 24);
buff[7] = (DL >> 16);
buff[8] = (DL >> 8);
buff[9] = DL;
// 16 bit address
buff[10] = (_16bitAddr >> 8);
buff[11] = _16bitAddr;
// Number of hops for message to take
buff[12] = Hops;
// Options
buff[13] = Options;
for(i = 0; i < len; i++)
buff[14+i] = RF_Data[i];
send_Msg(buff, 14+len);

}

limitations of this function include the specified buffer size, but that will be something that can be changed by adding a #define to the header file of the library for better elegance.


Now that we've covered how to send a packet, let's look at receiving a packet.


RxPacket rx_pkt;

ISR(USART_RX_vect) 
{   
cli();
if(UDR0 == 0x7E)
receive_Msg(&rx_pkt);
else
{
sei();
return;
}

}

This is the interrupt service routine for USART0 Rx.  And a global RxPacket variable from the structure shown earlier. In this ISR we check to see if the incoming byte was the start delimeter, 0x7E. If it is, we know we are receiving a packet, and we call the receive_Msg() routine. Otherwise the byte is disregarded.
We also disable interrupts (using cli() and sei(), these are avr-gcc shortcuts for disabling and enabling all interrupts, respectively) when receiving bytes so the function doesn't get preempted by another ISR. I've read that Atmega ISRs disable all other interrupts by default when executing an ISR, but I'm paranoid so I disable them anyways. Now let's look at receive_Msg():

void receive_Msg(RxPacket *rx_data)
{
PORTD ^= 0x80; // TEST LED
cli(); // Disable Interrupts
int count, len;
char temp, checksum;

while ((UCSR0A & (1 << RXC0)) == 0) {}; // Do nothing until data have been received and is ready to be read from UDR
temp = UDR0; //next incoming byte is the MSB of the data size
while ((UCSR0A & (1 << RXC0)) == 0) {}; // Do nothing until data have been received and is ready to be read from UDR

rx_data->len = (temp << 8) | UDR0; //merge LSB and MSB to obtain data length
while ((UCSR0A & (1 << RXC0)) == 0) {}; // Do nothing until data have been received and is ready to be read from UDR
rx_data->api_identifier = UDR0;

switch(rx_data->api_identifier) // Select proper sequence for receiving various packet types
{
case 0x90: // Zigbee Receive Packet
for(count = 1; count < rx_data->len; count++)
{
while ((UCSR0A & (1 << RXC0)) == 0) {}; // Do nothing until data have been received and is ready to be read from UDR
if(count == 1)
rx_data->DH = (UDR0 << 24);
else if(count == 2)
rx_data->DH |= (UDR0 << 16);
else if(count == 3)
rx_data->DH |= (UDR0 << 8);
else if(count == 4)
rx_data->DH |= UDR0;
else if(count == 5)

rx_data->DL = (UDR0 << 24);
else if(count == 6)
rx_data->DL |= (UDR0 << 16);
else if(count == 7)
rx_data->DL |= (UDR0 << 8);
else if(count == 8)
rx_data->DL |= UDR0;
else if(count == 9)
rx_data->source_addr_16bit = (UDR0 << 8);
else if(count == 10)
rx_data->source_addr_16bit = UDR0;
else if(count == 11)
rx_data->options = UDR0;
else
rx_data->data[count - 12] = UDR0;
}
while ((UCSR0A & (1 << RXC0)) == 0) {}; // Do nothing until data have been received and is ready to be read from UDR
rx_data->checksum = UDR0; //store checksum
break;
default:
break;
}

sei(); //enable interrupts
PORTD ^= 0x80; // TEST LED

}

This routine reads in an API packet byte by byte and inserts them into the RxPacket structure. The comments should explain what's going on here pretty much, and if there's any further confusion take a look at the packet structure again in the Xbee documentation. The one thing to point out that looks weird right now is the switch statement. Currently, there is only one valid condition for it, which is case 0x90. This is the api identifier for Zigbee Rx packets, which are the only ones we are using currently. Eventually this code should be adapted to include various other types of packets.

And that's pretty much the bare minimum for passing messages using Xbee series 2 api mode. I would also like to note that the only setting that I had to modify to get this code to consistently work was setting JV to 1. If you don't, the module will not scan through different channels looking for a coordinator to join. It sucks and your Xbee will just give up joining a network and sit there otherwise. I'm not sure why that isn't enabled by default, but be sure to set that using X-CTU or your own AT parameter setup function.

Tuesday, July 16, 2013

The Origins of an Atmega Based Sensor Network

I've decided to make this blog to periodically post my progress on various projects and contribute back to the resource I use most - looking at people's code on other blogs that I find through Google searches. It might also help to keep me keep focus if I continuously write down what I've done.

So, I've been working on this project for nearly a year now, and there's much to be said about all that I've discovered, but I'm just going to start from where I am now, since I've practically had to start from square one again. The project is designing a sensor network that uses xbees and atmega328 microcontrollers. I'm not the first one to try to do this by far, but there's a few new changes that I believe are important enough to warrant the creation of a new wireless sensor network (WSN) development platform.

Most WSN dev boards are difficult to use because of proprietary software or a very small support community for a complex system. Also, most research in sensor networks is focused on routing protocols and reducing energy consumption. I'm mostly interested in the applications of WSNs, particularly with robotics. So the board being designed here will use Xbees because of the common interface they use, and if I want to change the radio frequency the project is using or the routing protocol, I can change Xbees and only apply minor changes to the code. Xbees are also good because they come in varieties that already run the Zigbee stack which makes multihop applications easier to develope and they also have modules that only run 802.15.4, so if you want to work on your own custom routing protocol you can. We're also using atmegas because of the low power consumption of the chips and because I just like them.

We are currently working on developing an Xbee library for the atmega328p that will support interfaces for using Xbee series 1 and 2, and eventually adding a HAL (hardware abstraction layer) for using the library with other atmega chips and even other brands. Now, an obvious thing people will like to point out is that there is an Xbee library for arduino already which also uses the atmega328. Arduino doesn't provide the fine control we will be looking for in this project, so we are coding in C and using AVR studio. The library will hopefully be able to be added to any arduino project when we are finished however.

For more information on Xbees, check out the links below:
https://www.sparkfun.com/pages/xbee_guide
http://www.digi.com/xbee/