Labels

Showing posts with label WSN. Show all posts
Showing posts with label WSN. Show all posts

Monday, March 31, 2014

XBee Library Evolved

It has been quite some time since the last entry, and since then much has happened with the XBee C library and the progress on the ATmega-based sensor network.

The ATmega portion has forked off into its own separate project, which is in its prototype stage right now. The design itself has already won awards at IEEE Southeastcon for best poster/short paper, and we are expecting great progress with the project!

Now that we are working with a specific board, it was time to make the library much more versatile. The hardware-dependent functions have been moved into a hardware abstraction layer (HAL), and only raw XBee functionality remains in the c file. This way, we have no  microcontroller preference, and only the HAL has to be modified for use in the code.

In previous versions of the code, we had a function within the library for responding to new packets. This was a huge problem, as it required the user to go digging through the library itself, modifying the function inside, and exporting variables to the application code. This was a terrible design, and a solution had to be implemented. Now, the user calls a function, "setNewPacketCB()", which is passed a pointer to a function that is written in the application code. So, if you have a function, called, say, weiners(), you can set weiners to be called when a new packet arrives by calling "setNewPacketCB(weiners);".

Since the addition of the HAL, the user now has to #define which microcontroller he/she (who am I kidding?) he wants to use, and the HAL defines the appropriate ISR for the micro's USART, which USART is used, and what USART settings are configured. So, if the default settings aren't what you want, you only need to modify the contents of the HAL.

Originally, the micro being used was the AVR ATmega328P. We have now moved onto the mega2560, and soon to the mega1280 for low power capabilities. Introducing multiple USARTs have caused us to move the streaming target for printf() into the libraries for the development board, which we will discuss another day. Now, the print statements are safely wrapped in a #ifdefine statement, which will give the user feedback if DEBUG is #define'd. This also makes it easier for user to use this library with other micros sine the standard streaming target code won't need to be modified.

SO! Overall, the user now only needs to modify the HAL to use the library with any micro! More functions are still to come! But for now, here's what we have:

Xbee.h:
#include <stdio.h>
#include <stdlib.h>

#define RSSI_PIN PIND
#define RSSI_DDR DDRD
#define RSSI_PIN_NO PIND3

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
int data_len; // stores the length of the data
char checksum;
} RxPacket;

void rxISR();
void receive_Msg(RxPacket*);
void send_Msg(char*, int);
void USART_vSendByte(char);
void ZigBee_TX_Request(char, long, long, int, char, char, char*, int);
void AT_Command(char, char, char, char*, int);
void Init_Timer0(void);
float getRSSIPWM(void);
char getRSSI(void);
int get16bitAddress(void);
void killTimer(void);
float getTime_us(void);
void setNewPacketCB(void (*funptr)(void));
RxPacket getPacket(void);
void XbeeUSART_init(void);

XBeeHAL.h:
#include "Xbee.h"

//#define ATmega328P
#define ATmega2560

// Register Names for selected microcontroller
#ifdef ATmega328P
#include <avr/io.h>
#include <avr/interrupt.h>

// USART Definitions
#ifdef F_CPU
#pragma message ("F_CPU already defined.")
#else
#define F_CPU 16000000 //be sure to adjust for various clock settings
#endif
#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)

// Registers
#define XBEE_UDR UDR0

// Macros
#define RX_BUFFER_IS_FULL() ((UCSR0A & (1 << RXC0)) == 0)
#define TX_BUFFER_IS_FULL() ((UCSR0A & (1 << UDRE0)) == 0)
#define TOGGLE_LED() (PORTD ^= 0x80)
#define LED_OUTPUT() (DDRD |= 0x80)
// Functions
ISR(USART_RX_vect) { rxISR(); }

/* Code to initialize USART
*
* Eventually, this function should receive a USART number (e.g., 0-X, X being the number of USARTs available
* on the chip) and a baud rate. For now, we will use the #define variables to set this.
*/
void USART_INIT(void)
{
UBRR0L = BAUD_PRESCALE; // Load lower 8-bits of the baud rate value into the low byte of the UBRR register
UBRR0H = (BAUD_PRESCALE >> 8); // Load upper 8-bits of the baud rate value into the high byte of the UBRR register

UCSR0C |= (1 << UCSZ00) | (1 << UCSZ01); // Use 8-bit character sizes
UCSR0B |= (1 << RXCIE0) | (1 << RXEN0) | (1 << TXEN0);   // Turn on the transmission and reception circuitry
// Enable RX interrupt as well.
}

#endif

#ifdef ATmega2560
#include <avr/io.h>
#include <avr/interrupt.h>

// USART Definitions
#ifdef F_CPU
#pragma message ("F_CPU already defined.")
#else
#define F_CPU 8000000 //be sure to adjust for various clock settings
#endif
#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)

