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:
Sun Jun 02 18:32:39 2013 +0000
Revision:
3:b3e21f3306a1
Parent:
2:5cd3f15ac98b
Child:
4:178df829d62b
Refactored main.cpp to extract the dynamic web page generation into DynamicPages.cpp, .h

Who changed what in which revision?

UserRevisionLine numberNew contents of line
WiredHome 3:b3e21f3306a1 1 /** @file main.cpp contains the main program that a user would write.
WiredHome 3:b3e21f3306a1 2 * see the documentation above "main"
WiredHome 3:b3e21f3306a1 3 */
WiredHome 0:08da60dc31bc 4 #include "mbed.h" // ver 63
WiredHome 0:08da60dc31bc 5 #include "SDFileSystem.h" // ver 2, standard
WiredHome 0:08da60dc31bc 6
WiredHome 0:08da60dc31bc 7 // Modified standard components
WiredHome 0:08da60dc31bc 8 #include "WiflyInterface.h" // ver 4+, derived from version 4 with my mods
WiredHome 0:08da60dc31bc 9
WiredHome 0:08da60dc31bc 10 // My components
WiredHome 0:08da60dc31bc 11 #include "SW_HTTPServer.h" // ver 0, the initial release.
WiredHome 0:08da60dc31bc 12 #include "Utility.h"
WiredHome 0:08da60dc31bc 13
WiredHome 3:b3e21f3306a1 14 #include "DynamicPages.h" // my dynamically generated pages
WiredHome 3:b3e21f3306a1 15
WiredHome 0:08da60dc31bc 16 Serial pc(USBTX, USBRX);
WiredHome 0:08da60dc31bc 17
WiredHome 3:b3e21f3306a1 18 LocalFileSystem local("local"); // some place to hold settings and maybe he static web pages
WiredHome 3:b3e21f3306a1 19 //SDFileSystem sd(p5, p6, p7, p8, "sd"); // for the static web pages
WiredHome 0:08da60dc31bc 20
WiredHome 0:08da60dc31bc 21
WiredHome 2:5cd3f15ac98b 22 /// This section helps manage the credentials. I don't like the examples
WiredHome 2:5cd3f15ac98b 23 /// that show clear text credentials, even if they are simply the
WiredHome 2:5cd3f15ac98b 24 /// access point ssid and passphrase. So, I create this to let me
WiredHome 2:5cd3f15ac98b 25 /// edit a simple file on the file system and put them in there.
WiredHome 2:5cd3f15ac98b 26 /// Marginally more secure, but at least not in the source code that
WiredHome 2:5cd3f15ac98b 27 /// I might share with others.
WiredHome 2:5cd3f15ac98b 28 ///
WiredHome 0:08da60dc31bc 29 typedef struct
WiredHome 0:08da60dc31bc 30 {
WiredHome 0:08da60dc31bc 31 char * ssid;
WiredHome 0:08da60dc31bc 32 char * pass;
WiredHome 0:08da60dc31bc 33 } credentials;
WiredHome 0:08da60dc31bc 34
WiredHome 0:08da60dc31bc 35 #define CREDENTIAL_SIZE 50
WiredHome 0:08da60dc31bc 36 bool ReadCredentials(char * file, credentials * cr)
WiredHome 0:08da60dc31bc 37 {
WiredHome 0:08da60dc31bc 38 FILE *fp = fopen(file, "r");
WiredHome 0:08da60dc31bc 39
WiredHome 0:08da60dc31bc 40 cr->ssid = (char *)malloc(CREDENTIAL_SIZE);
WiredHome 0:08da60dc31bc 41 cr->pass = (char *)malloc(CREDENTIAL_SIZE);
WiredHome 0:08da60dc31bc 42 if (fp) {
WiredHome 0:08da60dc31bc 43 fgets(cr->ssid, CREDENTIAL_SIZE, fp);
WiredHome 0:08da60dc31bc 44 fgets(cr->pass, CREDENTIAL_SIZE, fp);
WiredHome 0:08da60dc31bc 45 fclose(fp);
WiredHome 0:08da60dc31bc 46 RTrim(cr->ssid);
WiredHome 0:08da60dc31bc 47 RTrim(cr->pass);
WiredHome 0:08da60dc31bc 48 return true;
WiredHome 0:08da60dc31bc 49 } else {
WiredHome 0:08da60dc31bc 50 return false;
WiredHome 0:08da60dc31bc 51 }
WiredHome 0:08da60dc31bc 52 }
WiredHome 0:08da60dc31bc 53
WiredHome 0:08da60dc31bc 54 void FreeCredentials(credentials * cr)
WiredHome 0:08da60dc31bc 55 {
WiredHome 0:08da60dc31bc 56 // first secure them by wiping them out
WiredHome 0:08da60dc31bc 57 for (int i=0; i<CREDENTIAL_SIZE; i++) {
WiredHome 0:08da60dc31bc 58 cr->ssid[i] = cr->pass[i] = '*';
WiredHome 0:08da60dc31bc 59 }
WiredHome 0:08da60dc31bc 60 // then free the memory
WiredHome 0:08da60dc31bc 61 free(cr->ssid);
WiredHome 0:08da60dc31bc 62 free(cr->pass);
WiredHome 0:08da60dc31bc 63 }
WiredHome 0:08da60dc31bc 64
WiredHome 2:5cd3f15ac98b 65
WiredHome 3:b3e21f3306a1 66 /// Smart-WiFly-web server is a very primitive server using WiFly.
WiredHome 3:b3e21f3306a1 67 ///
WiredHome 3:b3e21f3306a1 68 /// Working version, but it is not using the standardized wifly
WiredHome 3:b3e21f3306a1 69 /// library, which would not work for me... I had to make a number
WiredHome 3:b3e21f3306a1 70 /// of changes to get it to work well.
WiredHome 3:b3e21f3306a1 71 ///
WiredHome 3:b3e21f3306a1 72 /// @caution If nothing visits its web page for a long time, it seems
WiredHome 3:b3e21f3306a1 73 /// to disable the interface. Perhaps a sleep timer I'm not spotting?
WiredHome 3:b3e21f3306a1 74 ///
WiredHome 3:b3e21f3306a1 75 /// I created this because I couldn't fine one that worked and wanted to
WiredHome 3:b3e21f3306a1 76 /// learn the WiFly module. There are a lot of possible improvements:
WiredHome 3:b3e21f3306a1 77 /// @li I think I'm not using the Socket interface as fully as I should.
WiredHome 3:b3e21f3306a1 78 /// @li I would like it to be non-blocking.
WiredHome 3:b3e21f3306a1 79 /// @li I would like it to be faster (the interface from mbed to wifly is
WiredHome 3:b3e21f3306a1 80 /// limited to 230400 baud before it drops chars.
WiredHome 3:b3e21f3306a1 81 /// @li I would like to integrate this with the rtos.
WiredHome 3:b3e21f3306a1 82 ///
WiredHome 3:b3e21f3306a1 83 /// history:
WiredHome 3:b3e21f3306a1 84 /// @li 20130602 added .txt to the supported types (e.g. robots.txt), so
WiredHome 3:b3e21f3306a1 85 /// revised the credentials to .crd, which is an unsupported type
WiredHome 3:b3e21f3306a1 86 /// so won't be delivered to the user.
WiredHome 3:b3e21f3306a1 87 ///
WiredHome 3:b3e21f3306a1 88 /// @note Copyright &copy; 2013 by Smartware Computing, all rights reserved.
WiredHome 3:b3e21f3306a1 89 /// Individuals may use this application for evaluation or non-commercial
WiredHome 3:b3e21f3306a1 90 /// purposes. Within this restriction, changes may be made to this application
WiredHome 3:b3e21f3306a1 91 /// as long as this copyright notice is retained. The user shall make
WiredHome 3:b3e21f3306a1 92 /// clear that their work is a derived work, and not the original.
WiredHome 3:b3e21f3306a1 93 /// Users of this application and sources accept this application "as is" and
WiredHome 3:b3e21f3306a1 94 /// shall hold harmless Smartware Computing, for any undesired results while
WiredHome 3:b3e21f3306a1 95 /// using this application - whether real or imagined.
WiredHome 3:b3e21f3306a1 96 ///
WiredHome 3:b3e21f3306a1 97 /// @author David Smart, Smartware Computing
WiredHome 2:5cd3f15ac98b 98 ///
WiredHome 0:08da60dc31bc 99 int main() {
WiredHome 2:5cd3f15ac98b 100 credentials cr; // handles the credentials
WiredHome 0:08da60dc31bc 101
WiredHome 2:5cd3f15ac98b 102 pc.baud(460800); // I like a snappy terminal, so crank it up!
WiredHome 2:5cd3f15ac98b 103
WiredHome 0:08da60dc31bc 104 pc.printf("\r\nSmart WiFly 4 - build " __DATE__ " " __TIME__ "\r\n");
WiredHome 0:08da60dc31bc 105
WiredHome 3:b3e21f3306a1 106 if (!ReadCredentials("/local/user.crd", &cr)) {
WiredHome 3:b3e21f3306a1 107 pc.printf("**** ERROR, no /local/user.crd file was found. ****\r\n");
WiredHome 2:5cd3f15ac98b 108 pc.printf(" awaiting watchdog (that means you push the button).\r\n");
WiredHome 0:08da60dc31bc 109 while(1);
WiredHome 0:08da60dc31bc 110 }
WiredHome 2:5cd3f15ac98b 111
WiredHome 2:5cd3f15ac98b 112 // instantiate the WiflyInterface, then release the credentials
WiredHome 0:08da60dc31bc 113 WiflyInterface wifly(p28, p27, p23, p24, cr.ssid, cr.pass, WPA);
WiredHome 0:08da60dc31bc 114 FreeCredentials(&cr);
WiredHome 2:5cd3f15ac98b 115
WiredHome 2:5cd3f15ac98b 116 // start it up as a client of my network
WiredHome 0:08da60dc31bc 117 wifly.init(); // use DHCP
WiredHome 2:5cd3f15ac98b 118
WiredHome 2:5cd3f15ac98b 119 wifly.baud(230400); // I like this fast, but it drops chars at 460800
WiredHome 0:08da60dc31bc 120
WiredHome 0:08da60dc31bc 121 while (!wifly.connect()) {
WiredHome 0:08da60dc31bc 122 printf(" ... failed to connect, retrying.\r\n");
WiredHome 0:08da60dc31bc 123 wifly.reset();
WiredHome 0:08da60dc31bc 124 wait(1.0);
WiredHome 0:08da60dc31bc 125 }
WiredHome 0:08da60dc31bc 126 printf("IP Address is %s\n\r", wifly.getIPAddress());
WiredHome 0:08da60dc31bc 127
WiredHome 2:5cd3f15ac98b 128 // Now let's instantiate the web server - along with a few settings
WiredHome 2:5cd3f15ac98b 129 // among which is the file system path to the static pages.
WiredHome 0:08da60dc31bc 130 #define HTTP_SERVER_PORT 80
WiredHome 3:b3e21f3306a1 131 HTTPServer svr(&wifly, HTTP_SERVER_PORT, "/local/", 30, 10, &pc);
WiredHome 2:5cd3f15ac98b 132
WiredHome 2:5cd3f15ac98b 133 // But for even more fun, I'm registering a couple of dynamic pages
WiredHome 2:5cd3f15ac98b 134 // which you saw the handlers for up above. Here you can see the path
WiredHome 2:5cd3f15ac98b 135 // to place on the URL.
WiredHome 0:08da60dc31bc 136 svr.RegisterHandler("/dyn1", SimpleDynamicPage);
WiredHome 0:08da60dc31bc 137 svr.RegisterHandler("/dyn2", SimpleDynamicForm);
WiredHome 0:08da60dc31bc 138
WiredHome 2:5cd3f15ac98b 139 // Let the human know it is ready - if they are watching
WiredHome 0:08da60dc31bc 140 pc.printf("Waiting for a connection...\r\n");
WiredHome 0:08da60dc31bc 141 while (true)
WiredHome 0:08da60dc31bc 142 {
WiredHome 3:b3e21f3306a1 143 svr.Poll(); // unfortunately, this is a blocking process
WiredHome 0:08da60dc31bc 144 }
WiredHome 0:08da60dc31bc 145 }
WiredHome 0:08da60dc31bc 146