Search Microcontrollers

Showing posts with label wifly. Show all posts
Showing posts with label wifly. Show all posts

Monday, September 17, 2012

MSP430G2 & Wifly - The internet of things

In the previous two posts I explained how to interface an RN XV wifly module with a Launchpad (MSP430G2553).
In the last post I discussed an easier method that can be used to connect to a remote webserver (or any TCP based service), and today I will implement it with the Launchpad.

First thing I created a simple php page, named pp.php which looks something like this :

<?
 // connect to databse
 //...
 // get $mn and $mv from the request
 // ...
 $sql = "insert into test (tstamp,measureName,measureValue) values (now(),'".$mn."',".$mv.")";
 // execute query
 echo "[[[[ok]]]]";
?>

this page inserts a record into a mysql table called "test".
Suppose my server is http://www,myserver.com, to test the page we need to open a browser and call :

http://www,myserver.com/pp.php?mn=wifly&mv=2.32

Using the technique illustrated in the previous post, I stored the server address into the wifly configuration :


set ip proto 18
set dns name www.myserver.com
set ip address 0
set ip remote 80
set com remote 0
save
reboot

We will use the wifly gpio lines 4,5 and 6 to detect if the module is associated with a ssid, then to ask it to connect with the TCP address stored in the config and once gpio6 is high (tcp connected) we will send the get command via uart.

I connected the pins 2.0, 2.1,2.2 on the launchpad respectively to gpio4,gpio6,gpio5 on the wifly

P2.0 -> gpio4 (associated) - input
P2.1 -> gpio6 (connected) - input
P2.2 -> gpio5 (connect) - output

Let's configure the P2 port on the launchpad :

void configGpio()
{
 P2DIR |=BIT2; // P2.2 output
 // P2.2 is connected to wifly gpio5
 P2DIR &= ~(BIT0 + BIT1); // P2.0 and P2.1 input
 // P2.0 is connected to wifly gpio4
 // P2.1 is connected to wifly gpio6
 P2REN |=BIT0 + BIT1;
 // pulldown resistors
 P2OUT &= ~(BIT2);
}

Then we need a procedure to send  the GET request.
In my case I had to use HTTP/1.1 specifications and chances are that you will have to do the same unless you are working with you local webserver.
The GET command for version 1.1 of the HTTP protocol requires that the Host is always specified at each request, so the GET is someting like :

GET /pp.php?mn=wifly&mv=123.45 HTTP/1.1
Host: www.myserver.com

And this is the procedure I use


void wifly_TCPsend(char *text)
{
 if ((P2IN & BIT0)>0) // wifly is associated
 {
  if ((P2IN & BIT1)>0) // wifly is connected
  {
    UART_TX(text);
    __delay_cycles(16000000); //wait 1 second
      P2OUT &= ~BIT2; // close connection
     __delay_cycles(16000000); //wait 1 second
    wifly_enterCommandMode();
    UART_TX("sleep\0");
    __delay_cycles(160000000); //wait 10 seconds
  } else 
   // associated, but not connected -> let's connect
   P2OUT |= BIT2;  // pulls wifly gpio5 high
 } else
 { // the module is not associated
wifly_enterCommandMode();
UART_TX("join\0");
__delay_cycles(16000000); //wait 1 second
 }
}


This implementation is pretty basic, it works this way :
1) we check if the module is associated to a ssid, if not we try to send a join command. It should not be needed as we pre-configured the module to automatically join the stored ssid
2) Once the module is associated (gpio4 -P2.0- is high) we raise gpio5 (P2.2) to initiate a tcp connection
3) Once the connection has been successfully established, the gpio6 (2.1) goes high and we can send the get request via uart

The main  procedure becomes :


