Wifi Shield 2.0 (RN-171, Seeedstudio, WiFly): How to reconnect to AP

Dear community,
I am pretty new to the Arduino neighborhood - and a bit rusty concerning my own programming in c++. But I hope to get into gear quickly again.

Anyway, I recently got myself an Arduino Uno and a Wifi Shield 2.0 from SeeedStudio (tech details at the end of this post). Planned project is to have a webserver (its basic purpose will be to read out some pin states, lit some LEDs, and primarily calculate and show the last time stamp when certain pin states had changed).

A bit of background story:
On my way there (during the last couple of evenings), I already solved a few problems.
Just in case anyone else runs into the same problems: here they are including the solution.
I had quite some difficulties connecting to my existing Wifi network, i.e. to my Access Point (AP) via WPA2 authentication.
As a beginner, I just tried the example from Wifi Shield V2.0 | Seeed Studio Wiki , specifically the Example 4, since that is basically what I need as a starter.
It wouldn't connect to my AP, and in the end turned out to be a mean combination of two factors:
a) The SSID may contain spaces (emtpy characters) like in my SSID, but in the string, you have to substitute them by escape character $.
The command to assign a different character here didn't work, by the way. Not sure if it was my fault. Was in an early phase of learning.
b) The WPA pass phrase may not exceed 22 characters. Mine does because the encryption gets stronger with longer passwords.
Hey guys, a tiny comment in the code snippet would have saved a lot of time.
#define SSID "myssid" //spaces in the SSID have to be substituted by $ in this string
#define KEY "mypassword" //keys with more than 22 characters are not processed correctly

Anyway, this is what I finally achieved: I could get the example 4 from Wifi Shield V2.0 | Seeed Studio Wiki running.
Wow. What an achievement.

Now, what is my actual current problem?
What if the AP has a temporary power failure, or the connection to the AP is lost temporarily for any other reason?
I learned from forum studies that the RN-171 which is on the shield is not good in reconnecting from within loop(). The handbook does not elaborate on this at all - or I am blind...
I also learned that rebooting is not so easy, neither for the shield (does not lead to reconnection) nor for the Arduino itself (a true reboot by software that has the same effect like the hardware button is not part of the design).
Well, so I did what is (according to most forums) the cleanest way and what is as close to the HW button as one can get: I use the watchdog to initiate a "reconnect" to the AP during setup(). Or better: I plan to use it once I can figure out how the damn RN-171 let me know whether it is still connected to the AP.
To be honest: the guys having written the WiFly library... A bit more of code commenting would help a lot.

To put it short:
How can I retrieve the information "still connected to AP" while being up and ready as a webserver?
If I use wifly.isAssociated() in the loop(), it leads to an immediate disconnect from the AP when it happens during a GET request from outside via HTTP. My guess is: ...because the module receives and sends data while I try to switch into the command mode.

Alternatively:
How can I make the Wifi Shield 2.0 automatically reconnect again once the AP is available again (after the circuit breaker for the room with the AP is in place again, for instance)?

My code (pretty much the same as in the example; just added or altered a few lines for trying to improve things) is in a folllowing post since there is a character limit for posts:

And to provide the complete picture, here is some data:

  • Arduino Uno rev. 3
  • Seeedstudio Wifi Shield 2.0
  • wifly-EZX Ver: 4.41 Build: r1057, Jan 17 2014 10:23:56 on RN-171
  • LinkSys router WRT54GL 1.1 (using that old one since I do not want to change my home setup for testing)

Anything else of relevance? Well, please feel free to ask for details. Can't think of any right now...

And here is the code:

#include <SoftwareSerial.h>
#include "WiFly.h"

#define SSID   "Best$Place"              // space characters in the SSID must be substituted in this string by $
#define KEY    "thisisnotmyrealpassword"  // maximum 22 characters allowed

// check your access point's security mode, mine was WPA20-PSK
// if yours is different you'll need to change the AUTH constant, see the file WiFly.h for avalable security codes
#define AUTH      WIFLY_AUTH_WPA2_PSK

int flag = 0;

// Pins' connection
// Arduino       WiFly
//  2    <---->    TX
//  3    <---->    RX

SoftwareSerial wiflyUart(2, 3); // create a WiFi shield serial object
WiFly wifly(&wiflyUart); // pass the wifi siheld serial object to the WiFly class
char ip[16];

