A sample program demonstrating a small but powerful web server using the Wifly module. This uses several libraries from others, but has a custom version of the WiflyInterface library, with numerous improvement to the mbed standard library.

Dependencies:   SW_HTTPServer WiflyInterface mbed C12832 IniManager

Here's the code

But you also might want to check out the SmartBoard-WiFly project page.

Basic Web Server

  • Serves static files from the selected file system. This is a compile-time setting, and a typical configuration supports gif, jpg, jpeg, ico, png, zip, gz, tar, txt, pdf, htm, and html.
  • It is designed to be small, thereby better supporting the limited resources of an embedded environment.

Advanced Web Services

  • Serves dynamically generated pages, where your software registers for a path, and then everything to that path activates your handler. Your handler then defines the header and body response.
  • Dynamic handlers can process GET query parameters (e.g. /dyn1?sky=blue&grass=green).
  • Dynamic handlers can process POST query parameters, as delivered from submission of a form.
  • Dynamic handlers can protect a resource with user:password access.

Run-Time Configurations

  • File System Support - using either the "local" file system supported by the magic chip, or from either an SD-Card or a USB flash drive.
  • Configurable to the maximum number of dynamic handlers (minimize memory requirements).
  • Configurable to the maximum number of name=value pairs for dynamic handlers (minimize memory requirements).

Compile-Time Configurations

  • Default filename for URL ending in '/' - default is 'index.htm'.
  • Configurable buffer sizes permit minimizing RAM requirements.
  • Configurable header response information.
  • Configurable for which serial port is used to communicate to the WiFly module.
  • Improved security option - to disable telnet access.

Diagnostics

  • API to determine the largest header (to more efficiently size the buffers).
  • API to gather the run-time characteristics - header processing time and content delivery time.

Limitations / Constraints

Known Issues

These are known issues, not yet resolved.

  1. Occasionally fails to serve a page - one test will constantly reload a web page every 30 seconds. It may run for hours, or minutes, then fail to load. Behaviors then are:
    • Hit the reload button in the browser and away it goes.
    • Hit the reload and you'll see the Wifly LEDs energize from the request, but no response by the web server. It appears that the embedded code does not "accept()" the connection in the TCP Socket Server.
      • In this case, the Wifly module has gone through an internal watchdog reset and the configuration parameters are such that it does not gracefully recover. Microchip is aware of this issue, but has not solved it.

Wifly Limitations

  • Single thread - it doesn't respond to overlapping requests (e.g. an embedded image may be requested before the main page completes transfer - the request is lost and the image not shown).
  • Single client - goes along with the single thread, but it doesn't support more than one client at a time.

Smart-Wifly-WebServer

  • Dynamic memory allocation - it does use dynamic memory allocation, which would be discouraged/avoided in many embedded systems. Here it uses it in parsing a request and it releases those resources upon completion of that request. If there is no other dynamic allocation that persists beyond a transaction, it should not cause memory fragmentation. Note that with multi-threading (if this is implemented with an OS), you then have race conditions that could cause fragmentation.

Web Server

Here's the web server in action. A combination of static pages served from the file system and dynamically generated pages.

/media/uploads/WiredHome/swsvr_1.pngPart of the main demo page,
which basically has all the
specifications, configurations, and limitations.
/media/uploads/WiredHome/swsvr_2.pngA zoomed out view of the same page.
/media/uploads/WiredHome/swsvr_3.pngIt would be possible to configure
the server via the web.
/media/uploads/WiredHome/swsvr_4.pngOne of the dynamically generated pages.
This one has parsed the query parameters.
/media/uploads/WiredHome/swsvr_5.pngA simple form which has a dynamic handler on the back end.
Here it takes the value associated with "leds"
and uses that to set the 4 LEDs on the mbed module.
/media/uploads/WiredHome/swsvr_6.pngA dynamic handler can require authentication.
/media/uploads/WiredHome/swsvr_7.pngSuccess!

But I've now gone so far beyond that in the current version. Here's what this one can do:

  1. It serves static web pages from a file system. I've only tested with the local file system and an SD card, but should work for any, so long as you remember that the local file system can't read subdirectories.
  2. It can serve dynamically generated web pages. This lets you accept name=value pairs using the URL (using either a GET or POST method). It can also accept them from forms. The demo lets you control the 4 LEDs from a form.
  3. As safely as possible it retrieves your credentials to the Wi-Fi Access Point. After using them, it overwrites that ram so they can't be as easily extracted.
  4. I made a large number of changes to the Wifly driver. It had too short of a timeout and I found quite a number of optimizations for performance and robustness.
  5. I have the start on a security feature - you can configure a resource to require user credentials to access it. The browser typically provides a username and password dialog. Take care however, as it does not support a secure (https) connection, so the credentials are not as securely transferred as I would like.

Optimizations I'd like to do:

  1. speed it up - I'm running the mbed to wifly module interface at 230K, which is about the top speed w/o flow control. There are other places where some time delays remain - I have eliminated a number of them.
  2. make it non-blocking, so other work can happen.
  3. integrate it with the rtos
  4. When a web page has referenced resources (e.g. an image tag), it rarely loads the image on the first try. I think the request for the resource comes in while it is still in the WiflyInterface cleaning up from the last connection. The Wifly module supports only a single connection at a time. I worked around this with a small bit of javascript to load the images after the web page.

But all in all I think it is a good start.

Program prerequisite

Here's the link to the program, but when you open it up, note a few very important items.

  1. Port Numbers listed in the constructor match the SmartBoard Baseboard.
  2. I sped up the communication baud rate to the mbed from the default 9600. Match your terminal program accordingly.
  3. Download this zip. Place it and an unzipped copy into the mbed local file system. These are the demo files.
  4. The typical ssid and password are not shown. See below to set yours.

ssid and password

You need to create a simple text file on your mbed root folder named "config.ini". The easiest way perhaps is to create "config.txt", add the information shown below and then rename it. This will be read at startup to connect you to the network. Something quite simple, like this:

[Wifi]
ssid=your_ssid
pass=your_pass_code

The program

And the program.

Import programSmart-WiFly-WebServer

A sample program demonstrating a small but powerful web server using the Wifly module. This uses several libraries from others, but has a custom version of the WiflyInterface library, with numerous improvement to the mbed standard library.

Files at this revision

API Documentation at this revision

Comitter:
WiredHome
Date:
Sun Sep 01 19:54:42 2013 +0000
Parent:
14:85c805890454
Child:
16:ab1867ffcf94
Commit message:
Cleaned up the code, removed some of the debug work.; Hopefully it is easier to follow by others.

Changed in this revision

