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.

Committer:
WiredHome
Date:
Mon Feb 02 03:03:08 2015 +0000
Revision:
38:962d71d6dd0e
Refresh update, bug fix, mbed application board compatibility.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
WiredHome 38:962d71d6dd0e 1 /// Demonstration of dynamic page creation using the Smartware Web Server
WiredHome 38:962d71d6dd0e 2 ///
WiredHome 38:962d71d6dd0e 3 /// There are a few samples here of the dynamically generated web pages.
WiredHome 38:962d71d6dd0e 4 /// Read the header above each one for more details.
WiredHome 38:962d71d6dd0e 5 ///
WiredHome 38:962d71d6dd0e 6 /// Status - very experimental.
WiredHome 38:962d71d6dd0e 7 ///
WiredHome 38:962d71d6dd0e 8 /// It reads the ini file, but is not reliably managing it.
WiredHome 38:962d71d6dd0e 9 /// have only tried on the local file system so far.
WiredHome 38:962d71d6dd0e 10 ///
WiredHome 38:962d71d6dd0e 11 #include "mbed.h"
WiredHome 38:962d71d6dd0e 12
WiredHome 38:962d71d6dd0e 13 #include "SW_HTTPServer.h"
WiredHome 38:962d71d6dd0e 14 #include "DynamicPages.h"
WiredHome 38:962d71d6dd0e 15 #include "Utility.h"
WiredHome 38:962d71d6dd0e 16 #include "IniManager.h"
WiredHome 38:962d71d6dd0e 17
WiredHome 38:962d71d6dd0e 18
WiredHome 38:962d71d6dd0e 19
WiredHome 38:962d71d6dd0e 20 /// ServerConfig
WiredHome 38:962d71d6dd0e 21 ///
WiredHome 38:962d71d6dd0e 22 /// ssid
WiredHome 38:962d71d6dd0e 23 /// pass code
WiredHome 38:962d71d6dd0e 24 /// Security
WiredHome 38:962d71d6dd0e 25 /// time server
WiredHome 38:962d71d6dd0e 26 /// time zone
WiredHome 38:962d71d6dd0e 27 ///
WiredHome 38:962d71d6dd0e 28 HTTPServer::CallBackResults ServerConfig(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount) {
WiredHome 38:962d71d6dd0e 29 char buf[250];
WiredHome 38:962d71d6dd0e 30 HTTPServer::CallBackResults ret = HTTPServer::ACCEPT_ERROR;
WiredHome 38:962d71d6dd0e 31 char ssid[50] = "";
WiredHome 38:962d71d6dd0e 32 char pass[50] = "";
WiredHome 38:962d71d6dd0e 33 char security[10] = "";
WiredHome 38:962d71d6dd0e 34 char timeserver[60] = "";
WiredHome 38:962d71d6dd0e 35 char timezone[4] = ""; // -12
WiredHome 38:962d71d6dd0e 36 //char *securityList[] =
WiredHome 38:962d71d6dd0e 37 // { "NONE", "WEP_128", "WPA1", "WPA", "WPA2_PSK", "n/a", "ADHOC", "n/a", "WPE_64" };
WiredHome 38:962d71d6dd0e 38 INI ini("/local/config.ini");
WiredHome 38:962d71d6dd0e 39
WiredHome 38:962d71d6dd0e 40 switch (type) {
WiredHome 38:962d71d6dd0e 41 case HTTPServer::SEND_PAGE:
WiredHome 38:962d71d6dd0e 42 // svr->GetParameter("SSID");
WiredHome 38:962d71d6dd0e 43
WiredHome 38:962d71d6dd0e 44 // ReadString(const char * section, const char * key, char * buffer, size_t bufferSize)
WiredHome 38:962d71d6dd0e 45 ini.ReadString("Wifi", "ssid", ssid, sizeof(ssid), svr->GetParameter("ssid"));
WiredHome 38:962d71d6dd0e 46 printf("svr ssid: %s\r\n", ssid);
WiredHome 38:962d71d6dd0e 47 printf("web ssid: %s\r\n", svr->GetParameter("ssid"));
WiredHome 38:962d71d6dd0e 48 if (svr->GetParameter("ssid") && strcmp(ssid, svr->GetParameter("ssid")) != 0)
WiredHome 38:962d71d6dd0e 49 {
WiredHome 38:962d71d6dd0e 50 strcpy(ssid, svr->GetParameter("ssid"));
WiredHome 38:962d71d6dd0e 51 printf("== ssid: %s\r\n", ssid);
WiredHome 38:962d71d6dd0e 52 ini.WriteString("Wifi", "ssid", ssid);
WiredHome 38:962d71d6dd0e 53 }
WiredHome 38:962d71d6dd0e 54 printf("svr ssid: %s\r\n", ssid);
WiredHome 38:962d71d6dd0e 55 ini.ReadString("Wifi", "pass", pass, sizeof(pass), svr->GetParameter("pass"));
WiredHome 38:962d71d6dd0e 56 if (svr->GetParameter("pass") && strcmp(pass, svr->GetParameter("pass")) != 0)
WiredHome 38:962d71d6dd0e 57 {
WiredHome 38:962d71d6dd0e 58 strcpy(pass, svr->GetParameter("pass"));
WiredHome 38:962d71d6dd0e 59 ini.WriteString("Wifi", "pass", pass);
WiredHome 38:962d71d6dd0e 60 }
WiredHome 38:962d71d6dd0e 61 printf("svr pass: %s\r\n", pass);
WiredHome 38:962d71d6dd0e 62 ini.ReadString("Wifi", "security", security, sizeof(security), svr->GetParameter("security"));
WiredHome 38:962d71d6dd0e 63 if (svr->GetParameter("security") && strcmp(security, svr->GetParameter("security")) != 0)
WiredHome 38:962d71d6dd0e 64 {
WiredHome 38:962d71d6dd0e 65 strcpy(security, svr->GetParameter("security"));
WiredHome 38:962d71d6dd0e 66 ini.WriteString("Wifi", "security", security);
WiredHome 38:962d71d6dd0e 67 }
WiredHome 38:962d71d6dd0e 68 ini.ReadString("Clock", "timeserver", timeserver, sizeof(timeserver), svr->GetParameter("timeserver"));
WiredHome 38:962d71d6dd0e 69 if (svr->GetParameter("timeserver") && strcmp(timeserver, svr->GetParameter("timeserver")) != 0)
WiredHome 38:962d71d6dd0e 70 {
WiredHome 38:962d71d6dd0e 71 strcpy(timeserver, svr->GetParameter("timeserver"));
WiredHome 38:962d71d6dd0e 72 ini.WriteString("Clock", "timeserver", timeserver);
WiredHome 38:962d71d6dd0e 73 }
WiredHome 38:962d71d6dd0e 74 ini.ReadString("Clock", "timezone", timezone, sizeof(timezone), svr->GetParameter("timezone"));
WiredHome 38:962d71d6dd0e 75 if (svr->GetParameter("timezone") && strcmp(timezone, svr->GetParameter("timezone")) != 0)
WiredHome 38:962d71d6dd0e 76 {
WiredHome 38:962d71d6dd0e 77 strcpy(timezone, svr->GetParameter("timezone"));
WiredHome 38:962d71d6dd0e 78 ini.WriteString("Clock", "timezone", timezone);
WiredHome 38:962d71d6dd0e 79 }
WiredHome 38:962d71d6dd0e 80
WiredHome 38:962d71d6dd0e 81 // send the header
WiredHome 38:962d71d6dd0e 82 svr->header(200, "OK", svr->GetSupportedType(".htm"));
WiredHome 38:962d71d6dd0e 83 // send some data
WiredHome 38:962d71d6dd0e 84 svr->send("<html><head><title>Server Config</title></head>\r\n");
WiredHome 38:962d71d6dd0e 85 svr->send("<body>\r\n");
WiredHome 38:962d71d6dd0e 86 svr->send("<h1>Smart WiFly Web Server - Server Config</h1>\r\n");
WiredHome 38:962d71d6dd0e 87 svr->send("Configure options here.<br/>\r\n");
WiredHome 38:962d71d6dd0e 88 // Create a user form for which they can post changes
WiredHome 38:962d71d6dd0e 89 sprintf(buf, "<form method='post' action='%s'>\r\n", path);
WiredHome 38:962d71d6dd0e 90 svr->send(buf);
WiredHome 38:962d71d6dd0e 91 // show the parameters in a nice format
WiredHome 38:962d71d6dd0e 92 svr->send("<table border='1'>\r\n");
WiredHome 38:962d71d6dd0e 93 sprintf(buf, "<tr><td>Setting</td><td>Value</td><td>Description</td></tr>\r\n");
WiredHome 38:962d71d6dd0e 94 svr->send(buf);
WiredHome 38:962d71d6dd0e 95 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);
WiredHome 38:962d71d6dd0e 96 svr->send(buf);
WiredHome 38:962d71d6dd0e 97 sprintf(buf, "<tr><td>PassCode</td><td><input type='text' name='pass' value='%s'></td><td>Passcode</td></tr>\r\n", pass);
WiredHome 38:962d71d6dd0e 98 svr->send(buf);
WiredHome 38:962d71d6dd0e 99 sprintf(buf, "<tr><td>Security</td><td><input type='text' name='security' value='%s'></td><td>Security Setting</td></tr>\r\n", security);
WiredHome 38:962d71d6dd0e 100 svr->send(buf);
WiredHome 38:962d71d6dd0e 101 sprintf(buf, "<tr><td>Time Server</td><td><input type='text' name='timeserver' value='%s'></td><td>Time Server</td></tr>\r\n", timeserver);
WiredHome 38:962d71d6dd0e 102 svr->send(buf);
WiredHome 38:962d71d6dd0e 103 sprintf(buf, "<tr><td>Time Zone</td><td><input type='text' name='timezone' value='%s'></td><td>Time Zone</td></tr>\r\n", timezone);
WiredHome 38:962d71d6dd0e 104 svr->send(buf);
WiredHome 38:962d71d6dd0e 105 //svr->send("<tr><td>&nbsp;</td><td>File</td><td><input type='file' name='InFile' size='40'></td></tr>\r\n");
WiredHome 38:962d71d6dd0e 106 svr->send("<tr><td>&nbsp;</td><td colspan='2'><input type='submit' value='submit'><input type='reset' value='clear'></td></tr>\r\n");
WiredHome 38:962d71d6dd0e 107 svr->send("</table>\r\n");
WiredHome 38:962d71d6dd0e 108 svr->send("</form>\r\n");
WiredHome 38:962d71d6dd0e 109 // see how we're doing with free memory
WiredHome 38:962d71d6dd0e 110 svr->send("<br/><a href='/'>Back to main</a></body></html>\r\n");
WiredHome 38:962d71d6dd0e 111 ret = HTTPServer::ACCEPT_COMPLETE;
WiredHome 38:962d71d6dd0e 112 break;
WiredHome 38:962d71d6dd0e 113 case HTTPServer::CONTENT_LENGTH_REQUEST:
WiredHome 38:962d71d6dd0e 114 ret = HTTPServer::ACCEPT_COMPLETE;
WiredHome 38:962d71d6dd0e 115 break;
WiredHome 38:962d71d6dd0e 116 case HTTPServer::DATA_TRANSFER:
WiredHome 38:962d71d6dd0e 117 ret = HTTPServer::ACCEPT_COMPLETE;
WiredHome 38:962d71d6dd0e 118 break;
WiredHome 38:962d71d6dd0e 119 default:
WiredHome 38:962d71d6dd0e 120 ret = HTTPServer::ACCEPT_ERROR;
WiredHome 38:962d71d6dd0e 121 break;
WiredHome 38:962d71d6dd0e 122 }
WiredHome 38:962d71d6dd0e 123 return ret;
WiredHome 38:962d71d6dd0e 124 }
WiredHome 38:962d71d6dd0e 125