// Registers
#define USART_NO 3
#define XBEE_UDR UDR3

// Macros
#define RX_BUFFER_IS_FULL() ((UCSR3A & (1 << RXC3)) == 0)
#define TX_BUFFER_IS_FULL() ((UCSR3A & (1 << UDRE3)) == 0)
#define TOGGLE_LED() (PORTD ^= 0x80)
#define LED_OUTPUT() (DDRD |= 0x80)
// Functions
ISR(USART3_RX_vect) { rxISR(); }
/* Code to initialize USART
*
* Eventually, this function should receive a USART number (e.g., 0-X, X being the number of USARTs available
* on the chip) and a baud rate. For now, we will use the #define variables to set this.
*/
void USART_INIT(void)
{
UBRR3L = BAUD_PRESCALE; // Load lower 8-bits of the baud rate value into the low byte of the UBRR register
UBRR3H = (BAUD_PRESCALE >> 8); // Load upper 8-bits of the baud rate value into the high byte of the UBRR register

UCSR3C |= (1 << UCSZ30) | (1 << UCSZ31); // Use 8-bit character sizes
UCSR3B |= (1 << RXCIE3) | (1 << RXEN3) | (1 << TXEN3);   // Turn on the transmission and reception circuitry
// Enable RX interrupt as well.
}
#endif

Xbee.c
/*
 * Xbee.c
 *
 * Created: 6/20/2013 1:29:02 PM
 *  Author: Waron
 */ 
#include "Xbee.h"
#include "XbeeHAL.h"

RxPacket rx_pkt;

// Global Variables
char RSSI;
int MY;
int cb; // callback flag -- set if a callback function for new packets has been specified
void (*cbFunPtr)(void); // callback function pointer

/* Routine to call USART initialization routine from the HAL
 *
 * This routine configures the USART that the XBee is attached to.
 * Configues for settings 8N1 and 9600 baud.
 */
void XbeeUSART_init()
{
USART_INIT();
}

/* Routine to send a byte through USART
 *
 * This routine polls the USART data buffer register full flag until it indicates it is ready
 * for a new byte to be transmitted, then loads the new byte into the USART data buffer
 * register for transmission.
 */
void USART_vSendByte(char Data)
{
// Wait if a byte is being transmitted
while (TX_BUFFER_IS_FULL()) {}; // Do nothing until UDR is ready for more data to be written to it
// Transmit data
XBEE_UDR = Data;
}

/* USART Receive Interrupt
 * 
 * Hardware interrupt routine inside the HAL calls this function. When a new byte is received, it is
 * checked for the start delimeter of a new packet. If it is, then the receive message function is called.
 * Otherwise, nothing happens. After a packet is received, the callback function is called to handle the
 * packet, if one has been configured.
 */
void rxISR()
{
cli();
TOGGLE_LED();
if(XBEE_UDR == 0x7E)
{
RxPacket pkt;
receive_Msg(&pkt);
rx_pkt = pkt;
// Call function for responding to new packet
if(cb)
(*cbFunPtr)();
}

TOGGLE_LED();
sei();
}
/* Function to return the 16 bit Address of the Xbee
 *
 * This function calls the MY AT command and waits until the response frame is 
 * received.
 */
int get16bitAddress()
{
RxPacket pkt;
int temp;

do 
{
AT_Command(0x01, 'M', 'Y', 0, 0); // Send DB AT Command
while (RX_BUFFER_IS_FULL()){};
temp = XBEE_UDR;
receive_Msg(&pkt);
} while (pkt.api_identifier != 0x88);

#ifdef DEBUG
printf("MY is %d\n", (pkt.data[0]<<8)|pkt.data[1]);
#endif

return (pkt.data[0]<<8)|pkt.data[1]; // Return MY
}

/* Send Message Function
 *
 * This function is passed a character array of an XBee API Packet, along with the
 * length of the packet. The checksum is calculated, then the start delimeter is transmitted,
 * followed by the length of the packet, then the data, and finally the checksum.
 */
void send_Msg(char *data, int len)
{
//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
}

