Wifi Shield 2.0: How to reconnect to AP

Ardiuno Compatible shields

Moderators: violet, jessie, cory.j.fowler

Post Reply
achim.kuschowski
Pre-kindergarten
Pre-kindergarten
Posts: 4
Joined: Thu Mar 29, 2018 10:17 pm

Wifi Shield 2.0: How to reconnect to AP

Post by achim.kuschowski » Fri Mar 30, 2018 12:13 am

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.
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 http://wiki.seeedstudio.com/Wifi_Shield_V2.0/ , 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 http://wiki.seeedstudio.com/Wifi_Shield_V2.0/ 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

Code: Select all

wifly.isAssociated()
in the loop(), it leads to an immediate disconnect from the AP if 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 this:

Code: Select all

#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");
        }
    }
}
And to provide the complete picture, here is some data:
  • Arduino Uno rev. 3
  • 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...

achim.kuschowski
Pre-kindergarten
Pre-kindergarten
Posts: 4
Joined: Thu Mar 29, 2018 10:17 pm

Re: Wifi Shield 2.0: How to reconnect to AP

Post by achim.kuschowski » Fri Mar 30, 2018 4:08 pm

I didn't realize until now that this forum appears to be dead. The last substantial answers on posts given in this forum date from two years ago (2016).
If someone still feels like providing help here, please do so, and it will be very welcome.
Anyway, I will post the same question on https://forum.arduino.cc/index.php?topic=538388.0 (just for those of you who have the same question and want to check if there are answers...).

User avatar
violet
Staff
Staff
Posts: 32
Joined: Thu Nov 15, 2012 11:49 am

Re: Wifi Shield 2.0: How to reconnect to AP

Post by violet » Mon Apr 16, 2018 3:42 pm

achim.kuschowski wrote:
Fri Mar 30, 2018 4:08 pm
I didn't realize until now that this forum appears to be dead. The last substantial answers on posts given in this forum date from two years ago (2016).
If someone still feels like providing help here, please do so, and it will be very welcome.
Anyway, I will post the same question on https://forum.arduino.cc/index.php?topic=538388.0 (just for those of you who have the same question and want to check if there are answers...).
Hi there,
Thank you very much for posting on Seeed forum. Yes, the Seeed forum was in hibernation for almost 2 years. And now it's back :D . Welcome to let us know if you have suggestions/complaints about the forum. At the same time, our Tech Support Team is working on getting solutions for your questions. And they will answer you here on the forum when they find the solutions. Thank you very much for your patience.

Enjoy making!

BR,
Violet
Community Manager @Seeed.
Years may wrinkle the skin, but to give up enthusiasm wrinkles the soul.---Youth

achim.kuschowski
Pre-kindergarten
Pre-kindergarten
Posts: 4
Joined: Thu Mar 29, 2018 10:17 pm

Re: Wifi Shield 2.0: How to reconnect to AP

Post by achim.kuschowski » Mon Apr 30, 2018 5:40 pm

Hi Voilet,
thanks for the reply. Good to know this forum is still on. :)
Still would be very interested in a solution.
Best regards, Achim

achim.kuschowski
Pre-kindergarten
Pre-kindergarten
Posts: 4
Joined: Thu Mar 29, 2018 10:17 pm

Re: Wifi Shield 2.0: How to reconnect to AP

Post by achim.kuschowski » Tue May 01, 2018 12:42 am

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

Code: Select all

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

Code: Select all

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 that finally works for me.
It is similar to example 5 of http://wiki.seeedstudio.com/Wifi_Shield_V2.0/. But it has the ability to reconnect to the AP if the wifi connection failed temporarily.

Code: Select all

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

#define stringSSID   "Best$Place"                        // space characters in the SSID must be substituted by $ in this string 
#define stringKEY    "thisisnotmyrealpassword"   // maximum of 22 characters allowed
#define AUTH         WIFLY_AUTH_WPA2_PSK      // the security level of the access point (constants are defined in WiFly.h)

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 sheld serial object to the WiFly class
char ip[16];

void connectToWifi()
{
    Serial.println("Connecting to Wifi...");

    wifly.reset(); // reset the shield
    delay(1000);

    //set parameters to make wifi shield safer against outside attacks.
    wifly.sendCommand("set ip tcp-mode 0x10\r"); // disable remote configuration via TCP connections
    delay(100);
    wifly.sendCommand("set wlan hide 1\r"); //when the module shows the wlan settings, the passphrase is shown as ******
    delay(100);

    //set parameters for HTTP server
    wifly.sendCommand("set ip local 80\r"); // set the local port to 80 which is standard for HTTP servers
    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);
    
    //set parameters so the module automatically connects to the access point and attempts to reconnect after loss of connection
    wifly.sendCommand("set wlan auth WIFLY_AUTH_WPA2_PSK\r"); //set authentication mode to WPA2 with personal key
    delay(100);
    wifly.sendCommand("set wlan passphrase ssid " stringSSID "\r"); //set the SSID
    delay(100);
    wifly.sendCommand("set wlan passphrase " stringKEY "\r"); //set the passphrase (WPA/WPA2 personal key)
    delay(500); //longer delay needed, otherwise linkmon causes failure of connection; no documentation found on this topic; just trial and error :-(
    wifly.sendCommand("set wlan linkmon 30\r"); //set the link monitor to 30 attempts of reconnecting when detecting the loss of connection to the access point
    delay(500); //longer delay needed, otherwise linkmon causes failure of connection; no documentation found on this topic; just trial and error :-(
    wifly.sendCommand("set wlan join 1\r"); //set the mode (value 1) so the module will auto-connect and re-connect to the access point
    delay(100);
    
   
    Serial.println("Join " stringSSID );
    if (wifly.join(stringSSID, stringKEY, AUTH)) 
    {
        Serial.println("OK");
    } 
    else 
    {
        Serial.println("Failed");
    }

    delay(5000);

    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 setup()
{   
    //set up pins
    pinMode(11,OUTPUT);
    digitalWrite(11,LOW);
    pinMode(12,OUTPUT);
    digitalWrite(12,LOW);
    pinMode(13,OUTPUT);
    digitalWrite(13,LOW);
  
    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);

    Serial.println("Initializing web connection...");
    connectToWifi();
    Serial.println("Finished initializing web connection.");
    
    Serial.println("Setup finished, starting loop...");
}

void loop()
{
    if (wifly.available())       // the wifi shield has data available
    {
        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");
        }
    }
}
Hope this helps some of the other newbies. Pros probably don't need this info since they would have known right away. :D

Post Reply