void main(void)
{ // stop the watchdog
 WDTCTL = WDTPW + WDTHOLD; // allow debugging
 configClock();
 configUART();
  configGpio();
 while (1)
 {  
  // do whatever you need here, like getting values from the ADC 
  // or reading sensors via SPI or I2C...
 // then customize the get call according to your needs 
   wifly_TCPsend("GET /pp.php?m=wifly&v=123.45 HTTP/1.1\nHost: www.myserver.com\n\0");
  // you may want to place this in an interrupt timer routine and sleep while waiting 
 }
}

.. and sue enough I have the values logged in my mysql table in my remote webserver. 

One tricky issue is to actually read data from the webserver.
Data is coming back, no issues with that, but the server will send you also some header information, which will force you to parse the content.
Again, parsing a text is no the best thing to do with a device with minim memory, so you may want to use some kind of TAG to identify the only part of text that should be considered.
My php webpage uses

echo "[[[[ok]]]]";

to pass back the "ok" message, so, the launchpad will disregard the incoming uart data until a series of four consecutive "[" are received.
Why would we want to pass data back to the launchpad?
Imagine you store in your webserver the configuration to manage the heating system of your Chalet.
When you decide it's a good weekend to go there, you may want to turn the heating on (or increase the temperature) few hours before, then to turn it off automatically at the end of the weekend.
Your launchpad, installed in your chalet, will read the current temperature (it has a temp sensor inside), every minute it will check with your webserver what it has to do with that temperature.
The webserver will check and will send back a "ON" or "OFF" response.


There are plenty of possible implementations, let me know your ideas! 

Friday, September 14, 2012

Roving Networks RN XV - Wifly module

In the previous post I illustrated a basic way to interface the wifly module to the MSP430G2 using the uart.
As I already anticipated in that post, the method could be improved, but for that we need to dig in a bit into the features of this module.

The wifly has the ability to store configurations, this means that we can pre-set all the needed parameters and we can avoid to send them from the mcu via uart.

Using an FTDI cable I connect to the module and set the config pars :

$$$  (to enter in command mode)
set wlan auth 3 
set wlan ssid myssid
set wlan phrase mypwd
set wlan channel 0   (0 means autoscan)
save

The configuration is now saved and it will be automatically loaded at each reboot / power up.
Once these parameters are set, to join the ssid, we will just need to issue the command

join

However we could even do better than that, in fact, if we set the auto join function to "1" (which btw is the default) the wifly will automatically try to join the ssid at each reboot.

set wlan join 1
save

Cool, not much to do for our mcu now, eh?
Just wait, it actually gets even better.

Fine, we are associated with the ssid, we got an ip address etc, now we need to establish a tcp connection to a remote server.
I quote from the RN XV user manual :


set ip proto 18 //enable html client
set dns name www.webserver.com //name of your webserver
set ip address 0 // so WiFly will use DNS
set ip remote 80 // standard webserver port
set com remote 0 // turn off the REMOTE string so it does not interfere with the post

You guessed that, you can save this too in the internal config!

save

To make the connection the command would be:
open
or inline you can send open www.webserver.com 80
The user’s microprocessor should write to the uart:
GET /ob.php?obvar=WEATHER \n\n

So, finally this makes things way easier, but it's not the end of the story...
What if we could check from a gpio pin if the wifly is associated, then raise another gpio to ask it to connect to the remote tcp port and finally get from a third gpio if it successfully connected?


Yup, that's another nice feature which is actually available.
The specific gpio pins are the 4,5 and 6 and must be configured to be used in this way.

set sys iofunc 0x70 // enable alternate function for GPIO 6, 5 and 4
save

With all this set and saved in the configuration, at power on the wifly module will try to associate to the ssid. once it is ready to perform a tcp connection it will raise gpio4 (which we will check from our mcu).
At that point, when we need to contact the remote server, we will raise the gpio5 and check gpio6 to verify that the connection was established.
At that point, if we are contacting a web server, we will send the GET request through the uart and will receive the answer from there.

... and we did not even need to enter in command mode!

Nice eh?
Will implement this with the launchpad in the next days.


MSP430G2 - Interfacing a wifly (RN XV) module

Do you like "the internet of things" concept?

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.