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:
Thu Jun 27 17:14:18 2013 +0000
Revision:
8:50fc3ddf828a
Parent:
7:72b5df2fac93
Child:
10:b0b6da272a7b
Small enhancement to show the SW version API.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
WiredHome 7:72b5df2fac93 1 /// Demonstration of dynamic page creation using the Smartware Web Server
WiredHome 3:b3e21f3306a1 2 #include "mbed.h"
WiredHome 3:b3e21f3306a1 3
WiredHome 3:b3e21f3306a1 4 #include "SW_HTTPServer.h"
WiredHome 3:b3e21f3306a1 5 #include "DynamicPages.h"
WiredHome 3:b3e21f3306a1 6 #include "Utility.h"
WiredHome 3:b3e21f3306a1 7
WiredHome 3:b3e21f3306a1 8 BusOut leds(LED1,LED2,LED3,LED4); // dynamic page "/dyn2" lets you control these
WiredHome 3:b3e21f3306a1 9
WiredHome 3:b3e21f3306a1 10
WiredHome 4:178df829d62b 11 /// SimplyDynamicPage1
WiredHome 4:178df829d62b 12 ///
WiredHome 4:178df829d62b 13 /// This web page is generated dynamically.
WiredHome 4:178df829d62b 14 ///
WiredHome 4:178df829d62b 15 bool SuperSimpleDynamicPage(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount) {
WiredHome 4:178df829d62b 16 bool ret = false;
WiredHome 4:178df829d62b 17
WiredHome 4:178df829d62b 18 switch (type) {
WiredHome 4:178df829d62b 19 case HTTPServer::SEND_PAGE:
WiredHome 4:178df829d62b 20 svr->header(200, "OK", "Content-Type: text/html\r\n");
WiredHome 4:178df829d62b 21 svr->send("<html><head><title>Dynamic Page</title></head>\r\n");
WiredHome 4:178df829d62b 22 svr->send("<body>\r\n");
WiredHome 7:72b5df2fac93 23 svr->send("This page was generated dynamically.<br/>\r\n");
WiredHome 7:72b5df2fac93 24 svr->send("<a href='/'>back to main</a></body></html>\r\n");
WiredHome 4:178df829d62b 25 ret = true;
WiredHome 4:178df829d62b 26 break;
WiredHome 4:178df829d62b 27 case HTTPServer::CONTENT_LENGTH_REQUEST:
WiredHome 4:178df829d62b 28 ret = true;
WiredHome 4:178df829d62b 29 break;
WiredHome 4:178df829d62b 30 case HTTPServer::DATA_TRANSFER:
WiredHome 4:178df829d62b 31 ret = true;
WiredHome 4:178df829d62b 32 break;
WiredHome 4:178df829d62b 33 default:
WiredHome 4:178df829d62b 34 ret = false;
WiredHome 4:178df829d62b 35 break;
WiredHome 4:178df829d62b 36 }
WiredHome 4:178df829d62b 37 return ret;
WiredHome 4:178df829d62b 38 }
WiredHome 4:178df829d62b 39
WiredHome 4:178df829d62b 40
WiredHome 3:b3e21f3306a1 41 /// SimplyDynamicPage
WiredHome 3:b3e21f3306a1 42 ///
WiredHome 3:b3e21f3306a1 43 /// This web page is generated dynamically and fills in part of the information from
WiredHome 3:b3e21f3306a1 44 /// data extracted from the URL.
WiredHome 3:b3e21f3306a1 45 ///
WiredHome 3:b3e21f3306a1 46 /// You can see in main how this page was registered.
WiredHome 3:b3e21f3306a1 47 ///
WiredHome 3:b3e21f3306a1 48 bool SimpleDynamicPage(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount) {
WiredHome 3:b3e21f3306a1 49 char buf[100];
WiredHome 7:72b5df2fac93 50 char remoteIP[16];
WiredHome 3:b3e21f3306a1 51 bool ret = false;
WiredHome 4:178df829d62b 52 HTTPServer::SW_PerformanceData perfData;
WiredHome 3:b3e21f3306a1 53
WiredHome 3:b3e21f3306a1 54 switch (type) {
WiredHome 3:b3e21f3306a1 55 case HTTPServer::SEND_PAGE:
WiredHome 4:178df829d62b 56 if (strcmp("true", svr->GetParameter("Reset")) == 0)
WiredHome 4:178df829d62b 57 svr->ResetPerformanceData();
WiredHome 4:178df829d62b 58 // Send the header
WiredHome 3:b3e21f3306a1 59 svr->header(200, "OK", "Content-Type: text/html\r\n");
WiredHome 4:178df829d62b 60
WiredHome 4:178df829d62b 61 // Send top of the page
WiredHome 3:b3e21f3306a1 62 svr->send("<html><head><title>Dynamic Page</title></head>\r\n");
WiredHome 3:b3e21f3306a1 63 svr->send("<body>\r\n");
WiredHome 3:b3e21f3306a1 64 svr->send("This page was generated dynamically. Create your own name=value pairs on the URL "
WiredHome 3:b3e21f3306a1 65 "which uses the GET method.<br/>\r\n");
WiredHome 4:178df829d62b 66
WiredHome 4:178df829d62b 67 // Show Passed-in parameters
WiredHome 4:178df829d62b 68 svr->send("<h2>Query Parameters:</h2><blockquote>\r\n");
WiredHome 3:b3e21f3306a1 69 sprintf(buf, "%d parameters passed to {%s}:<br/>\r\n", paramcount, path);
WiredHome 3:b3e21f3306a1 70 svr->send(buf);
WiredHome 3:b3e21f3306a1 71 // show each of the parameters passed on the URL
WiredHome 3:b3e21f3306a1 72 for (int i=0; i<paramcount; i++) {
WiredHome 3:b3e21f3306a1 73 sprintf(buf, "%d: %s = %s<br/>\r\n", i, params[i].name, params[i].value);
WiredHome 3:b3e21f3306a1 74 svr->send(buf);
WiredHome 3:b3e21f3306a1 75 }
WiredHome 4:178df829d62b 76 svr->send("</blockquote>\r\n");
WiredHome 4:178df829d62b 77
WiredHome 4:178df829d62b 78 // Show Memory Stats
WiredHome 7:72b5df2fac93 79 svr->send("<h2>System Statistics:</h2><blockquote>\r\n");
WiredHome 7:72b5df2fac93 80 svr->send("<table border='1'><tr><td>Parameter</td><td>Description</td></tr>\r\n");
WiredHome 7:72b5df2fac93 81 sprintf(buf,"<tr><td align='right'>%d</td><td>Free memory</td></tr>\r\n", Free());
WiredHome 7:72b5df2fac93 82 svr->send(buf);
WiredHome 7:72b5df2fac93 83 sprintf(buf,"<tr><td align='right'>%d</td><td>Max Header size</td></tr>\r\n", svr->GetMaxHeaderSize());
WiredHome 4:178df829d62b 84 svr->send(buf);
WiredHome 8:50fc3ddf828a 85 sprintf(buf,"<tr><td align='right'>%3.2f</td><td>Wifly SW version</td></tr>\r\n", svr->GetWifly()->getWiflyVersion());
WiredHome 8:50fc3ddf828a 86 svr->send(buf);
WiredHome 8:50fc3ddf828a 87 sprintf(buf,"<tr><td align='right'>%s</td><td>Wifly Version Information</td></tr>\r\n", svr->GetWifly()->getWiflyVersionString());
WiredHome 4:178df829d62b 88 svr->send(buf);
WiredHome 7:72b5df2fac93 89 svr->GetRemoteAddr(remoteIP, sizeof(remoteIP));
WiredHome 7:72b5df2fac93 90 sprintf(buf,"<tr><td align='right'>%s</td><td>Client IP Address</td></tr>\r\n", remoteIP);
WiredHome 7:72b5df2fac93 91 svr->send(buf);
WiredHome 7:72b5df2fac93 92 svr->send("</table>\r\n");
WiredHome 4:178df829d62b 93 svr->send("</blockquote>\r\n");
WiredHome 4:178df829d62b 94
WiredHome 7:72b5df2fac93 95 // Show Web Server Performance metrics
WiredHome 4:178df829d62b 96 svr->GetPerformanceData(&perfData);
WiredHome 7:72b5df2fac93 97 svr->send("<h2>Web Server Performance Metrics</h2><blockquote>\r\n");
WiredHome 4:178df829d62b 98 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");
WiredHome 4:178df829d62b 99 sprintf(buf, "<tr><td align='right'>%d</td><td align='right'>%d</td><td align='right'>%d</td><td>%s</td></tr>\r\n",
WiredHome 4:178df829d62b 100 (unsigned long)(perfData.Header.TotalTime_us / perfData.Header.Samples),
WiredHome 4:178df829d62b 101 perfData.Header.Samples,
WiredHome 4:178df829d62b 102 (unsigned long)(perfData.Header.MaxTime_us),
WiredHome 4:178df829d62b 103 "Header Response");
WiredHome 4:178df829d62b 104 svr->send(buf);
WiredHome 4:178df829d62b 105 sprintf(buf, "<tr><td align='right'>%d</td><td align='right'>%d</td><td align='right'>%d</td><td>%s</td></tr>\r\n",
WiredHome 4:178df829d62b 106 (unsigned long)(perfData.SendData.TotalTime_us / perfData.SendData.Samples),
WiredHome 4:178df829d62b 107 perfData.SendData.Samples,
WiredHome 4:178df829d62b 108 (unsigned long)(perfData.SendData.MaxTime_us),
WiredHome 4:178df829d62b 109 "SendData Response");
WiredHome 4:178df829d62b 110 svr->send(buf);
WiredHome 7:72b5df2fac93 111 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");
WiredHome 4:178df829d62b 112 svr->send("</table>\r\n");
WiredHome 4:178df829d62b 113 svr->send("</blockquote>\r\n");
WiredHome 4:178df829d62b 114
WiredHome 4:178df829d62b 115 // Send bottom of the page
WiredHome 4:178df829d62b 116 svr->send("<br/><a href='/'>back to main</a></body></html>\r\n");
WiredHome 3:b3e21f3306a1 117 ret = true;
WiredHome 3:b3e21f3306a1 118 break;
WiredHome 3:b3e21f3306a1 119 case HTTPServer::CONTENT_LENGTH_REQUEST:
WiredHome 3:b3e21f3306a1 120 ret = true;
WiredHome 3:b3e21f3306a1 121 break;
WiredHome 3:b3e21f3306a1 122 case HTTPServer::DATA_TRANSFER:
WiredHome 3:b3e21f3306a1 123 ret = true;
WiredHome 3:b3e21f3306a1 124 break;
WiredHome 3:b3e21f3306a1 125 default:
WiredHome 3:b3e21f3306a1 126 ret = false;
WiredHome 3:b3e21f3306a1 127 break;
WiredHome 3:b3e21f3306a1 128 }
WiredHome 3:b3e21f3306a1 129 return ret;
WiredHome 3:b3e21f3306a1 130 }
WiredHome 3:b3e21f3306a1 131
WiredHome 3:b3e21f3306a1 132 /// SimplyDynamicForm
WiredHome 3:b3e21f3306a1 133 ///
WiredHome 3:b3e21f3306a1 134 /// This web page is generated dynamically and fills in part of the information from
WiredHome 3:b3e21f3306a1 135 /// data extracted from the URL. It also generates a web form, and allows you to
WiredHome 3:b3e21f3306a1 136 /// update the data in the form using the POST method.
WiredHome 3:b3e21f3306a1 137 ///
WiredHome 3:b3e21f3306a1 138 /// You can see in main how this page was registered.
WiredHome 3:b3e21f3306a1 139 ///
WiredHome 3:b3e21f3306a1 140 bool SimpleDynamicForm(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount) {
WiredHome 3:b3e21f3306a1 141 char buf[100];
WiredHome 3:b3e21f3306a1 142 bool ret = false;
WiredHome 3:b3e21f3306a1 143
WiredHome 3:b3e21f3306a1 144 switch (type) {
WiredHome 3:b3e21f3306a1 145 case HTTPServer::SEND_PAGE:
WiredHome 3:b3e21f3306a1 146 // set the LEDs based on a passed in parameter.
WiredHome 3:b3e21f3306a1 147 leds = atoi(svr->GetParameter("leds"));
WiredHome 3:b3e21f3306a1 148 // send the header
WiredHome 3:b3e21f3306a1 149 svr->header(200, "OK", svr->GetSupportedType(".htm")); //svr->header(200, "OK", "Content-Type: text/html\r\n");
WiredHome 3:b3e21f3306a1 150 // send some data
WiredHome 3:b3e21f3306a1 151 svr->send("<html><head><title>Dynamic Page</title></head>\r\n");
WiredHome 3:b3e21f3306a1 152 svr->send("<body>\r\n");
WiredHome 3:b3e21f3306a1 153 svr->send("This form was generated dynamically. You can add name=value pairs on the URL "
WiredHome 3:b3e21f3306a1 154 "which will show up in the form, but the form is submitted using POST method.<br/>\r\n");
WiredHome 3:b3e21f3306a1 155 svr->send("Hint: leds=7 turns on 3 of the 4 blue leds on the mbed<br/>\r\n");
WiredHome 3:b3e21f3306a1 156 sprintf(buf, "%d parameters passed to {%s}:<br/>\r\n", paramcount, path);
WiredHome 3:b3e21f3306a1 157 svr->send(buf);
WiredHome 3:b3e21f3306a1 158 // Create a user form for which they can post changes
WiredHome 3:b3e21f3306a1 159 sprintf(buf, "<form method='post' action='%s'>\r\n", path);
WiredHome 3:b3e21f3306a1 160 //sprintf(buf, "<form method='post' enctype='multipart/form-data' action='%s'>\r\n", path); // not supported
WiredHome 3:b3e21f3306a1 161 svr->send(buf);
WiredHome 3:b3e21f3306a1 162 // show the parameters in a nice format
WiredHome 3:b3e21f3306a1 163 svr->send("<table>\r\n");
WiredHome 3:b3e21f3306a1 164 for (int i=0; i<paramcount; i++) {
WiredHome 3:b3e21f3306a1 165 if (strcmp(params[i].name, "InFile") == 0)
WiredHome 3:b3e21f3306a1 166 continue;
WiredHome 3:b3e21f3306a1 167 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);
WiredHome 3:b3e21f3306a1 168 svr->send(buf);
WiredHome 3:b3e21f3306a1 169 }
WiredHome 3:b3e21f3306a1 170 //svr->send("<tr><td>&nbsp;</td><td>File</td><td><input type='file' name='InFile' size='40'></td></tr>\r\n");
WiredHome 3:b3e21f3306a1 171 svr->send("<tr><td>&nbsp;</td><td colspan='2'><input type='submit' value='submit'><input type='reset' value='clear'></td></tr>\r\n");
WiredHome 3:b3e21f3306a1 172 svr->send("</table>\r\n");
WiredHome 3:b3e21f3306a1 173 svr->send("</form>\r\n");
WiredHome 3:b3e21f3306a1 174 // see how we're doing with free memory
WiredHome 4:178df829d62b 175 svr->send("<br/><a href='/'>Back to main</a></body></html>\r\n");
WiredHome 3:b3e21f3306a1 176 ret = true;
WiredHome 3:b3e21f3306a1 177 break;
WiredHome 3:b3e21f3306a1 178 case HTTPServer::CONTENT_LENGTH_REQUEST:
WiredHome 3:b3e21f3306a1 179 ret = true;
WiredHome 3:b3e21f3306a1 180 break;
WiredHome 3:b3e21f3306a1 181 case HTTPServer::DATA_TRANSFER:
WiredHome 3:b3e21f3306a1 182 ret = true;
WiredHome 3:b3e21f3306a1 183 break;
WiredHome 3:b3e21f3306a1 184 default:
WiredHome 3:b3e21f3306a1 185 ret = false;
WiredHome 3:b3e21f3306a1 186 break;
WiredHome 3:b3e21f3306a1 187 }
WiredHome 3:b3e21f3306a1 188 return ret;
WiredHome 3:b3e21f3306a1 189 }
WiredHome 7:72b5df2fac93 190
WiredHome 8:50fc3ddf828a 191