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 Jul 01 04:38:45 2013 +0000
Revision:
10:b0b6da272a7b
Parent:
8:50fc3ddf828a
Child:
11:183b3893eb7d
Small changes while working the underlying wifly interface.

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 4:178df829d62b 4 #include "mbed.h" // ver 63
WiredHome 10:b0b6da272a7b 5 //#include "SDFileSystem.h" // ver 2, standard
WiredHome 4:178df829d62b 6
WiredHome 4:178df829d62b 7 // Modified standard components
WiredHome 4:178df829d62b 8 #include "WiflyInterface.h" // ver 4+, derived from version 4 with my mods
WiredHome 4:178df829d62b 9
WiredHome 4:178df829d62b 10 // My components
WiredHome 4:178df829d62b 11 #include "SW_HTTPServer.h" // ver 0, the initial release.
WiredHome 10:b0b6da272a7b 12 #include "DynamicPages.h" // my dynamically generated pages
WiredHome 10:b0b6da272a7b 13 #define HTTP_SERVER_PORT 80
WiredHome 4:178df829d62b 14
WiredHome 10:b0b6da272a7b 15 #include "Utility.h"
WiredHome 4:178df829d62b 16
WiredHome 8:50fc3ddf828a 17 extern "C" void mbed_reset();
WiredHome 8:50fc3ddf828a 18
WiredHome 4:178df829d62b 19 Serial pc(USBTX, USBRX);
WiredHome 4:178df829d62b 20
WiredHome 4:178df829d62b 21 LocalFileSystem local("local"); // some place to hold settings and maybe he static web pages
WiredHome 4:178df829d62b 22 //SDFileSystem sd(p5, p6, p7, p8, "sd"); // for the static web pages
WiredHome 4:178df829d62b 23
WiredHome 4:178df829d62b 24
WiredHome 4:178df829d62b 25 /// This section helps manage the credentials. I don't like the examples
WiredHome 10:b0b6da272a7b 26 /// that show clear text credentials, even if they are simply the
WiredHome 4:178df829d62b 27 /// access point ssid and passphrase. So, I created this to let me
WiredHome 4:178df829d62b 28 /// edit a simple file on the file system and put them in there.
WiredHome 4:178df829d62b 29 /// Marginally more secure, but at least not in the source code that
WiredHome 4:178df829d62b 30 /// I might inadvertently share with others.
WiredHome 4:178df829d62b 31 ///
WiredHome 10:b0b6da272a7b 32 typedef struct {
WiredHome 4:178df829d62b 33 char * ssid;
WiredHome 4:178df829d62b 34 char * pass;
WiredHome 4:178df829d62b 35 } credentials;
WiredHome 4:178df829d62b 36
WiredHome 4:178df829d62b 37 #define CREDENTIAL_SIZE 50
WiredHome 4:178df829d62b 38 bool ReadCredentials(char * file, credentials * cr)
WiredHome 4:178df829d62b 39 {
WiredHome 4:178df829d62b 40 FILE *fp = fopen(file, "r");
WiredHome 10:b0b6da272a7b 41
WiredHome 4:178df829d62b 42 cr->ssid = (char *)malloc(CREDENTIAL_SIZE);
WiredHome 4:178df829d62b 43 cr->pass = (char *)malloc(CREDENTIAL_SIZE);
WiredHome 4:178df829d62b 44 if (fp) {
WiredHome 4:178df829d62b 45 fgets(cr->ssid, CREDENTIAL_SIZE, fp);
WiredHome 4:178df829d62b 46 fgets(cr->pass, CREDENTIAL_SIZE, fp);
WiredHome 4:178df829d62b 47 fclose(fp);
WiredHome 4:178df829d62b 48 RTrim(cr->ssid);
WiredHome 4:178df829d62b 49 RTrim(cr->pass);
WiredHome 4:178df829d62b 50 return true;
WiredHome 4:178df829d62b 51 } else {
WiredHome 4:178df829d62b 52 return false;
WiredHome 4:178df829d62b 53 }
WiredHome 4:178df829d62b 54 }
WiredHome 4:178df829d62b 55
WiredHome 4:178df829d62b 56 void FreeCredentials(credentials * cr)
WiredHome 4:178df829d62b 57 {
WiredHome 4:178df829d62b 58 // first secure them by wiping them out
WiredHome 4:178df829d62b 59 for (int i=0; i<CREDENTIAL_SIZE; i++) {
WiredHome 4:178df829d62b 60 cr->ssid[i] = cr->pass[i] = '*';
WiredHome 4:178df829d62b 61 }
WiredHome 4:178df829d62b 62 // then free the memory
WiredHome 4:178df829d62b 63 free(cr->ssid);
WiredHome 4:178df829d62b 64 free(cr->pass);
WiredHome 4:178df829d62b 65 }
WiredHome 4:178df829d62b 66
WiredHome 4:178df829d62b 67
WiredHome 4:178df829d62b 68 /// Smart-WiFly is a very primitive server using WiFly.
WiredHome 10:b0b6da272a7b 69 ///
WiredHome 4:178df829d62b 70 /// This is a working version, but it is not using the standardized wifly
WiredHome 4:178df829d62b 71 /// library, which would not work for me... I had to make a number
WiredHome 10:b0b6da272a7b 72 /// of changes to get it to work well. After trying to minmimize those
WiredHome 10:b0b6da272a7b 73 /// changes, additional improvements become more and more clumsy, so
WiredHome 10:b0b6da272a7b 74 /// I have now been working to refactor where it makes sense, even as
WiredHome 10:b0b6da272a7b 75 /// it further deviates from the mbed library version.
WiredHome 4:178df829d62b 76 ///
WiredHome 4:178df829d62b 77 /// I created this because I couldn't find one that worked and wanted to
WiredHome 4:178df829d62b 78 /// learn the WiFly module. There are a lot of possible improvements:
WiredHome 4:178df829d62b 79 /// @li I think I'm not using the Socket interface as fully as I should.
WiredHome 10:b0b6da272a7b 80 /// @li I would like it to be faster (the interface from mbed to wifly is
WiredHome 10:b0b6da272a7b 81 /// limited to 230400 baud before it drops chars. HW handshake could
WiredHome 10:b0b6da272a7b 82 /// improve this, but the HW handshake pins on the LPC1768 are not
WiredHome 10:b0b6da272a7b 83 /// brought out.
WiredHome 4:178df829d62b 84 /// @li I would like to integrate this with the rtos.
WiredHome 10:b0b6da272a7b 85 /// @li If a page has multiple components (e.g. images), it appears
WiredHome 4:178df829d62b 86 /// unreliable. It doesn't see the request for the extra component.
WiredHome 4:178df829d62b 87 ///
WiredHome 4:178df829d62b 88 /// history:
WiredHome 4:178df829d62b 89 /// @li 20130602 added .txt to the supported types (e.g. robots.txt), so
WiredHome 4:178df829d62b 90 /// revised the credentials to .crd, which is an unsupported type
WiredHome 10:b0b6da272a7b 91 /// therefore won't be delivered to the user.
WiredHome 4:178df829d62b 92 ///
WiredHome 4:178df829d62b 93 /// @note Copyright &copy; 2013 by Smartware Computing, all rights reserved.
WiredHome 4:178df829d62b 94 /// Individuals may use this application for evaluation or non-commercial
WiredHome 4:178df829d62b 95 /// purposes. Within this restriction, changes may be made to this application
WiredHome 4:178df829d62b 96 /// as long as this copyright notice is retained. The user shall make
WiredHome 4:178df829d62b 97 /// clear that their work is a derived work, and not the original.
WiredHome 4:178df829d62b 98 /// Users of this application and sources accept this application "as is" and
WiredHome 4:178df829d62b 99 /// shall hold harmless Smartware Computing, for any undesired results while
WiredHome 4:178df829d62b 100 /// using this application - whether real or imagined.
WiredHome 4:178df829d62b 101 ///
WiredHome 4:178df829d62b 102 /// @author David Smart, Smartware Computing
WiredHome 4:178df829d62b 103 ///
WiredHome 10:b0b6da272a7b 104 int main()
WiredHome 10:b0b6da272a7b 105 {
WiredHome 4:178df829d62b 106 credentials cr; // handles the credentials
WiredHome 10:b0b6da272a7b 107
WiredHome 4:178df829d62b 108 pc.baud(460800); // I like a snappy terminal, so crank it up!
WiredHome 10:b0b6da272a7b 109
WiredHome 4:178df829d62b 110 pc.printf("\r\nSmart WiFly 4 - build " __DATE__ " " __TIME__ "\r\n");
WiredHome 4:178df829d62b 111
WiredHome 4:178df829d62b 112 if (!ReadCredentials("/local/user.crd", &cr)) {
WiredHome 4:178df829d62b 113 pc.printf("**** ERROR, no /local/user.crd file was found. ****\r\n");
WiredHome 10:b0b6da272a7b 114 pc.printf(" Nothing else I can do until you fix this.\r\n");
WiredHome 8:50fc3ddf828a 115 wait(1.0);
WiredHome 10:b0b6da272a7b 116 error(" Waiting for user to fix this problem. \r\n"); // flash and die
WiredHome 4:178df829d62b 117 }
WiredHome 10:b0b6da272a7b 118
WiredHome 4:178df829d62b 119 // instantiate the WiflyInterface, then release the credentials
WiredHome 4:178df829d62b 120 WiflyInterface wifly(p28, p27, p23, p24, cr.ssid, cr.pass, WPA);
WiredHome 4:178df829d62b 121 FreeCredentials(&cr);
WiredHome 4:178df829d62b 122
WiredHome 10:b0b6da272a7b 123 do
WiredHome 10:b0b6da272a7b 124 {
WiredHome 10:b0b6da272a7b 125 wifly.init(); // start it up as a client of my network using DHCP
WiredHome 10:b0b6da272a7b 126 wifly.baud(230400); // Only 230K w/o HW flow control
WiredHome 10:b0b6da272a7b 127 if (wifly.connect())
WiredHome 10:b0b6da272a7b 128 break;
WiredHome 10:b0b6da272a7b 129 pc.printf(" Failed to connect, retrying...\r\n");
WiredHome 10:b0b6da272a7b 130 wait(1.0);
WiredHome 4:178df829d62b 131 wifly.reset();
WiredHome 10:b0b6da272a7b 132 wifly.init(); // start it up as a client of my network using DHCP
WiredHome 10:b0b6da272a7b 133 wifly.baud(230400); // Only 230K w/o HW flow control
WiredHome 10:b0b6da272a7b 134 } while (1);
WiredHome 10:b0b6da272a7b 135 printf("Connect to this server at http://%s\r\n", wifly.getIPAddress());
WiredHome 4:178df829d62b 136
WiredHome 4:178df829d62b 137 // Now let's instantiate the web server - along with a few settings
WiredHome 4:178df829d62b 138 // among which is the file system path to the static pages.
WiredHome 4:178df829d62b 139 HTTPServer svr(&wifly, HTTP_SERVER_PORT, "/local/", 30, 10, &pc);
WiredHome 10:b0b6da272a7b 140
WiredHome 4:178df829d62b 141 // But for even more fun, I'm registering a few dynamic pages
WiredHome 10:b0b6da272a7b 142 // which you see the handlers for in DynamicPages.cpp.
WiredHome 4:178df829d62b 143 // Here you can see the path to place on the URL.
WiredHome 4:178df829d62b 144 // ex. http://192.168.1.140/dyn
WiredHome 4:178df829d62b 145 svr.RegisterHandler("/dyn", SuperSimpleDynamicPage);
WiredHome 4:178df829d62b 146 svr.RegisterHandler("/dyn1", SimpleDynamicPage);
WiredHome 4:178df829d62b 147 svr.RegisterHandler("/dyn2", SimpleDynamicForm);
WiredHome 10:b0b6da272a7b 148
WiredHome 4:178df829d62b 149 // Let the human know it is ready - if they are watching
WiredHome 4:178df829d62b 150 pc.printf("Waiting for a connection...\r\n");
WiredHome 10:b0b6da272a7b 151 while (true) {
WiredHome 4:178df829d62b 152 svr.Poll(); // unfortunately, this is a blocking process
WiredHome 4:178df829d62b 153 //pc.printf("this %d, longest %d\r\n", thisRun, longestRun);
WiredHome 10:b0b6da272a7b 154 }
WiredHome 4:178df829d62b 155 }