DynamicPages.cpp Show diff for this revision Revisions of this file
DynamicPages.h Show diff for this revision Revisions of this file
Examples/DynamicPages.cpp Show annotated file Show diff for this revision Revisions of this file
Examples/DynamicPages.h Show annotated file Show diff for this revision Revisions of this file
Examples/SecurePage.cpp Show annotated file Show diff for this revision Revisions of this file
Examples/SecurePage.h Show annotated file Show diff for this revision Revisions of this file
Examples/ServerConfig.cpp Show annotated file Show diff for this revision Revisions of this file
Examples/ServerConfig.h Show annotated file Show diff for this revision Revisions of this file
IniManager.lib Show annotated file Show diff for this revision Revisions of this file
SW_HTTPServer.lib Show annotated file Show diff for this revision Revisions of this file
ServerConfig.cpp Show diff for this revision Revisions of this file
ServerConfig.h Show diff for this revision Revisions of this file
Utility.cpp Show diff for this revision Revisions of this file
Utility.h Show diff for this revision Revisions of this file
Utility/Utility.cpp Show annotated file Show diff for this revision Revisions of this file
Utility/Utility.h Show annotated file Show diff for this revision Revisions of this file
WiflyInterface.lib Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
--- a/DynamicPages.cpp	Mon Aug 12 23:06:52 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,280 +0,0 @@
-/// Demonstration of dynamic page creation using the Smartware Web Server
-///
-/// There are a few samples here of the dynamically generated web pages.
-/// Read the header above each one for more details.
-///
-#include "mbed.h"
-
-#include "SW_HTTPServer.h"
-#include "DynamicPages.h"
-#include "Utility.h"
-#include "Base64.h"
-
-BusOut leds(LED4,LED3,LED2,LED1);       // dynamic page "/dyn2" lets you control these
-
-Base64 bc64;
-char * converted = NULL;
-size_t convertedLen = 0;
-bool accessGranted = false;
-
-bool CredentialCheck(char * user, char * pass)
-{
-    printf("CredentialCheck(%s,%s)\r\n", user, pass);
-    if (strcmp(user, "user1") == 0 && strcmp(pass, "pass1") == 0)
-        return true;
-    else
-        return false;
-}
-
-
-/// SimpleSecurityCheck
-bool SimpleSecurityCheck(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount)
-{
-    bool ret = false;
-    char buf[150];
-
-    switch (type) {
-        case HTTPServer::SEND_PAGE:
-            accessGranted = false;
-            if (svr->GetHeaderValue("Authorization") != NULL) {
-                const char * p = svr->GetHeaderValue("Authorization");
-                if (p && strncmp(p, "Basic ", 6) == 0) {
-                    p += 6;
-                    if (converted)          // this should never be true, but is a nice safeguard
-                        free(converted);
-                    converted = bc64.Decode(p, strlen(p), &convertedLen);
-                    if (converted) {
-                        // Now check the actual credentials...
-                        char *colon;
-                        converted[convertedLen] = '\0';
-                        colon = strchr(converted, ':');
-                        if (colon) {
-                            *colon++ = '\0';
-                            if (CredentialCheck(converted, colon))
-                                accessGranted = true;
-                        }
-                    }
-                }
-            }
-            if (!accessGranted)
-                svr->header(401, "Access Denied", "WWW-Authenticate: Basic realm='Smart HTTPServer insecure logon'\r\n");
-            else {
-                svr->header(200, "OK", "Content-Type: text/html\r\n");
-                svr->send("<html><head><title>Security Check</title></head>\r\n");
-                svr->send("<body>\r\n");
-                sprintf(buf, "<h1>Welcome %s</h1>\r\n", converted);
-                svr->send(buf);
-                sprintf(buf, "Authorization was approved.<br/>\r\n");
-                svr->send(buf);
-                svr->send("Remember to close your browser for maximum security (and don't cache your credentials).<br/>\r\n");
-                svr->send("<a href='/'>back to main</a></body></html>\r\n");
-            }
-            if (converted)
-                free(converted);    // don't leak memory
-            ret = true;
-            break;
-        case HTTPServer::CONTENT_LENGTH_REQUEST:
-            ret = true;
-            break;
-        case HTTPServer::DATA_TRANSFER:
-            ret = true;
-            break;
-        default:
-            ret = false;
-            break;
-    }
-    return ret;
-}
-
-/// SimplyDynamicPage1
-///
-/// This web page is generated dynamically as a kind of "bare minimum".
-/// It doesn't do much.
-///
-/// You can see in main how this page was registered.
-///
-bool SuperSimpleDynamicPage(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount)
-{
-    bool ret = false;
-
-    switch (type) {
-        case HTTPServer::SEND_PAGE:
-            svr->header(200, "OK", "Content-Type: text/html\r\n");
-            svr->send("<html><head><title>Dynamic Page</title></head>\r\n");
-            svr->send("<body>\r\n");
-            svr->send("<h1>Smart WiFly Web Server</h1>\r\n");
-            svr->send("This page was generated dynamically.<br/>\r\n");
-            svr->send("<a href='/'>back to main</a></body></html>\r\n");
-            ret = true;
-            break;
-        case HTTPServer::CONTENT_LENGTH_REQUEST:
-            ret = true;
-            break;
-        case HTTPServer::DATA_TRANSFER:
-            ret = true;
-            break;
-        default:
-            ret = false;
-            break;
-    }
-    return ret;
-}
-
-
-/// SimplyDynamicPage
-///
-/// This web page is generated dynamically and fills in part of the information from
-/// data extracted from the URL as a query string. It also shows some additional
-/// information - memory stats, server stats and so forth.
-///
-/// You can see in main how this page was registered.
-///
-bool SimpleDynamicPage(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount)
-{
-    char buf[100];
-    char remoteIP[36];
-    bool ret = false;
-    HTTPServer::SW_PerformanceData perfData;
-
-    switch (type) {
-        case HTTPServer::SEND_PAGE:
-            if (strcmp("true", svr->GetParameter("Reset")) == 0)
-                svr->ResetPerformanceData();
-            // Send the header
-            svr->header(200, "OK", "Content-Type: text/html\r\n");
-
-            // Send top of the page
-            svr->send("<html><head><title>Dynamic Page</title></head>\r\n");
-            svr->send("<body>\r\n");
-            svr->send("<h1>Smart WiFly Web Server</h1>\r\n");
-            svr->send("This page was generated dynamically. Create your own name=value pairs on the URL "
-                      "which uses the GET method.<br/>\r\n");
-
-            // Show Passed-in parameters
-            svr->send("<h2>Query Parameters:</h2><blockquote>\r\n");
-            sprintf(buf, "%d parameters passed to {%s}:<br/>\r\n", paramcount, path);
-            svr->send(buf);
-            // show each of the parameters passed on the URL
-            for (int i=0; i<paramcount; i++) {
-                sprintf(buf, "%d: %s = %s<br/>\r\n", i, params[i].name, params[i].value);
-                svr->send(buf);
-            }
-            svr->send("</blockquote>\r\n");
-
-            // Show Memory Stats
-            svr->send("<h2>System Statistics:</h2><blockquote>\r\n");
-            svr->send("<table border='1'><tr><td>Parameter</td><td>Description</td></tr>\r\n");
-            sprintf(buf,"<tr><td align='right'>%d</td><td>Free memory</td></tr>\r\n", Free());
-            svr->send(buf);
-            sprintf(buf,"<tr><td align='right'>%d</td><td>Max Header size</td></tr>\r\n", svr->GetMaxHeaderSize());
-            svr->send(buf);
-//TODO           sprintf(buf,"<tr><td align='right'>%3.2f</td><td>Wifly SW version</td></tr>\r\n", svr->GetWifly()->getWiflyVersion());
-//           svr->send(buf);
-//           sprintf(buf,"<tr><td align='right'>%s</td><td>Wifly Version Information</td></tr>\r\n", svr->GetWifly()->getWiflyVersionString());
-//           svr->send(buf);
-            svr->GetRemoteAddr(remoteIP, sizeof(remoteIP));
-            sprintf(buf,"<tr><td align='right'>%s</td><td>Client IP Address</td></tr>\r\n", remoteIP);
-            svr->send(buf);
-            svr->send("</table>\r\n");
-            svr->send("</blockquote>\r\n");
-
-            // Show Web Server Performance metrics
-            svr->GetPerformanceData(&perfData);
-            svr->send("<h2>Web Server Performance Metrics</h2><blockquote>\r\n");
-            svr->send("<table border='1'><tr><td>avg time (uS)</td><td>samples</td><td>max time (uS)</td><td>description</td></tr>\r\n");
-            sprintf(buf, "<tr><td align='right'>%d</td><td align='right'>%d</td><td align='right'>%d</td><td>%s</td></tr>\r\n",
-                    (unsigned long)(perfData.Header.TotalTime_us / perfData.Header.Samples),
-                    perfData.Header.Samples,
-                    (unsigned long)(perfData.Header.MaxTime_us),
-                    "Header Response");
-            svr->send(buf);
-            sprintf(buf, "<tr><td align='right'>%d</td><td align='right'>%d</td><td align='right'>%d</td><td>%s</td></tr>\r\n",
-                    (unsigned long)(perfData.SendData.TotalTime_us / perfData.SendData.Samples),
-                    perfData.SendData.Samples,
-                    (unsigned long)(perfData.SendData.MaxTime_us),
-                    "SendData Response");
-            svr->send(buf);
-            svr->send("<tr><td colspan='3'>&nbsp;</td><td><a href='?Reset=false'>Reload</a> <a href='?Reset=true'>Reset Metrics</a></td></tr>\r\n");
-            svr->send("</table>\r\n");
-            svr->send("</blockquote>\r\n");
-
-            // Send bottom of the page
-            svr->send("<br/><a href='/'>back to main</a></body></html>\r\n");
-            ret = true;
-            break;
-        case HTTPServer::CONTENT_LENGTH_REQUEST:
-            ret = true;
-            break;
-        case HTTPServer::DATA_TRANSFER:
-            ret = true;
-            break;
-        default:
-            ret = false;
-            break;
-    }
-    return ret;
-}
-
-/// SimplyDynamicForm
-///
-/// This web page is generated dynamically and fills in part of the information from
-/// data extracted from the URL. It also generates a web form, and allows you to
-/// update the data in the form using the POST method. This one also updates the
-/// leds on the mbed module based on the form submission, so demonstrates a very
-/// simple interaction with the hardware.
-///
-/// You can see in main how this page was registered.
-///
-bool SimpleDynamicForm(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount)
-{
-    char buf[100];
-    bool ret = false;
-
-    switch (type) {
-        case HTTPServer::SEND_PAGE:
-            // set the LEDs based on a passed in parameter.
-            leds = atoi(svr->GetParameter("leds"));
-            // send the header
-            svr->header(200, "OK", svr->GetSupportedType(".htm"));  //svr->header(200, "OK", "Content-Type: text/html\r\n");
-            // send some data
-            svr->send("<html><head><title>Dynamic Page</title></head>\r\n");
-            svr->send("<body>\r\n");
-            svr->send("<h1>Smart WiFly Web Server</h1>\r\n");
-            svr->send("This form was generated dynamically. You can add name=value pairs on the URL "
-                      "which will show up in the form, but the form is submitted using POST method.<br/>\r\n");
-            svr->send("Hint: leds=7 turns on 3 of the 4 blue leds on the mbed<br/>\r\n");
-            sprintf(buf, "%d parameters passed to {%s}:<br/>\r\n", paramcount, path);
-            svr->send(buf);
-            // Create a user form for which they can post changes
-            sprintf(buf, "<form method='post' action='%s'>\r\n", path);
-            //sprintf(buf, "<form method='post' enctype='multipart/form-data' action='%s'>\r\n", path); // not supported
-            svr->send(buf);
-            // show the parameters in a nice format
-            svr->send("<table>\r\n");
-            for (int i=0; i<paramcount; i++) {
-                if (strcmp(params[i].name, "InFile") == 0)
-                    continue;
-                sprintf(buf, "<tr><td>%d</td><td>%s</td><td><input type='text' name='%s' value='%s'></td></tr>\r\n", i, params[i].name, params[i].name, params[i].value);
-                svr->send(buf);
-            }
-            //svr->send("<tr><td>&nbsp;</td><td>File</td><td><input type='file' name='InFile' size='40'></td></tr>\r\n");
-            svr->send("<tr><td>&nbsp;</td><td colspan='2'><input type='submit' value='submit'><input type='reset' value='clear'></td></tr>\r\n");
-            svr->send("</table>\r\n");
-            svr->send("</form>\r\n");
-            // see how we're doing with free memory
-            svr->send("<br/><a href='/'>Back to main</a></body></html>\r\n");
-            ret = true;
-            break;
-        case HTTPServer::CONTENT_LENGTH_REQUEST:
-            ret = true;
-            break;
-        case HTTPServer::DATA_TRANSFER:
-            ret = true;
-            break;
-        default:
-            ret = false;
-            break;
-    }
-    return ret;
-}
-
--- a/DynamicPages.h	Mon Aug 12 23:06:52 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-
-#ifndef DYNAMICPAGES_H
-#define DYNAMICPAGES_H
-#include "mbed.h"
-
-#include "SW_HTTPServer.h"
-
-bool SuperSimpleDynamicPage(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount);
-bool SimpleDynamicPage(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount);
-
-bool SimpleDynamicForm(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount);
-
-bool SimpleSecurityCheck(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount);
-
-#endif // DYNAMICPAGES_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Examples/DynamicPages.cpp	Sun Sep 01 19:54:42 2013 +0000
@@ -0,0 +1,207 @@
+/// Demonstration of dynamic page creation using the Smartware Web Server
+///
+/// There are a few samples here of the dynamically generated web pages.
+/// Read the header above each one for more details.
+///
+#include "mbed.h"
+
+#include "SW_HTTPServer.h"
+#include "DynamicPages.h"
+#include "Utility.h"
+
+
+BusOut leds(LED4,LED3,LED2,LED1);       // dynamic page "/dyn2" lets you control these
+
+
+/// SimplyDynamicPage1
+///
+/// This web page is generated dynamically as a kind of "bare minimum".
+/// It doesn't do much.
+///
+/// You can see in main how this page was registered.
+///
+bool SuperSimpleDynamicPage(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount)
+{
+    bool ret = false;
+
+    switch (type) {
+        case HTTPServer::SEND_PAGE:
+            svr->header(200, "OK", "Content-Type: text/html\r\n");
+            svr->send("<html><head><title>Dynamic Page</title></head>\r\n");
+            svr->send("<body>\r\n");
+            svr->send("<h1>Smart WiFly Web Server</h1>\r\n");
+            svr->send("This page was generated dynamically.<br/>\r\n");
+            svr->send("<a href='/'>back to main</a></body></html>\r\n");
+            ret = true;
+            break;
+        case HTTPServer::CONTENT_LENGTH_REQUEST:
+            ret = true;
+            break;
+        case HTTPServer::DATA_TRANSFER:
+            ret = true;
+            break;
+        default:
+            ret = false;
+            break;
+    }
+    return ret;
+}
+
+
+/// SimplyDynamicPage
+///
+/// This web page is generated dynamically and fills in part of the information from
+/// data extracted from the URL as a query string. It also shows some additional
+/// information - memory stats, server stats and so forth.
+///
+/// You can see in main how this page was registered.
+///
+bool SimpleDynamicPage(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount)
+{
+    char buf[100];
+    char remoteIP[36];
+    bool ret = false;
+    HTTPServer::SW_PerformanceData perfData;
+
+    switch (type) {
+        case HTTPServer::SEND_PAGE:
+            if (strcmp("true", svr->GetParameter("Reset")) == 0)
+                svr->ResetPerformanceData();
+            // Send the header
+            svr->header(200, "OK", "Content-Type: text/html\r\n");
+
+            // Send top of the page
+            svr->send("<html><head><title>Dynamic Page</title></head>\r\n");
+            svr->send("<body>\r\n");
+            svr->send("<h1>Smart WiFly Web Server</h1>\r\n");
+            svr->send("This page was generated dynamically. Create your own name=value pairs on the URL "
+                      "which uses the GET method.<br/>\r\n");
+
+            // Show Passed-in parameters
+            svr->send("<h2>Query Parameters:</h2><blockquote>\r\n");
+            sprintf(buf, "%d parameters passed to {%s}:<br/>\r\n", paramcount, path);
+            svr->send(buf);
+            // show each of the parameters passed on the URL
+            for (int i=0; i<paramcount; i++) {
+                sprintf(buf, "%d: %s = %s<br/>\r\n", i, params[i].name, params[i].value);
+                svr->send(buf);
+            }
+            svr->send("</blockquote>\r\n");
+
+            // Show Memory Stats
+            svr->send("<h2>System Statistics:</h2><blockquote>\r\n");
+            svr->send("<table border='1'><tr><td>Parameter</td><td>Description</td></tr>\r\n");
+            sprintf(buf,"<tr><td align='right'>%d</td><td>Free memory</td></tr>\r\n", Free());
+            svr->send(buf);
+            sprintf(buf,"<tr><td align='right'>%d</td><td>Max Header size</td></tr>\r\n", svr->GetMaxHeaderSize());
+            svr->send(buf);
+//TODO           sprintf(buf,"<tr><td align='right'>%3.2f</td><td>Wifly SW version</td></tr>\r\n", svr->GetWifly()->getWiflyVersion());
+//           svr->send(buf);
+//           sprintf(buf,"<tr><td align='right'>%s</td><td>Wifly Version Information</td></tr>\r\n", svr->GetWifly()->getWiflyVersionString());
+//           svr->send(buf);
+            svr->GetRemoteAddr(remoteIP, sizeof(remoteIP));
+            sprintf(buf,"<tr><td align='right'>%s</td><td>Client IP Address</td></tr>\r\n", remoteIP);
+            svr->send(buf);
+            svr->send("</table>\r\n");
+            svr->send("</blockquote>\r\n");
+
+            // Show Web Server Performance metrics
+            svr->GetPerformanceData(&perfData);
+            svr->send("<h2>Web Server Performance Metrics</h2><blockquote>\r\n");
+            svr->send("<table border='1'><tr><td>avg time (uS)</td><td>samples</td><td>max time (uS)</td><td>description</td></tr>\r\n");
+            sprintf(buf, "<tr><td align='right'>%d</td><td align='right'>%d</td><td align='right'>%d</td><td>%s</td></tr>\r\n",
+                    (unsigned long)(perfData.Header.TotalTime_us / perfData.Header.Samples),
+                    perfData.Header.Samples,
+                    (unsigned long)(perfData.Header.MaxTime_us),
+                    "Header Response");
+            svr->send(buf);
+            sprintf(buf, "<tr><td align='right'>%d</td><td align='right'>%d</td><td align='right'>%d</td><td>%s</td></tr>\r\n",
+                    (unsigned long)(perfData.SendData.TotalTime_us / perfData.SendData.Samples),
+                    perfData.SendData.Samples,
+                    (unsigned long)(perfData.SendData.MaxTime_us),
+                    "SendData Response");
+            svr->send(buf);
+            svr->send("<tr><td colspan='3'>&nbsp;</td><td><a href='?Reset=false'>Reload</a> <a href='?Reset=true'>Reset Metrics</a></td></tr>\r\n");
+            svr->send("</table>\r\n");
+            svr->send("</blockquote>\r\n");
+
+            // Send bottom of the page
+            svr->send("<br/><a href='/'>back to main</a></body></html>\r\n");
+            ret = true;
+            break;
+        case HTTPServer::CONTENT_LENGTH_REQUEST:
+            ret = true;
+            break;
+        case HTTPServer::DATA_TRANSFER:
+            ret = true;
+            break;
+        default:
+            ret = false;
+            break;
+    }
+    return ret;
+}
+
+/// SimplyDynamicForm
+///
+/// This web page is generated dynamically and fills in part of the information from
+/// data extracted from the URL. It also generates a web form, and allows you to
+/// update the data in the form using the POST method. This one also updates the
+/// leds on the mbed module based on the form submission, so demonstrates a very
+/// simple interaction with the hardware.
+///
+/// You can see in main how this page was registered.
+///
+bool SimpleDynamicForm(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount)
+{
+    char buf[100];
+    bool ret = false;
+
+    switch (type) {
+        case HTTPServer::SEND_PAGE:
+            // set the LEDs based on a passed in parameter.
+            leds = atoi(svr->GetParameter("leds"));
+            // send the header
+            svr->header(200, "OK", svr->GetSupportedType(".htm"));  //svr->header(200, "OK", "Content-Type: text/html\r\n");
+            // send some data
+            svr->send("<html><head><title>Dynamic Page</title></head>\r\n");
+            svr->send("<body>\r\n");
+            svr->send("<h1>Smart WiFly Web Server</h1>\r\n");
+            svr->send("This form was generated dynamically. You can add name=value pairs on the URL "
+                      "which will show up in the form, but the form is submitted using POST method.<br/>\r\n");
+            svr->send("Hint: leds=7 turns on 3 of the 4 blue leds on the mbed<br/>\r\n");
+            sprintf(buf, "%d parameters passed to {%s}:<br/>\r\n", paramcount, path);
+            svr->send(buf);
+            // Create a user form for which they can post changes
+            sprintf(buf, "<form method='post' action='%s'>\r\n", path);
+            //sprintf(buf, "<form method='post' enctype='multipart/form-data' action='%s'>\r\n", path); // not supported
+            svr->send(buf);
+            // show the parameters in a nice format
+            svr->send("<table>\r\n");
+            for (int i=0; i<paramcount; i++) {
+                if (strcmp(params[i].name, "InFile") == 0)
+                    continue;
+                sprintf(buf, "<tr><td>%d</td><td>%s</td><td><input type='text' name='%s' value='%s'></td></tr>\r\n", i, params[i].name, params[i].name, params[i].value);
+                svr->send(buf);
+            }
+            //svr->send("<tr><td>&nbsp;</td><td>File</td><td><input type='file' name='InFile' size='40'></td></tr>\r\n");
+            svr->send("<tr><td>&nbsp;</td><td colspan='2'><input type='submit' value='submit'><input type='reset' value='clear'></td></tr>\r\n");
+            svr->send("</table>\r\n");
+            svr->send("</form>\r\n");
+            // see how we're doing with free memory
+            svr->send("<br/><a href='/'>Back to main</a></body></html>\r\n");
+            ret = true;
+            break;
+        case HTTPServer::CONTENT_LENGTH_REQUEST:
+            ret = true;
+            break;
+        case HTTPServer::DATA_TRANSFER:
+            ret = true;
+            break;
+        default:
+            ret = false;
+            break;
+    }
+    return ret;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Examples/DynamicPages.h	Sun Sep 01 19:54:42 2013 +0000
@@ -0,0 +1,13 @@
+
+#ifndef DYNAMICPAGES_H
+#define DYNAMICPAGES_H
+#include "mbed.h"
+
+#include "SW_HTTPServer.h"
+
+bool SuperSimpleDynamicPage(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount);
+bool SimpleDynamicPage(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount);
+
+bool SimpleDynamicForm(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount);
+
+#endif // DYNAMICPAGES_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Examples/SecurePage.cpp	Sun Sep 01 19:54:42 2013 +0000
@@ -0,0 +1,82 @@
+
+#include "mbed.h"
+
+#include "SW_HTTPServer.h"
+#include "DynamicPages.h"
+#include "Utility.h"
+#include "Base64.h"
+
+
+Base64 bc64;
+char * converted = NULL;
+size_t convertedLen = 0;
+bool accessGranted = false;
+
+bool CredentialCheck(char * user, char * pass)
+{
+    printf("CredentialCheck(%s,%s)\r\n", user, pass);
+    if (strcmp(user, "user1") == 0 && strcmp(pass, "pass1") == 0)
+        return true;
+    else
+        return false;
+}
+
+
+/// SimpleSecurityCheck
+bool SimpleSecurityCheck(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount)
+{
+    bool ret = false;
+    char buf[150];
+
+    switch (type) {
+        case HTTPServer::SEND_PAGE:
+            accessGranted = false;
+            if (svr->GetHeaderValue("Authorization") != NULL) {
+                const char * p = svr->GetHeaderValue("Authorization");
+                if (p && strncmp(p, "Basic ", 6) == 0) {
+                    p += 6;
+                    if (converted)          // this should never be true, but is a nice safeguard
+                        free(converted);
+                    converted = bc64.Decode(p, strlen(p), &convertedLen);
+                    if (converted) {
+                        // Now check the actual credentials...
+                        char *colon;
+                        converted[convertedLen] = '\0';
+                        colon = strchr(converted, ':');
+                        if (colon) {
+                            *colon++ = '\0';
+                            if (CredentialCheck(converted, colon))
+                                accessGranted = true;
+                        }
+                    }
+                }
+            }
+            if (!accessGranted)
+                svr->header(401, "Access Denied", "WWW-Authenticate: Basic realm='Smart HTTPServer insecure logon'\r\n");
+            else {
+                svr->header(200, "OK", "Content-Type: text/html\r\n");
+                svr->send("<html><head><title>Security Check</title></head>\r\n");
+                svr->send("<body>\r\n");
+                sprintf(buf, "<h1>Welcome %s</h1>\r\n", converted);
+                svr->send(buf);
+                sprintf(buf, "Authorization was approved.<br/>\r\n");
+                svr->send(buf);
+                svr->send("Remember to close your browser for maximum security (and don't cache your credentials).<br/>\r\n");
+                svr->send("<a href='/'>back to main</a></body></html>\r\n");
+            }
+            if (converted)
+                free(converted);    // don't leak memory
+            ret = true;
+            break;
+        case HTTPServer::CONTENT_LENGTH_REQUEST:
+            ret = true;
+            break;
+        case HTTPServer::DATA_TRANSFER:
+            ret = true;
+            break;
+        default:
+            ret = false;
+            break;
+    }
+    return ret;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Examples/SecurePage.h	Sun Sep 01 19:54:42 2013 +0000
@@ -0,0 +1,10 @@
+
+#ifndef SECUREPAGE_H
+#define SECUREPAGE_H
+#include "mbed.h"
+
+#include "SW_HTTPServer.h"
+
+bool SimpleSecurityCheck(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount);
+
+#endif // SECUREPAGE_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Examples/ServerConfig.cpp	Sun Sep 01 19:54:42 2013 +0000
@@ -0,0 +1,119 @@
+/// Demonstration of dynamic page creation using the Smartware Web Server
+///
+/// There are a few samples here of the dynamically generated web pages.
+/// Read the header above each one for more details.
+///
+#include "mbed.h"
+
+#include "SW_HTTPServer.h"
+#include "DynamicPages.h"
+#include "Utility.h"
+#include "IniManager.h"
+
+
+
+/// ServerConfig
+///
+/// ssid
+/// pass code
+/// Security
+/// time server
+/// time zone
+///
+bool ServerConfig(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount) {
+    char buf[250];
+    bool ret = false;
+    char ssid[50] = "";
+    char pass[50] = "";
+    char security[10] = "";
+    char timeserver[60] = "";
+    char timezone[4] = "";       // -12
+    char *securityList[] = 
+        { "NONE", "WEP_128", "WPA1", "WPA", "WPA2_PSK", "n/a", "ADHOC", "n/a", "WPE_64" };
+    INI ini("/local/config.ini");
+            
+    switch (type) {
+        case HTTPServer::SEND_PAGE:
+            // svr->GetParameter("SSID");
+            
+            // ReadString(const char * section, const char * key, char * buffer, size_t bufferSize)
+            ini.ReadString("Wifi", "ssid", ssid, sizeof(ssid), svr->GetParameter("ssid"));
+            printf("svr ssid: %s\r\n", ssid);
+            printf("web ssid: %s\r\n", svr->GetParameter("ssid"));
+            if (svr->GetParameter("ssid") && strcmp(ssid, svr->GetParameter("ssid")) != 0)
+                {
+                strcpy(ssid, svr->GetParameter("ssid"));
+                printf("==  ssid: %s\r\n", ssid);
+                ini.WriteString("Wifi", "ssid", ssid);
+                }
+            printf("svr ssid: %s\r\n", ssid);
+            ini.ReadString("Wifi", "pass", pass, sizeof(pass), svr->GetParameter("pass"));
+            if (svr->GetParameter("pass") && strcmp(pass, svr->GetParameter("pass")) != 0)
+                {
+                strcpy(pass, svr->GetParameter("pass"));
+                ini.WriteString("Wifi", "pass", pass);
+                }
+            printf("svr pass: %s\r\n", pass);
+            ini.ReadString("Wifi", "security", security, sizeof(security), svr->GetParameter("security"));
+            if (svr->GetParameter("security") && strcmp(security, svr->GetParameter("security")) != 0)
+                {
+                strcpy(security, svr->GetParameter("security"));
+                ini.WriteString("Wifi", "security", security);
+                }
+            ini.ReadString("Clock", "timeserver", timeserver, sizeof(timeserver), svr->GetParameter("timeserver"));
+            if (svr->GetParameter("timeserver") && strcmp(timeserver, svr->GetParameter("timeserver")) != 0)
+                {
+                strcpy(timeserver, svr->GetParameter("timeserver"));
+                ini.WriteString("Clock", "timeserver", timeserver);
+                }
+            ini.ReadString("Clock", "timezone", timezone, sizeof(timezone), svr->GetParameter("timezone"));
+            if (svr->GetParameter("timezone") && strcmp(timezone, svr->GetParameter("timezone")) != 0)
+                {
+                strcpy(timezone, svr->GetParameter("timezone"));
+                ini.WriteString("Clock", "timezone", timezone);
+                }
+            
+            // send the header
+            svr->header(200, "OK", svr->GetSupportedType(".htm"));
+            // send some data
+            svr->send("<html><head><title>Server Config</title></head>\r\n");
+            svr->send("<body>\r\n");
+            svr->send("<h1>Smart WiFly Web Server - Server Config</h1>\r\n");
+            svr->send("Configure options here.<br/>\r\n");
+            // Create a user form for which they can post changes
+            sprintf(buf, "<form method='post' action='%s'>\r\n", path);
+            svr->send(buf);
+            // show the parameters in a nice format
+            svr->send("<table border='1'>\r\n");
+            sprintf(buf, "<tr><td>Setting</td><td>Value</td><td>Description</td></tr>\r\n");
+            svr->send(buf);
+            sprintf(buf, "<tr><td>SSID</td><td><input type='text' name='ssid' value='%s'></td><td>Name of the Access Point</td></tr>\r\n", ssid);
+            svr->send(buf);
+            sprintf(buf, "<tr><td>PassCode</td><td><input type='text' name='pass' value='%s'></td><td>Passcode</td></tr>\r\n", pass);
+            svr->send(buf);
+            sprintf(buf, "<tr><td>Security</td><td><input type='text' name='security' value='%s'></td><td>Security Setting</td></tr>\r\n", security);
+            svr->send(buf);
+            sprintf(buf, "<tr><td>Time Server</td><td><input type='text' name='timeserver' value='%s'></td><td>Time Server</td></tr>\r\n", timeserver);
+            svr->send(buf);
+            sprintf(buf, "<tr><td>Time Zone</td><td><input type='text' name='timezone' value='%s'></td><td>Time Zone</td></tr>\r\n", timezone);
+            svr->send(buf);
+            //svr->send("<tr><td>&nbsp;</td><td>File</td><td><input type='file' name='InFile' size='40'></td></tr>\r\n");
+            svr->send("<tr><td>&nbsp;</td><td colspan='2'><input type='submit' value='submit'><input type='reset' value='clear'></td></tr>\r\n");
+            svr->send("</table>\r\n");
+            svr->send("</form>\r\n");
+            // see how we're doing with free memory
+            svr->send("<br/><a href='/'>Back to main</a></body></html>\r\n");
+            ret = true;
+            break;
+        case HTTPServer::CONTENT_LENGTH_REQUEST:
+            ret = true;
+            break;
+        case HTTPServer::DATA_TRANSFER:
+            ret = true;
+            break;
+        default:
+            ret = false;
+            break;
+    }
+    return ret;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Examples/ServerConfig.h	Sun Sep 01 19:54:42 2013 +0000
@@ -0,0 +1,10 @@
+
+#ifndef SERVERCONFIG_H
+#define SERVERCONFIG_H
+#include "mbed.h"
+
+#include "SW_HTTPServer.h"
+
+bool ServerConfig(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount);
+
+#endif // SERVERCONFIG_H
--- a/IniManager.lib	Mon Aug 12 23:06:52 2013 +0000
+++ b/IniManager.lib	Sun Sep 01 19:54:42 2013 +0000
@@ -1,1 +1,1 @@
-http://mbed.org/users/WiredHome/code/IniManager/#ae5bf432c249
+http://mbed.org/users/WiredHome/code/IniManager/#1e2ee9bbee40
--- a/SW_HTTPServer.lib	Mon Aug 12 23:06:52 2013 +0000
+++ b/SW_HTTPServer.lib	Sun Sep 01 19:54:42 2013 +0000
@@ -1,1 +1,1 @@
-http://mbed.org/users/WiredHome/code/SW_HTTPServer/#19c5f6151319
+http://mbed.org/users/WiredHome/code/SW_HTTPServer/#6ebacf2946d8
--- a/ServerConfig.cpp	Mon Aug 12 23:06:52 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,119 +0,0 @@
-/// Demonstration of dynamic page creation using the Smartware Web Server
-///
-/// There are a few samples here of the dynamically generated web pages.
-/// Read the header above each one for more details.
-///
-#include "mbed.h"
-
-#include "SW_HTTPServer.h"
-#include "DynamicPages.h"
-#include "Utility.h"
-#include "IniManager.h"
-
-
-
-/// ServerConfig
-///
-/// ssid
-/// pass code
-/// Security
-/// time server
-/// time zone
-///
-bool ServerConfig(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount) {
-    char buf[250];
-    bool ret = false;
-    char ssid[50] = "";
-    char pass[50] = "";
-    char security[10] = "";
-    char timeserver[60] = "";
-    char timezone[4] = "";       // -12
-    char *securityList[] = 
-        { "NONE", "WEP_128", "WPA1", "WPA", "WPA2_PSK", "n/a", "ADHOC", "n/a", "WPE_64" };
-    INI ini("/local/config.ini");
-            
-    switch (type) {
-        case HTTPServer::SEND_PAGE:
-            // svr->GetParameter("SSID");
-            
-            // ReadString(const char * section, const char * key, char * buffer, size_t bufferSize)
-            ini.ReadString("Wifi", "ssid", ssid, sizeof(ssid), svr->GetParameter("ssid"));
-            printf("svr ssid: %s\r\n", ssid);
-            printf("web ssid: %s\r\n", svr->GetParameter("ssid"));
-            if (svr->GetParameter("ssid") && strcmp(ssid, svr->GetParameter("ssid")) != 0)
-                {
-                strcpy(ssid, svr->GetParameter("ssid"));
-                printf("==  ssid: %s\r\n", ssid);
-                ini.WriteString("Wifi", "ssid", ssid);
-                }
-            printf("svr ssid: %s\r\n", ssid);
-            ini.ReadString("Wifi", "pass", pass, sizeof(pass), svr->GetParameter("pass"));
-            if (svr->GetParameter("pass") && strcmp(pass, svr->GetParameter("pass")) != 0)
-                {
-                strcpy(pass, svr->GetParameter("pass"));
-                ini.WriteString("Wifi", "pass", pass);
-                }
-            printf("svr pass: %s\r\n", pass);
-            ini.ReadString("Wifi", "security", security, sizeof(security), svr->GetParameter("security"));
-            if (svr->GetParameter("security") && strcmp(security, svr->GetParameter("security")) != 0)
-                {
-                strcpy(security, svr->GetParameter("security"));
-                ini.WriteString("Wifi", "security", security);
-                }
-            ini.ReadString("Clock", "timeserver", timeserver, sizeof(timeserver), svr->GetParameter("timeserver"));
-            if (svr->GetParameter("timeserver") && strcmp(timeserver, svr->GetParameter("timeserver")) != 0)
-                {
-                strcpy(timeserver, svr->GetParameter("timeserver"));
-                ini.WriteString("Clock", "timeserver", timeserver);
-                }
-            ini.ReadString("Clock", "timezone", timezone, sizeof(timezone), svr->GetParameter("timezone"));
-            if (svr->GetParameter("timezone") && strcmp(timezone, svr->GetParameter("timezone")) != 0)
-                {
-                strcpy(timezone, svr->GetParameter("timezone"));
-                ini.WriteString("Clock", "timezone", timezone);
-                }
-            
-            // send the header
-            svr->header(200, "OK", svr->GetSupportedType(".htm"));
-            // send some data
-            svr->send("<html><head><title>Server Config</title></head>\r\n");
-            svr->send("<body>\r\n");
-            svr->send("<h1>Smart WiFly Web Server - Server Config</h1>\r\n");
-            svr->send("Configure options here.<br/>\r\n");
-            // Create a user form for which they can post changes
-            sprintf(buf, "<form method='post' action='%s'>\r\n", path);
-            svr->send(buf);
-            // show the parameters in a nice format
-            svr->send("<table border='1'>\r\n");
-            sprintf(buf, "<tr><td>Setting</td><td>Value</td><td>Description</td></tr>\r\n");
-            svr->send(buf);
-            sprintf(buf, "<tr><td>SSID</td><td><input type='text' name='ssid' value='%s'></td><td>Name of the Access Point</td></tr>\r\n", ssid);
-            svr->send(buf);
-            sprintf(buf, "<tr><td>PassCode</td><td><input type='text' name='pass' value='%s'></td><td>Passcode</td></tr>\r\n", pass);
-            svr->send(buf);
-            sprintf(buf, "<tr><td>Security</td><td><input type='text' name='security' value='%s'></td><td>Security Setting</td></tr>\r\n", security);
-            svr->send(buf);
-            sprintf(buf, "<tr><td>Time Server</td><td><input type='text' name='timeserver' value='%s'></td><td>Time Server</td></tr>\r\n", timeserver);
-            svr->send(buf);
-            sprintf(buf, "<tr><td>Time Zone</td><td><input type='text' name='timezone' value='%s'></td><td>Time Zone</td></tr>\r\n", timezone);
-            svr->send(buf);
-            //svr->send("<tr><td>&nbsp;</td><td>File</td><td><input type='file' name='InFile' size='40'></td></tr>\r\n");
-            svr->send("<tr><td>&nbsp;</td><td colspan='2'><input type='submit' value='submit'><input type='reset' value='clear'></td></tr>\r\n");
-            svr->send("</table>\r\n");
-            svr->send("</form>\r\n");
-            // see how we're doing with free memory
-            svr->send("<br/><a href='/'>Back to main</a></body></html>\r\n");
-            ret = true;
-            break;
-        case HTTPServer::CONTENT_LENGTH_REQUEST:
-            ret = true;
-            break;
-        case HTTPServer::DATA_TRANSFER:
-            ret = true;
-            break;
-        default:
-            ret = false;
-            break;
-    }
-    return ret;
-}
--- a/ServerConfig.h	Mon Aug 12 23:06:52 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,10 +0,0 @@
-
-#ifndef SERVERCONFIG_H
-#define SERVERCONFIG_H
-#include "mbed.h"
-
-#include "SW_HTTPServer.h"
-
-bool ServerConfig(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount);
-
-#endif // SERVERCONFIG_H
--- a/Utility.cpp	Mon Aug 12 23:06:52 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-#ifndef UTILITY_H
-#define UTILITY_H
-#include "Utility.h"
-
-
-uint32_t Free() {
-    uint32_t max = 100000;
-    uint32_t x = max / 2;
-    uint32_t min = 0;
-
-    while (min < max-1) {
-        void * p = malloc(x);
-        if (p) {
-            free(p);
-            min = x;
-        } else {
-            max = x;
-        }
-        x = (max + min)/2;
-    }
-    return(x);
-}
-
-#if 0
-// CopyFile
-//
-// An idea that didn't pan out since the local file does not support folders
-//
-bool CopyFile(const char * dst, const char * src) {
-    FILE * fsrc;
-    FILE * fdst;
-    char fbuffer[1000];
-    int bytes;
-    
-    pc.printf("Copy to %s from %s\r\n", dst, src);
-    fsrc = fopen(src,"rb");
-    if (fsrc) {
-        pc.printf("  opened %s\r\n", src);
-        fdst = fopen(dst,"wb");
-        if (fdst) {
-            pc.printf("  opened %s\r\n", dst);
-            bytes = fread(fbuffer,sizeof(char),sizeof(fbuffer),fsrc);
-            while (bytes > 0) {
-                fwrite(fbuffer, sizeof(char), bytes, fdst);
-                bytes = fread(fbuffer,sizeof(char),sizeof(fbuffer),fsrc);
-            }
-            fclose(fdst);
-            fclose(fsrc);
-            pc.printf("  success.\r\n");
-            return true;
-        }
-        fclose(fsrc);
-    }
-    pc.printf("  failed.\r\n");
-    return false;
-}
-
-// Copy /sd/web to /local/web [local file system does not support subdirs]
-    // I wanted to first image the sd card to the local file system for easier
-    // access, and then reverse it for easier updates, but was not able to do this.
-    if (0) {
-        printf("Open directory on /sd/web\r\n");
-        DIR *d = opendir("/sd/web");
-        struct dirent *p;
-        while((p = readdir(d)) != NULL) {
-            printf("%s\r\n", p->d_name);
-            char sfqfn[40], dfqfn[40];
-            sprintf(sfqfn,"%s/%s","/sd/web", p->d_name);
-            sprintf(dfqfn,"%s/%s","/local", p->d_name);
-            CopyFile(dfqfn,sfqfn);
-        }
-        closedir(d);
-        printf("Directory closed\r\n");
-    }
-#endif
-
-
-#endif
\ No newline at end of file
--- a/Utility.h	Mon Aug 12 23:06:52 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-#include "mbed.h"
-#ifndef UTILITY_H
-#define UTILITY_H
-
-
-
-/** Get amount of free memory.
-*
-* @returns number of bytes of unused memory on the heap.
-*/
-uint32_t Free();
-
-
-extern "C" void mbed_reset();
-
-#endif // UTILITY_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Utility/Utility.cpp	Sun Sep 01 19:54:42 2013 +0000
@@ -0,0 +1,78 @@
+#ifndef UTILITY_H
+#define UTILITY_H
+#include "Utility.h"
+
+
+uint32_t Free() {
+    uint32_t max = 100000;
+    uint32_t x = max / 2;
+    uint32_t min = 0;
+
+    while (min < max-1) {
+        void * p = malloc(x);
+        if (p) {
+            free(p);
+            min = x;
+        } else {
+            max = x;
+        }
+        x = (max + min)/2;
+    }
+    return(x);
+}
+
+#if 0
+// CopyFile
+//
+// An idea that didn't pan out since the local file does not support folders
+//
+bool CopyFile(const char * dst, const char * src) {
+    FILE * fsrc;
+    FILE * fdst;
+    char fbuffer[1000];
+    int bytes;
+    
+    pc.printf("Copy to %s from %s\r\n", dst, src);
+    fsrc = fopen(src,"rb");
+    if (fsrc) {
+        pc.printf("  opened %s\r\n", src);
+        fdst = fopen(dst,"wb");
+        if (fdst) {
+            pc.printf("  opened %s\r\n", dst);
+            bytes = fread(fbuffer,sizeof(char),sizeof(fbuffer),fsrc);
+            while (bytes > 0) {
+                fwrite(fbuffer, sizeof(char), bytes, fdst);
+                bytes = fread(fbuffer,sizeof(char),sizeof(fbuffer),fsrc);
+            }
+            fclose(fdst);
+            fclose(fsrc);
+            pc.printf("  success.\r\n");
+            return true;
+        }
+        fclose(fsrc);
+    }
+    pc.printf("  failed.\r\n");
+    return false;
+}
+
+// Copy /sd/web to /local/web [local file system does not support subdirs]
+    // I wanted to first image the sd card to the local file system for easier
+    // access, and then reverse it for easier updates, but was not able to do this.
+    if (0) {
+        printf("Open directory on /sd/web\r\n");
+        DIR *d = opendir("/sd/web");
+        struct dirent *p;
+        while((p = readdir(d)) != NULL) {
+            printf("%s\r\n", p->d_name);
+            char sfqfn[40], dfqfn[40];
+            sprintf(sfqfn,"%s/%s","/sd/web", p->d_name);
+            sprintf(dfqfn,"%s/%s","/local", p->d_name);
+            CopyFile(dfqfn,sfqfn);
+        }
+        closedir(d);
+        printf("Directory closed\r\n");
+    }
+#endif
+
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Utility/Utility.h	Sun Sep 01 19:54:42 2013 +0000
@@ -0,0 +1,16 @@
+#include "mbed.h"
+#ifndef UTILITY_H
+#define UTILITY_H
+
+
+
+/** Get amount of free memory.
+*
+* @returns number of bytes of unused memory on the heap.
+*/
+uint32_t Free();
+
+
+extern "C" void mbed_reset();
+
+#endif // UTILITY_H
--- a/WiflyInterface.lib	Mon Aug 12 23:06:52 2013 +0000
+++ b/WiflyInterface.lib	Sun Sep 01 19:54:42 2013 +0000
@@ -1,1 +1,1 @@
-http://mbed.org/users/WiredHome/code/WiflyInterface/#e4422f192d25
+http://mbed.org/users/WiredHome/code/WiflyInterface/#25958bf80481
--- a/main.cpp	Mon Aug 12 23:06:52 2013 +0000
+++ b/main.cpp	Sun Sep 01 19:54:42 2013 +0000
@@ -1,22 +1,26 @@
 /** @file main.cpp contains the main program that a user would write.
  * see the documentation above "main"
  */
-#include "mbed.h"               // ver 63
+#include "mbed.h"               // ver 66
 //#include "SDFileSystem.h"       // ver 2, standard
 
 // Modified standard components
-#include "WiflyInterface.h"     // ver 4+, derived from version 4 with my mods
+#include "WiflyInterface.h"     // ver 33, derived from version 4 with my mods
 
 // My components
+#include "SW_HTTPServer.h"      // ver 16, the initial release.
+
+// Project features
 #include "Utility.h"
 #include "Credentials.h"        // ver 0, credential manager
-#include "SW_HTTPServer.h"      // ver 0, the initial release.
+
 #include "DynamicPages.h"       // my dynamically generated pages
+#include "SecurePage.h"         // my secure pages
 #include "ServerConfig.h"
+
 #define HTTP_SERVER_PORT 80
 
 Serial pc(USBTX, USBRX);
-//BusOut testLEDs(LED2, LED1);
 
 LocalFileSystem local("local");         // some place to hold settings and maybe the static web pages
 //SDFileSystem sd(p5, p6, p7, p8, "sd");  // for the static web pages
@@ -26,9 +30,10 @@
 InterruptIn button(p24);
 DigitalIn actStatus(p24);
 
+Timer onceinawhile;
+
 void flip() {
     act = actStatus;
-    //pc.printf("**** FLIP %d\r\n", actStatus.read());
 }
 
 // A time-bound hook on startup to permit the user to access the wifly module
@@ -39,7 +44,7 @@
     const int baudrates[] = {2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400};
     static int i = 0;
     
-    pc.printf("Pausing 5 sec for user to interact with WiFly (press <enter>).\r\n");
+    pc.printf("Pausing 5 sec for shell access to WiFly (press <enter>).\r\n");
     userTimer.start();
     do {
         if (pc.readable()) {
@@ -73,7 +78,7 @@
             }
         }
     } while (userTimer.read_ms() < 5000);
-    pc.printf("shell closed.\r\n");
+    pc.printf("  WiFly shell closed.\r\n");
 }
 
 
@@ -119,35 +124,20 @@
     credentials cr;     // handles the credentials
 
     pc.baud(460800);    // I like a snappy terminal, so crank it up!
-
     pc.printf("\r\nSmart WiFly - Build " __DATE__ " " __TIME__ "\r\n");
 
     if (!ReadCredentials("/local/user.crd", &cr)) {
         pc.printf("**** ERROR, no /local/user.crd file was found. ****\r\n");
-        pc.printf("     Nothing else I can do until you fix this.\r\n");
         wait(1.0);
-        error("     Waiting for user to fix this problem.         \r\n");       // flash and die
+        error("     Waiting for user to fix this problem.         \r\n"); // flash and die
     }
 
-    // instantiate the WiflyInterface, then release the credentials
+    // Instantiate the WiflyInterface
     WiflyInterface wifly(p28, p27, p23, p24, cr.ssid, cr.pass, WPA);
+    // Release the credentials, which also secures them
     FreeCredentials(&cr);
 
-
-    // baud test
-#if 0
-    wifly.init(); // start it up as a client of my network using DHCP
-    const int testBauds[] = {9600, 2400, 12345, 19200, 2400, 4800, 115200, 230400, 230400, 4800, 2400, 57600};
-    for (int i=0; i<(sizeof(testBauds)/sizeof(testBauds[0])); i++)
-        {
-        int r = wifly.baud(testBauds[i]);
-        pc.printf("wifly.baud(%d) returned %d\r\n\r\n", testBauds[i], r);
-        }
-    pc.printf("wifly.baud() test complete.\r\n");
-    while (1)
-        ;
-#endif
-
+    // Bring the WiFly interface online
     do {
         wifly.init(); // start it up as a client of my network using DHCP
         wifly.baud(230400);     // Only 230K w/o HW flow control
@@ -158,7 +148,7 @@
         wifly.reset();
     } while (1);
 
-    WiFlyShell(wifly, pc);
+    WiFlyShell(wifly, pc);  // 5 second access at startup - in case you need it.
 
     // Now let's instantiate the web server - along with a few settings:
     // the Wifly object, the port of interest (typically 80),
@@ -167,32 +157,32 @@
     // the maximum number of dynamic pages that can be registered,
     // the serial port back thru USB (for development/logging)
     HTTPServer svr(&wifly, HTTP_SERVER_PORT, "/local/", 15, 30, 10, &pc);
-    pc.printf("Connect to this server at http://%s\r\n", wifly.getIPAddress());
+    pc.printf("Connect to this server at http://%s:%d\r\n", wifly.getIPAddress(), HTTP_SERVER_PORT);
 
     // But for even more fun, I'm registering a few dynamic pages
-    // which you see the handlers for in DynamicPages.cpp.
+    // You see the handlers for in DynamicPages.cpp.
     // Here you can see the path to place on the URL.
     // ex. http://192.168.1.140/dyn
     svr.RegisterHandler("/dyn",  SuperSimpleDynamicPage);
     svr.RegisterHandler("/dyn1", SimpleDynamicPage);
     svr.RegisterHandler("/dyn2", SimpleDynamicForm);
-
     svr.RegisterHandler("/pass", SimpleSecurityCheck);
-    
     svr.RegisterHandler("/config", ServerConfig);
 
     // Let the human know it is ready - if they are watching
     pc.printf("Waiting for a connection...\r\n");
 
-// wifly active pin    
+// wifly active pin
 button.rise(&flip);
 button.fall(&flip);
-    
+
+    onceinawhile.start();
     while (true) {
-        //if (testLEDs == 1)
-        //    testLEDs = 2;
-        //else
-        //    testLEDs = 1;
+        if (onceinawhile.read_ms() >= 1000)
+            {
+            onceinawhile.reset();
+            pc.printf("Largest free mem block is %d\r\n", Free());
+            }
         svr.Poll();   // non-blocking, but not deterministic
         //pc.printf("this %d, longest %d\r\n", thisRun, longestRun);
         if (pc.readable())
--- a/mbed.bld	Mon Aug 12 23:06:52 2013 +0000
+++ b/mbed.bld	Sun Sep 01 19:54:42 2013 +0000
@@ -1,1 +1,1 @@
-http://mbed.org/users/mbed_official/code/mbed/builds/b3110cd2dd17
\ No newline at end of file
+http://mbed.org/users/mbed_official/code/mbed/builds/9c8f0e3462fb
\ No newline at end of file