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:
Fri Jan 03 19:57:31 2014 +0000
Revision:
33:41ac99847df8
Parent:
32:34dae3cae1b0
Child:
34:77d0f63f612a
Child:
35:87d3577800dc
Ongoing changes are working on supporting file upload.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
WiredHome 4:178df829d62b 1 /** @file main.cpp contains the main program that a user would write.
WiredHome 4:178df829d62b 2 * see the documentation above "main"
WiredHome 4:178df829d62b 3 */
WiredHome 33:41ac99847df8 4 #include "mbed.h" // ver 75
WiredHome 4:178df829d62b 5
WiredHome 30:4717cc1f970e 6 // Standard components
WiredHome 32:34dae3cae1b0 7 #include "MODSERIAL.h" // ver 32 of Erik Olieman's derivative from Andy Kircham
WiredHome 30:4717cc1f970e 8
WiredHome 4:178df829d62b 9 // My components
WiredHome 29:14c47d31a9dc 10 #include "Utility.h" // a couple of simple helper functions
WiredHome 33:41ac99847df8 11 #include "WiflyInterface.h" // ver 53, derived from mbed official ver 4
WiredHome 32:34dae3cae1b0 12 #include "SW_HTTPServer.h" // ver 31, derived from nweb
WiredHome 12:479ff89c190b 13 #include "Credentials.h" // ver 0, credential manager
WiredHome 10:b0b6da272a7b 14 #include "DynamicPages.h" // my dynamically generated pages
WiredHome 15:1f2b62130ffb 15 #include "SecurePage.h" // my secure pages
WiredHome 29:14c47d31a9dc 16 #include "ServerConfig.h" // various configuration options
WiredHome 26:eea4db568404 17 #include "DynamicFileIn.h" // Upload a file to the server
WiredHome 33:41ac99847df8 18 #include "MSCFileSystem.h" // ver 1, this and SDFileSystem
WiredHome 33:41ac99847df8 19 #include "SDFileSystem.h" // ver 1, this and MSCFileSystem
WiredHome 15:1f2b62130ffb 20
WiredHome 10:b0b6da272a7b 21 #define HTTP_SERVER_PORT 80
WiredHome 4:178df829d62b 22
WiredHome 30:4717cc1f970e 23 #ifdef MODSERIAL_H
WiredHome 30:4717cc1f970e 24 MODSERIAL pc(USBTX, USBRX, "modser");
WiredHome 30:4717cc1f970e 25 #else
WiredHome 4:178df829d62b 26 Serial pc(USBTX, USBRX);
WiredHome 30:4717cc1f970e 27 #endif
WiredHome 30:4717cc1f970e 28
WiredHome 4:178df829d62b 29
WiredHome 14:85c805890454 30 LocalFileSystem local("local"); // some place to hold settings and maybe the static web pages
WiredHome 33:41ac99847df8 31 MSCFileSystem msc("msc"); // Mass Storage on USB
WiredHome 29:14c47d31a9dc 32 SDFileSystem sd(p5, p6, p7, p8, "sd"); // for the static web pages
WiredHome 29:14c47d31a9dc 33 #define WEBROOT "/msc/web"
WiredHome 18:42bb1805c37c 34
WiredHome 32:34dae3cae1b0 35 PwmOut signOfLife(LED1);
WiredHome 13:14c6ce4ced9c 36
WiredHome 18:42bb1805c37c 37 Timer onceinawhile;
WiredHome 15:1f2b62130ffb 38
WiredHome 32:34dae3cae1b0 39 /// ShowSignOfLife
WiredHome 32:34dae3cae1b0 40 ///
WiredHome 32:34dae3cae1b0 41 /// Pulse an LED to indicate a sign of life of the program.
WiredHome 32:34dae3cae1b0 42 /// also has some moderate entertainment value.
WiredHome 32:34dae3cae1b0 43 ///
WiredHome 32:34dae3cae1b0 44 void ShowSignOfLife() {
WiredHome 32:34dae3cae1b0 45 #define PI 3.14159265359
WiredHome 32:34dae3cae1b0 46 static Timer activityTimer;
WiredHome 32:34dae3cae1b0 47 static unsigned int activityStart;
WiredHome 32:34dae3cae1b0 48 static bool init;
WiredHome 32:34dae3cae1b0 49 //static float currentBrightness = 0.0;
WiredHome 32:34dae3cae1b0 50 static int degrees = 0;
WiredHome 32:34dae3cae1b0 51 float v;
WiredHome 32:34dae3cae1b0 52 //static bool rampUp = true;
WiredHome 32:34dae3cae1b0 53
WiredHome 32:34dae3cae1b0 54 if (!init) {
WiredHome 32:34dae3cae1b0 55 activityTimer.start();
WiredHome 32:34dae3cae1b0 56 activityStart = (unsigned int) activityTimer.read_ms();
WiredHome 32:34dae3cae1b0 57 init = true;
WiredHome 32:34dae3cae1b0 58 }
WiredHome 32:34dae3cae1b0 59 if ((unsigned int)activityTimer.read_ms() - activityStart > 20) {
WiredHome 32:34dae3cae1b0 60
WiredHome 32:34dae3cae1b0 61 v = sin(degrees * PI / 180);
WiredHome 32:34dae3cae1b0 62 if (v < 0)
WiredHome 32:34dae3cae1b0 63 v = 0;
WiredHome 32:34dae3cae1b0 64 signOfLife = v;
WiredHome 32:34dae3cae1b0 65 degrees += 2;
WiredHome 32:34dae3cae1b0 66 activityStart = (unsigned int) activityTimer.read_ms();
WiredHome 32:34dae3cae1b0 67 }
WiredHome 32:34dae3cae1b0 68 }
WiredHome 32:34dae3cae1b0 69
WiredHome 4:178df829d62b 70
WiredHome 11:183b3893eb7d 71 // A time-bound hook on startup to permit the user to access the wifly module
WiredHome 11:183b3893eb7d 72 //
WiredHome 11:183b3893eb7d 73 void WiFlyShell(Wifly & wifly, PC & pc)
WiredHome 11:183b3893eb7d 74 {
WiredHome 11:183b3893eb7d 75 Timer userTimer;
WiredHome 12:479ff89c190b 76 const int baudrates[] = {2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400};
WiredHome 11:183b3893eb7d 77 static int i = 0;
WiredHome 11:183b3893eb7d 78
WiredHome 15:1f2b62130ffb 79 pc.printf("Pausing 5 sec for shell access to WiFly (press <enter>).\r\n");
WiredHome 11:183b3893eb7d 80 userTimer.start();
WiredHome 11:183b3893eb7d 81 do {
WiredHome 11:183b3893eb7d 82 if (pc.readable()) {
WiredHome 11:183b3893eb7d 83 bool loop = true;
WiredHome 11:183b3893eb7d 84 int c = pc.getc();
WiredHome 11:183b3893eb7d 85 pc.printf("Shell opened to WiFly module: <esc> to exit,\r\n ctrl-B to reboot, ctrl-C to step baud\r\n");
WiredHome 11:183b3893eb7d 86 while (loop) {
WiredHome 11:183b3893eb7d 87 if (pc.readable()) {
WiredHome 11:183b3893eb7d 88 int c = pc.getc();
WiredHome 11:183b3893eb7d 89 switch (c) {
WiredHome 11:183b3893eb7d 90 case '\x1B': // <ESC>
WiredHome 11:183b3893eb7d 91 loop = false;
WiredHome 11:183b3893eb7d 92 break;
WiredHome 11:183b3893eb7d 93 case '\x02': // Ctrl-B
WiredHome 11:183b3893eb7d 94 wifly.reboot();
WiredHome 11:183b3893eb7d 95 break;
WiredHome 11:183b3893eb7d 96 case '\x03': // Ctrl-C
WiredHome 11:183b3893eb7d 97 pc.printf("Setting to %d baud.\r\n", baudrates[i]);
WiredHome 11:183b3893eb7d 98 wifly.baud(baudrates[i]);
WiredHome 11:183b3893eb7d 99 i++;
WiredHome 11:183b3893eb7d 100 if (i >= (sizeof(baudrates)/sizeof(baudrates[0])))
WiredHome 11:183b3893eb7d 101 i = 0;
WiredHome 11:183b3893eb7d 102 break;
WiredHome 11:183b3893eb7d 103 default:
WiredHome 11:183b3893eb7d 104 wifly.putc(c);
WiredHome 11:183b3893eb7d 105 break;
WiredHome 11:183b3893eb7d 106 }
WiredHome 11:183b3893eb7d 107 }
WiredHome 11:183b3893eb7d 108 if (wifly.readable())
WiredHome 11:183b3893eb7d 109 pc.putc(wifly.getc());
WiredHome 11:183b3893eb7d 110 }
WiredHome 11:183b3893eb7d 111 }
WiredHome 11:183b3893eb7d 112 } while (userTimer.read_ms() < 5000);
WiredHome 15:1f2b62130ffb 113 pc.printf(" WiFly shell closed.\r\n");
WiredHome 11:183b3893eb7d 114 }
WiredHome 11:183b3893eb7d 115
WiredHome 11:183b3893eb7d 116
WiredHome 11:183b3893eb7d 117
WiredHome 11:183b3893eb7d 118 /// Smart-WiFly-WebServer is the creation of a web server using a WiFly module.
WiredHome 10:b0b6da272a7b 119 ///
WiredHome 4:178df829d62b 120 /// This is a working version, but it is not using the standardized wifly
WiredHome 4:178df829d62b 121 /// library, which would not work for me... I had to make a number
WiredHome 11:183b3893eb7d 122 /// of changes to get it to work well. After trying to minmimize those
WiredHome 11:183b3893eb7d 123 /// changes, additional improvements became more and more clumsy, so
WiredHome 10:b0b6da272a7b 124 /// I have now been working to refactor where it makes sense, even as
WiredHome 11:183b3893eb7d 125 /// it further deviates from the mbed-library version.
WiredHome 4:178df829d62b 126 ///
WiredHome 4:178df829d62b 127 /// I created this because I couldn't find one that worked and wanted to
WiredHome 4:178df829d62b 128 /// learn the WiFly module. There are a lot of possible improvements:
WiredHome 4:178df829d62b 129 /// @li I think I'm not using the Socket interface as fully as I should.
WiredHome 10:b0b6da272a7b 130 /// @li I would like it to be faster (the interface from mbed to wifly is
WiredHome 10:b0b6da272a7b 131 /// limited to 230400 baud before it drops chars. HW handshake could
WiredHome 10:b0b6da272a7b 132 /// improve this, but the HW handshake pins on the LPC1768 are not
WiredHome 33:41ac99847df8 133 /// both brought out.
WiredHome 4:178df829d62b 134 /// @li I would like to integrate this with the rtos.
WiredHome 10:b0b6da272a7b 135 /// @li If a page has multiple components (e.g. images), it appears
WiredHome 4:178df829d62b 136 /// unreliable. It doesn't see the request for the extra component.
WiredHome 33:41ac99847df8 137 /// A poor workaround, for images, is to use a javascript to post-
WiredHome 33:41ac99847df8 138 /// load them. This is fundamentally a constraint of the WiFly module.
WiredHome 4:178df829d62b 139 ///
WiredHome 4:178df829d62b 140 /// history:
WiredHome 4:178df829d62b 141 /// @li 20130602 added .txt to the supported types (e.g. robots.txt), so
WiredHome 4:178df829d62b 142 /// revised the credentials to .crd, which is an unsupported type
WiredHome 10:b0b6da272a7b 143 /// therefore won't be delivered to the user.
WiredHome 4:178df829d62b 144 ///
WiredHome 4:178df829d62b 145 /// @note Copyright &copy; 2013 by Smartware Computing, all rights reserved.
WiredHome 4:178df829d62b 146 /// Individuals may use this application for evaluation or non-commercial
WiredHome 4:178df829d62b 147 /// purposes. Within this restriction, changes may be made to this application
WiredHome 4:178df829d62b 148 /// as long as this copyright notice is retained. The user shall make
WiredHome 4:178df829d62b 149 /// clear that their work is a derived work, and not the original.
WiredHome 4:178df829d62b 150 /// Users of this application and sources accept this application "as is" and
WiredHome 4:178df829d62b 151 /// shall hold harmless Smartware Computing, for any undesired results while
WiredHome 4:178df829d62b 152 /// using this application - whether real or imagined.
WiredHome 4:178df829d62b 153 ///
WiredHome 4:178df829d62b 154 /// @author David Smart, Smartware Computing
WiredHome 4:178df829d62b 155 ///
WiredHome 10:b0b6da272a7b 156 int main()
WiredHome 10:b0b6da272a7b 157 {
WiredHome 4:178df829d62b 158 credentials cr; // handles the credentials
WiredHome 10:b0b6da272a7b 159
WiredHome 33:41ac99847df8 160 pc.baud(921600); // I like a snappy terminal, so crank it up!
WiredHome 30:4717cc1f970e 161 pc.claim(); // capture stdout
WiredHome 14:85c805890454 162 pc.printf("\r\nSmart WiFly - Build " __DATE__ " " __TIME__ "\r\n");
WiredHome 4:178df829d62b 163
WiredHome 4:178df829d62b 164 if (!ReadCredentials("/local/user.crd", &cr)) {
WiredHome 4:178df829d62b 165 pc.printf("**** ERROR, no /local/user.crd file was found. ****\r\n");
WiredHome 8:50fc3ddf828a 166 wait(1.0);
WiredHome 15:1f2b62130ffb 167 error(" Waiting for user to fix this problem. \r\n"); // flash and die
WiredHome 4:178df829d62b 168 }
WiredHome 10:b0b6da272a7b 169
WiredHome 33:41ac99847df8 170 WiflyInterface wifly(p28, p27, p23, p24, cr.ssid, cr.pass, WPA); // Instantiate the WiflyInterface
WiredHome 33:41ac99847df8 171 FreeCredentials(&cr); // Release the credentials, which also secures them
WiredHome 4:178df829d62b 172
WiredHome 15:1f2b62130ffb 173 // Bring the WiFly interface online
WiredHome 11:183b3893eb7d 174 do {
WiredHome 10:b0b6da272a7b 175 wifly.init(); // start it up as a client of my network using DHCP
WiredHome 33:41ac99847df8 176 wifly.baud(921600); // Only 230K w/o HW flow control
WiredHome 10:b0b6da272a7b 177 if (wifly.connect())
WiredHome 10:b0b6da272a7b 178 break;
WiredHome 10:b0b6da272a7b 179 pc.printf(" Failed to connect, retrying...\r\n");
WiredHome 10:b0b6da272a7b 180 wait(1.0);
WiredHome 4:178df829d62b 181 wifly.reset();
WiredHome 10:b0b6da272a7b 182 } while (1);
WiredHome 11:183b3893eb7d 183
WiredHome 15:1f2b62130ffb 184 WiFlyShell(wifly, pc); // 5 second access at startup - in case you need it.
WiredHome 4:178df829d62b 185
WiredHome 12:479ff89c190b 186 // Now let's instantiate the web server - along with a few settings:
WiredHome 12:479ff89c190b 187 // the Wifly object, the port of interest (typically 80),
WiredHome 12:479ff89c190b 188 // file system path to the static pages,
WiredHome 12:479ff89c190b 189 // the maximum parameters per transaction (in the query string),
WiredHome 12:479ff89c190b 190 // the maximum number of dynamic pages that can be registered,
WiredHome 12:479ff89c190b 191 // the serial port back thru USB (for development/logging)
WiredHome 29:14c47d31a9dc 192 HTTPServer svr(&wifly, HTTP_SERVER_PORT, WEBROOT, 15, 30, 10, &pc);
WiredHome 10:b0b6da272a7b 193
WiredHome 4:178df829d62b 194 // But for even more fun, I'm registering a few dynamic pages
WiredHome 15:1f2b62130ffb 195 // You see the handlers for in DynamicPages.cpp.
WiredHome 4:178df829d62b 196 // Here you can see the path to place on the URL.
WiredHome 4:178df829d62b 197 // ex. http://192.168.1.140/dyn
WiredHome 4:178df829d62b 198 svr.RegisterHandler("/dyn", SuperSimpleDynamicPage);
WiredHome 4:178df829d62b 199 svr.RegisterHandler("/dyn1", SimpleDynamicPage);
WiredHome 4:178df829d62b 200 svr.RegisterHandler("/dyn2", SimpleDynamicForm);
WiredHome 14:85c805890454 201 svr.RegisterHandler("/pass", SimpleSecurityCheck);
WiredHome 14:85c805890454 202 svr.RegisterHandler("/config", ServerConfig);
WiredHome 26:eea4db568404 203 svr.RegisterHandler("/FileIn", DynamicFileIn);
WiredHome 33:41ac99847df8 204
WiredHome 4:178df829d62b 205 // Let the human know it is ready - if they are watching
WiredHome 32:34dae3cae1b0 206 pc.printf("Connect to this server at http://%s:%d\r\n", wifly.getIPAddress(), HTTP_SERVER_PORT);
WiredHome 13:14c6ce4ced9c 207
WiredHome 18:42bb1805c37c 208 onceinawhile.start();
WiredHome 10:b0b6da272a7b 209 while (true) {
WiredHome 18:42bb1805c37c 210 if (onceinawhile.read_ms() >= 200)
WiredHome 18:42bb1805c37c 211 {
WiredHome 18:42bb1805c37c 212 onceinawhile.reset();
WiredHome 18:42bb1805c37c 213 //pc.printf("Largest free mem block is %d\r\n", Free());
WiredHome 32:34dae3cae1b0 214 ShowSignOfLife();
WiredHome 18:42bb1805c37c 215 }
WiredHome 33:41ac99847df8 216 svr.Poll(); // non-blocking, but also not deterministic
WiredHome 12:479ff89c190b 217 if (pc.readable())
WiredHome 33:41ac99847df8 218 WiFlyShell(wifly, pc); // allow shell access at runtime (if user taps a key)
WiredHome 10:b0b6da272a7b 219 }
WiredHome 4:178df829d62b 220 }
WiredHome 24:849cbe0b36ac 221