void receive_Msg(RxPacket *rx_data)
{
int count;
char temp;
rx_data->data_len = 0;

while (RX_BUFFER_IS_FULL()) {}; // Do nothing until data have been received and is ready to be read from UDR
temp = XBEE_UDR; //next incoming byte is the MSB of the data size
while (RX_BUFFER_IS_FULL()) {}; // Do nothing until data have been received and is ready to be read from UDR

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

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 (RX_BUFFER_IS_FULL()) {}; // Do nothing until data have been received and is ready to be read from UDR
if(count == 1)
rx_data->DH = ((long)XBEE_UDR << 24);
else if(count == 2)
rx_data->DH |= ((long)XBEE_UDR << 16);
else if(count == 3)
rx_data->DH |= ((long)XBEE_UDR << 8);
else if(count == 4)
rx_data->DH |= (long)XBEE_UDR;
else if(count == 5)
rx_data->DL = ((long)XBEE_UDR << 24);
else if(count == 6)
rx_data->DL |= ((long)XBEE_UDR << 16);
else if(count == 7)
rx_data->DL |= ((long)XBEE_UDR << 8);
else if(count == 8)
rx_data->DL |= (long)XBEE_UDR;
else if(count == 9)
rx_data->source_addr_16bit = (XBEE_UDR << 8);
else if(count == 10)
rx_data->source_addr_16bit = XBEE_UDR;
else if(count == 11)
rx_data->options = XBEE_UDR;
else
{
rx_data->data[count - 12] = XBEE_UDR;
rx_data->data_len++;
}
}
while (RX_BUFFER_IS_FULL()) {}; // Do nothing until data have been received and is ready to be read from UDR
rx_data->checksum = XBEE_UDR; //store checksum
break;
case 0x88: // AT Command
for(count = 1; count < rx_data->len; count++)
{
while (RX_BUFFER_IS_FULL()) {}; // Do nothing until data have been received and is ready to be read from UDR
if(count == 1)
rx_data->frame_id = XBEE_UDR;
else if(count == 2)
rx_data->AT_com[0] = XBEE_UDR;
else if(count == 3)
rx_data->AT_com[1] = XBEE_UDR;
else if(count == 4)
rx_data->status = XBEE_UDR;
else
{
rx_data->data[count - 5] = XBEE_UDR;
rx_data->data_len++;
}
}
while (RX_BUFFER_IS_FULL()) {}; // Do nothing until data have been received and is ready to be read from UDR
rx_data->checksum = XBEE_UDR; //store checksum
break;
default:
break;
}

}

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[14 + len]; //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 (acknowledgment). 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);
}

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);
}

/* Function to get RSSI from the PWM pin on the XBee
 *
 * This function returns the high time in microseconds from the PWM signal with corresponds to
 * the RSSI of the last received message. This is accomplished by starting and stopping an 8-bit
 * timer.
 */
float getRSSIPWM(void)
{
char RSSI;
float time_us;

Init_Timer0();
while(bit_is_set(PIND, PORTD3) && (TCNT0 != 50));
killTimer();
while(bit_is_clear(PIND, PORTD3));
Init_Timer0();
while(bit_is_set(PIND, PORTD3) && (TCNT0 != 50));
#ifdef DEBUG
printf("TCNT0 is %d\n", TCNT0);
#endif
killTimer();

return time_us/200.0;
}

/* This function is under construction, and is not guaranteed to work for all uC's */
void Init_Timer0(void)
{
TCNT0 = 0; // clear count value
TCCR0B = 0x03; // Select clk/64
}

/* This function is under construction, and is not guaranteed to work for all uC's */
void killTimer(void)
{
TCCR0B = 0x00; // disable clk
TCNT0 = 0; // clear count value
}

/* This function is under construction, and is not guaranteed to work for all uC's */
float getTime_us(void)
{
return (float)TCNT0*(1.0/((float)F_CPU/64.0));

}

/* Function to get the RSSI value of the last received message in dB
 *
 * This function calls the DB AT command. It then waits for the returning packet.
 * if the packet is not the returned DB value, then it is discarded.
 * The returned value is the attenuation of the signal in -dB.
 */
char getRSSI(void)
{
RxPacket pkt;
int temp;

AT_Command(0x01, 'D', 'B', 0, 0); // Send DB AT Command
while (RX_BUFFER_IS_FULL()){};
temp = XBEE_UDR; // Probably should add some code to check for start delimeter
receive_Msg(&pkt);

if(pkt.api_identifier != 0x88)
{
#ifdef DEBUG
printf("Failed to retrieve RSSI\n");
#endif
// otherwise, handle the packet as usual
if(cb)
(*cbFunPtr)();
return 0xFF;
}
else
{
#ifdef DEBUG
printf("RSSI is %d\n", pkt.data[0]);
#endif
return pkt.data[0]; // Return RSSI value
}

}

/* Callback function for new packets
 *
 * The user passes the name of their designated callback function. The function's address is
 * then set to the callback fuction pointer, cbFunPtr. The callback flag, cb, is set.
 */
void setNewPacketCB(void (*funptr)())
{
cb = 1;
cbFunPtr = funptr;
}

/* Get Packet
 *
 * Returns the last received packet.
 */
RxPacket getPacket()
{
return rx_pkt;
}

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.