The RN XV, from Roving Networks is a versatile little device.
It is basically a wifi card with a serial port, designed to be easily connected to microcontrollers.
I previously used on my Arduino and even on my BeagleBone, where I used java (RXTX) to drive it.
I have a youtube video about the BeagleBone / wifly, but it's mainly about the Bone and RXTX than the RN XV, so I will not embed it here.
Did you ever work with old modems?
If so you are probably familiar with the Hayes protocol and the AT commands you sent over the serial port.
No, the RN XV does not use Hayes, but the concept is similar : you send commands in ASCII format to the serial port and in that way instruct the module to connect to a ssid, perform a http get etc.
The good thing about this is that it's easy to test the protocol just using an hyperterminal kind of software.
In windows I like to use Realterm, it's open source.
To connect the wifly to a PC you will need an FTDI usb interface (get yourself a couple of those from ebay if you don't have them already, they are extremely handy).
To easily work with the RN XV (other compatible modules might be slightly different in this regard, so check carefully your specific case) you will also need (or "you will be better of with") a breakout board for it. (it is cheap, no worries).
The reason you may want the breakout board is that the pin pitch of the RN XV is designed to match the XBee standard, which would not work with the common headers you normally use.
Final consideration : the wifly modules work at 3.3V which makes them perfect to work with the MSP430G2, should you use an AVR or other MCUs, make sure the serial communication runs ar 3.3V, else you need a logic level converter.
Let's connect the RN XV with the Launchpad :
In my example I am powering the wifly module from the Launchpad, the 3,3V is taken from pin 1 on the lp.
Besides Vcc and GND for now we will just need the UART rx and tx signals.
You may wonder why the wifly has so many pins.
Like Dave would say : "I am glad you asked!".
The Roving Networks wifi interface contains an MCU which obviously has GPIO and other features (including an ADC which I never tried so far, but it promises 14 bit resolution !!!).
The functionality of the contained mcu are accessible through the various pins, we will probably need to use some of those later on.
As a general consideration, a text based protocol is normally handy to implement an interface, it makes it easier to debug and it's normally quite flexible.
Unfortunately it may become difficult to manage if the amount of RAM you have at your disposal is limited, like in the case of the MSP430G2.
We then need to design the handling routines accordingly, we will not be able to allocate a big buffer to store incoming data to be processed asynchronously.
In general a synchronous approach would probably reduce the amount of needed memory and it could eventually work with the wifly protocol, however I am going for a simple asynchronous implementation, using the uart interrupt on the MSP430G2, just to make sure we do not miss any incoming char.
By default, the wifly uart starts at 9600 baud, 8 N 1.
Let's start configuring the clock and uart on the launchpad (details here) :
#include <msp430g2553.h>
// configure the CPU clock (MCLK)
// to run from DCO @ 16MHz and SMCLK = DCO / 4
void configClock()
{
BCSCTL1 = CALBC1_16MHZ; // Set DCO
DCOCTL = CALDCO_16MHZ;
BCSCTL2= DIVS_2 + DIVM_0; // divider=4 for SMCLK and 1 for MCLK
}
void configUART()
{
// set the pin mode 3 for pins 1 & 2 of port 1 (Uart mode)
P1SEL |= BIT1 + BIT2; // low bit = 1 for pin 1 and 2
P1SEL2 |= BIT1 + BIT2; // high bit = 1 for pin 1 and 2
// configure the UART
UCA0CTL0 = 0; //UART mode, No parity, LSB first, 8 data, 1 stop
UCA0CTL1 = UCSSEL_2; //use SCLK
UCA0BR0 = 0x1A; //lower byte of UCBR0. 26dec
//(4MHz / 9600 baud) see table 15-5
UCA0BR1 = 0x0; //upper byte of UCBR0.set to 0
UCA0MCTL = UCBRF_1 + UCBRS_0 + UCOS16; //sets UCBRFx to 1,
// UCBRSx tto 0 , UCOS16=1
UCA0CTL1 &= ~UCSWRST; // **Initialize USCI **
UC0IE |= UCA0RXIE; // Enable USCI_A1 RX interrupt
_EINT(); // global enable interrupts
}
Now we need to create a simple interrupt handling procedure for the uart rx, it will fill a receive buffer.
Normally I would allocate 4Kb of ram for a uart rx buffer, but there is no way we can do that on the G2, so we will need to limit it to 256 bytes, if we play it smart, it should be enough.
char UART_buffer[256];
unsigned char bufPtr = 0;
Together with the buffer I declare a buffer pointer, now, there are different ways to manage a buffer, I am going for a "circular management" approach which is quite common in these situations.
The receiving (interrupt) routine will store the incoming char in UART_buffer[bufPtr] and then increment bufPtr, when the end of the buffer is reached, since bufPtr is a byte (unsigned char) it will restart automatically from 0.
If we wanted a buffer size different from 256 we would have needed an if statement to check if the end of the buffer was reached and in that case reset the pointer.
#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void)
{
char c = UCA0RXBUF;
if ((c!='\n')&&(c!='\r'))
UART_buffer[bufPtr++] = c;
else
if (c=='\r') wifly_process(bufPtr);
}
The command protocol of the wifly uses ASCII text responses terminated by CR LF (\r\n) so we will use that to acknowledge that a response has been received and needs to be processed.
While we process a text returned from the wifly, other chars could be incoming at the same time, therefore we need to pass the position of the end of the text to be processed in this iteration.
So, what's happening is that while we are filling the uart rx buffer, in parallel we have a procedure that at each CRLF received extracts a single response line.
The process procedure knows up to which char it has to process since this is passed by the interrupt routine, but if also needs to know from which char to START processing.
In the current implementation (it can be improved) I am copying a single returned line into a separate buffer that will be processed later on.
Also I added a flag to identify if there is a complete text line ready to be processed.
char wifly_rx[128];
unsigned char processPtr = 0;
unsigned char commComplete = 0;
void wifly_process(unsigned char i)
{
unsigned char cnt = 0;
while (processPtr!=i)
wifly_rx[cnt++] = UART_buffer[processPtr++];
wifly_rx[cnt] = '\0';
commComplete=1;
}
I am adding a char 0 at the end of the buffer as it is needed by the string handling routines I created.
The first (a bit painful) thing you have to know when working with the wifly protocol is that it has two functioning modes :
The data mode and the command mode.
To send commands you need to enter in the command mode (fair, no?) which, via the UART is achieved by sending three dollar isngs ( $$$ ) not followed by a crlf and wait for 250 ms.
That's fairly easy and the module will answer with "CMD".
Practically it's less obvious than you may think.
Firts, you may already be in command mode and in that case you will not get any answer until you send a crlf, second in some cases the module may not respond (it improved with the latest firmware releases).
A typical case is while establishing a connection to a ssid.
For this reason in the latest firmware releases an alternative method to enter in command mode, was implemented and it is based on pulses sent through the GPIO.
We will not implement that one right now.
void UART_SendChar(unsigned char txChar)
{
while (!(IFG2&UCA0TXIFG)); // USCI_A0 TX buffer ready?
UCA0TXBUF=txChar;
}
void UART_TX(char *txStr)
{
unsigned int i=0;
while(txStr[i]!=0)
UART_SendChar((unsigned char)txStr[i++]);
}
unsigned char wifly_waitForText(char *text)
{
unsigned char retry = 255;
unsigned char matched = 0;
while ((retry-->0)&&(commComplete==0))
__delay_cycles(160000);
if (commComplete==0) return 0;
while ((text[matched]==wifly_rx[matched])&&(matched<255))
{
matched++;
if (text[matched]=='\0') return 1;
}
return 0;
}
unsigned int wifly_enterCommandMode()
{
unsigned char retry=20;
while (retry-->0)
{
UART_TX("\r\n\0");
__delay_cycles(250 * 16000);
UART_TX("\r\n\0"); // we send 2 times cr lf
// since the first one is only used to clear the wifly command buffer
if (wifly_waitForText("<2.32>\0")==1) return 1;
UART_TX("$$$\0");
__delay_cycles(250 * 16000);
// should receive "CMD"
if (wifly_waitForText("CMD\0")==1) return 1;
}
return 0;
}
The enterCommandMode procedure checks first if we are already in command mode.
In that case the wifly will respond to an empty command with a prompt with the version number, in my case this would be "<2.32>".
Once we are in command mode we can connect to a ssid
void wifly_connect(char *ssid,char *pwd)
{
UART_TX("set wlan auth 3\r\n\0"); // WPA mixed, might be different for you,
// check the RN XV user manual
__delay_cycles(160000);
UART_TX("set wlan ssid \0");
UART_TX(ssid);
UART_TX("\r\n\0");
__delay_cycles(160000);
UART_TX("set wlan phrase \0"); // you may need key instead,
// depending on your configuration
UART_TX(pwd);
UART_TX("\r\n\0");
__delay_cycles(160000);
UART_TX("join\r\n\0");
__delay_cycles(48000000); // wait 3 seconds, might actually take longer
}
void main(void)
{ // stop the watchdog
WDTCTL = WDTPW + WDTHOLD; // allow debugging
configClock();
configUART();
while (1)
{
if (wifly_enterCommandMode()==1)
{
wifly_connect("myssid\0","mypwd\0");
// do stuff here
// make sure you do not keep trying to connect if
// it is already connecting
}
}
}
Ok, this implementation is quite basic, but was enough in my case to see the wifly pop up with a dhcp assigned ip in the admin page of my router.
I will fine tune and expand this implementation in the next days and hopefully interface it with a simple webservice page to send and receive some data.
9 comments:
Hey, I'm trying to use your code to interface the WiFly with the MSP430G2553 and I'm getting an error when I try to upload the code to the micro-controller. The error is:
"#161 Declaration is incompatible with previous "wifly_process".
Any help you can give me would be great. Thank you.
Hello, maybe you wrote twice the wifly_process procedure?
Hello,
I copied and pasted the code and received the following error?
"#161 Declaration is incompatible with previous "wifly_process".
I tried moving the code around with no success.
Any ideas?
Thank you !
hello,
I am getting the same error as Cecilia.
Any help would be appreciated.
Thanks.
Hello, simply move the wifly_process function BEFORE the interrupt function, you can find my code here
Hi,
I'm trying to use your code but the red led on the wifly is flshing. In the Datasheet of the wifly they say that this means no ip. Can help me with this problem, please?
Hello Pemozo... difficult to understand what exactly is wrong with your example.
Most likely you have
1) mistyped the name of the SSID
or
2) chosen the wrong authentication model for you wifi network
or
3) mistyped the authentication password
or
4) your wifi network accepts 802.2n only while the wifly is 802.2g
or
5) you do not have an enabled DHCP on your router
or
6) any other thing I cannot think of right now :)
Hello,
I have a problem, I am using MSP430 and Rn-171-xv, but i cant do a debug because when i try to do it the wifly is disconected.
Someone can help to me?
thanks
Someone knows how do a debug while wifly is on?
When i try to do a debug it is disable.
thanks
Post a Comment