void setup()
{    
    wiflyUart.begin(9600); // start wifi shield uart port
    Serial.begin(9600); // start the arduino serial port
    Serial.println("--------- WIFLY Webserver --------");

    // wait for initilization of wifly
    delay(1000);

    wifly.reset(); // reset the shield
    delay(1000);
    //set WiFly params

    //added this command hoping it would improve things - didn't.
    wifly.sendCommand("set wlan linkmon 30\r"); // set the local comm port to 80
    delay(100);
    
    wifly.sendCommand("set ip local 80\r"); // set the local comm port to 80
    delay(100);

    wifly.sendCommand("set comm remote 0\r"); // do not send a default string when a connection opens
    delay(100);

    wifly.sendCommand("set comm open *OPEN*\r"); // set the string that the wifi shield will output when a connection is opened
    delay(100);

    

    Serial.println("Join " SSID );
    if (wifly.join(SSID, KEY, AUTH)) {
        Serial.println("OK");
    } else {
        Serial.println("Failed");
    }

    delay(5000);

   
    //changed this part a bit by replacing it with code from another example
    wifly.sendCommand("get ip\r");

    wiflyUart.setTimeout(500);
    if(!wiflyUart.find("IP="))
    {
        Serial.println("can not get ip");
        while(1);;
    }else
    {
        Serial.print("IP:");
    }

    char c;
    int index = 0;
    while (wifly.receive((uint8_t *)&c, 1, 300) > 0) { // print the response from the get ip command
        if(c == ':')
        {
            ip[index] = 0;
            break;
        }
        ip[index++] = c;
        Serial.print((char)c);

    }
    Serial.println();
    while (wifly.receive((uint8_t *)&c, 1, 300) > 0);;
    Serial.println("Web server ready");
}

void loop()
{

    if(wifly.available())       // the wifi shield has data available
    {
        //exchanged the following part by the code from example 5; 
        //at that time, I didn't know I was running in the AP reconnect problem so had time to play around...
        if(wiflyUart.find("*OPEN*")) // see if the data available is from an open connection by looking for the *OPEN* string
        {
            Serial.println("New Browser Request!");
            delay(1000); // delay enough time for the browser to complete sending its HTTP request string

            if(wiflyUart.find("pin=")) // look for the string "pin=" in the http request, if it's there then we want to control the LED
            {
                Serial.println("LED Control");
                // the user wants to toggle the LEDs
                int pinNumber = (wiflyUart.read()-48); // get first number i.e. if the pin 13 then the 1st number is 1
                int secondNumber = (wiflyUart.read()-48);
                if(secondNumber>=0 && secondNumber<=9)
                {
                    pinNumber*=10;
                    pinNumber +=secondNumber; // get second number, i.e. if the pin number is 13 then the 2nd number is 3, then add to the first number
                }
                digitalWrite(pinNumber, !digitalRead(pinNumber)); // toggle pin
                // Build pinstate string. The Arduino replies to the browser with this string.
                String pinState = "Pin ";
                pinState+=pinNumber;
                pinState+=" is ";
                if(digitalRead(pinNumber)) // check if the pin is ON or OFF
                {
                    pinState+="ON"; // the pin is on
                }
                else
                {
                    pinState+="OFF";  // the pin is off
                }
                // build HTTP header Content-Length string.
                String contentLength="Content-Length: ";
                contentLength+=pinState.length(); // the value of the length is the lenght of the string the Arduino is replying to the browser with.
                // send HTTP header
                wiflyUart.println("HTTP/1.1 200 OK");
                wiflyUart.println("Content-Type: text/html; charset=UTF-8");
                wiflyUart.println(contentLength); // length of HTML code
                wiflyUart.println("Connection: close");
                wiflyUart.println();
                // send response
                wiflyUart.print(pinState);
            }
            else
            {
                // send HTTP header
                wiflyUart.println("HTTP/1.1 200 OK");
                wiflyUart.println("Content-Type: text/html; charset=UTF-8");
                wiflyUart.println("Content-Length: 540"); // length of HTML code
                wiflyUart.println("Connection: close");
                wiflyUart.println();

                // send webpage's HTML code
                wiflyUart.print("<html>");
                wiflyUart.print("<head>");
                wiflyUart.print("<title>WiFi Shield Webpage</title>");
                wiflyUart.print("</head>");
                wiflyUart.print("<body>");
                wiflyUart.print("<h1>LED Toggle Webpage</h1>");
                // In the <button> tags, the ID attribute is the value sent to the arduino via the "pin" GET parameter
                wiflyUart.print("<button id=\"11\" class=\"led\">Toggle Pin 11</button> "); // button for pin 11
                wiflyUart.print("<button id=\"12\" class=\"led\">Toggle Pin 12</button> "); // button for pin 12
                wiflyUart.print("<button id=\"13\" class=\"led\">Toggle Pin 13</button> "); // button for pin 13
                wiflyUart.print("<script src=\"http://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js\"></script>");
                wiflyUart.print("<script type=\"text/javascript\">");
                wiflyUart.print("$(document).ready(function(){");
                wiflyUart.print("$(\".led\").click(function(){");
                wiflyUart.print("var p = $(this).attr('id');"); // get id value (i.e. pin13, pin12, or pin11)
                // send HTTP GET request to the IP address with the parameter "pin" and value "p", then execute the function
                // IMPORTANT: dont' forget to replace the IP address and port with YOUR shield's IP address and port
                wiflyUart.print("$.get(\"http://");
                wiflyUart.print(ip);
                wiflyUart.print(":80/a\", {pin:p},function(data){alert(data)});");// execute get request. Upon return execute the "function" (display an alert with the "data" send back to the browser.
                wiflyUart.print("});");
                wiflyUart.print("});");
                wiflyUart.print("</script>");
                wiflyUart.print("</body>");
                wiflyUart.print("</html>");
            }
            Serial.println("Data sent to browser");
        }
    }
}

Maybe I should add my straight observations as well...

If I run my above code, the arduino/shield does exactly what it is supposed to:

  • Connects to my AP alright
  • Provides a web page (I can get the page in my PC browser when typing in the arduino IP number)

If I then switch off my AP for like a minute and then switch it on again, the arduino does not reestablish the connection again.

Second observation:
If I add the following lines to my code:

void loop()
{
    if(wifly.available())       // the wifi shield has data available
    {
        ...
    }
    else
        if (!wifly.isAssociated())
             Serial.println("Lost connection to access point!");
}

in other words, if I try to find out whether the AP is still connected, I get the following behavior:

  • Connecting to the AP works fine.
  • In the very moment I try to get the web page by calling the IP in my browser, the arduino/shield is disconnected from the AP.

How do I know it disconnects?

  • The green LED D5 on the shield immediately changes from static on to blinking.
  • The browser reports that the http server cannot be contacted.

Hope this helps in addition. Any assistence would be really appreciated.

Ok, I did some further trying on my own.
For my first original question "How can I retrieve the information "still connected to AP" while being up and ready as a webserver?", I still haven't found an answer.
But at least I solved my second, alternative question "How can I make the Wifi Shield 2.0 automatically reconnect again once the AP is available again (after the circuit breaker for the room with the AP is in place again, for instance)?"
So finding a solution for the first question is not as urgent any more :mrgreen:

Anyway, here is the solution to make the wifi shield reconnect if the access point has been unavailable for some time.
It basically comes down to inserting

wifly.sendCommand("set wlan join 1\r");

If you have a look at the full code below, you will notice that my code still has the "delay" handling of executing commands (a delay of 100ms after each command) which is poor software design. It is a legacy from the example code that comes with the module.
You will notice that the delays around

wifly.sendCommand("set wlan linkmon 30\r");

have 500ms instead. It took me some time to find out that there appears to be a timing problem. Either I increase the delays, or I move linkmon up in the order of called commands (had it above set wlan hide first, and it worked with 100ms delay as well).
All very unsatisfying.

Anyway, here is the code (next post since this one exceeds 9000 characters) that finally works for me.
It is similar to example 5 of Wifi Shield V2.0 | Seeed Studio Wiki. But it has the ability to reconnect to the AP if the wifi connection failed temporarily.

Hope this helps some of the other newbies. Pros probably don't need this info since they would have known right away. :smiley:

Here is the code that at least leads to reconnection for the first minutes of network interruption.

webserver.ino (8.67 KB)