AdafruitHTTPServer

The AdafruitHTTPServer class requires WICED Feather Lib 0.6.0 or higher to run.

AdafruitHTTPServer makes it easy to run an HTTP server on the WICED feather in either SoftAP or normal operating mode, allowing you to implement custom admin consoles, rich data visualisations, or to publish 'always available' documention for your project right on the board itself.

The helper class allows you to serve static content stored in flash memory (compiled as part of the Arduino sketch itself), link to files on the 16MBit SPI flash on the WICED Feather (if enabled via the optional solder jumper on the bottom of the board), or to dynamically generate page content on to go.

AdafruitHTTPServer API

The AdafruitHTTPServer class has the following public functions:

AdafruitHTTPServer(uint8_t max_pages, uint8_t interface = WIFI_INTERFACE_STATION);

uint8_t interface ( void );

void addPages(HTTPPage const* http_pages, uint8_t count = 1);

bool begin(uint16_t port,
           uint8_t  max_clients, 
           uint32_t stacksize = HTTPSERVER_STACKSIZE_DEFAULT);

void stop(void);

bool started(void);

Dynamic page content can be generated with the following callback handler signature, changing the function name to something appropriate:

void dynamic_page_generator (const char* url, 
                             const char* query, 
                             httppage_request_t* http_request);

Constructor

When declaring a new instance of the AdafruitHTTPServer class you must declare the maximum number of pages that the server will host (based on available memory since each page record will require a chunk of SRAM to be allocated), and whether the server in running in normal (non access point) mode, or in AP mode.

You indicate the operating mode via the 'interface' field, which has one of the following values:

  • WIFI_INTERFACE_STATION : Default value, meaning this should run in normal non AP mode
  • WIFI_INTERFACE_AP : Indicates that the HTTP server should run on the AP (Access Point) interface

For example, to use the default (non AP) interface for the HTTP server you might use the following constructor declaration:

const char hello_html[] = "<html><body> <h1>Hello World!</h1> </body></html>";

HTTPPage pages[] = 
{
  HTTPPageRedirect("/", "/hello.html"), // Redirect root to hello page
  HTTPPage("/hello.html", HTTP_MIME_TEXT_HTML, hello_html),
};

uint8_t pagecount = sizeof(pages)/sizeof(HTTPPage);

// Declare HTTPServer with max number of pages
AdafruitHTTPServer httpserver(pagecount);

Adding Pages

All pages served by the HTTP server must be declared at compile time in a specifically formatted list made up of the following record types:

1. HTTPPageRedirect Records (Page Redirection Entries)

An HTTPPageRedirect entry redirects all requests for the specified resource to another location, and contains a string with the page to redirect from and the page to redirect to.

2. HTTPPage Records (Standard Pages)

An HTTPPage is composed of the page path + name, the mime type string (so that the browser knows how to render the resource), and the reference to the resource itself, which can be one of the following:

  • A Raw String : The text contained in the specified string will be served as the page contents
  • An HTTPResource (Static File) : The variable name for the binary contents of a file, converted using the pyresource tool. This tool takes binary or text files, and converts them to standard C headers, with the file contents added as an HTTPResource that AdafruitHTTPServer understands. This allows you to insert static pages, images or other file types, and the mime type will be used to indicate how the resource should be rendered in the browser.
  • A Dynamic Callback Handler : The specified callback handler function will be called when this resource is requested, and you can generate the page contents dynamically in the callback handler
  • An SPI Flash Filename : The path and filename to retrieve a file from on the on board SPI flash if enabled (files can be added to SPI flash over USB mass storage when the SPI flash is enabled via the optional solder jumper on the bottom of the board).

A sample list of a well formatted page list can be seen below, where raw string data ('hello_html'), and dynamic content ('info_html_generator' and 'file_not_found_generator') are both present, as well as a redirection of root ('/') to '/hello.html':

void info_html_generator      (const char* url, const char* query, httppage_request_t* http_request);
void file_not_found_generator (const char* url, const char* query, httppage_request_t* http_request);

const char hello_html[] = "<html><body> <h1>Hello World!</h1> </body></html>";

HTTPPage pages[] = 
{
  HTTPPageRedirect("/", "/hello.html"), // redirect root to hello page
  HTTPPage("/hello.html", HTTP_MIME_TEXT_HTML, hello_html),
  HTTPPage("/info.html" , HTTP_MIME_TEXT_HTML, info_html_generator),
  HTTPPage("/404.html" , HTTP_MIME_TEXT_HTML, file_not_found_generator),
};

# Note that we need to indicate the page count in the constructor!
// Declare HTTPServer with max number of pages
uint8_t pagecount = sizeof(pages)/sizeof(HTTPPage);
AdafruitHTTPServer httpserver(pagecount);

Converting Static Content (HTTPResources)

It's easy to convert a set of static files to resources that AdafruitHTTPServer can use and embed in the sketch itself. For details see the dedicated pyresource tool page.

Implementing Dynamic Page Handlers

Two of the HTTPPage entries in the example above ('/info.html' and '/404.html') show how dynamic pages can be added to the HTTP server.

The dynamic page function prototypes are declared at the top of the code above, and the functions can then be implemented following the example below, which is called when a 404 error occurs:

The HTTP Server will always redirect to ´/404.html´ when a 404 error occurs (meaning a user requested a URL that is not available in the HTTPPage list included at compile time). As such, it is a good idea to always include this page in your project.
/**************************************************************************/
/*!
 * @brief  HTTP 404 generator. The HTTP Server will automatically redirect
 *         to "/404.html" when it can't find the requested url in the
 *         list of registered pages
 *
 * The url and query string are already separated when this function
 * is called.
 *
 * @param url           url of this page
 * @param query         query string after '?' e.g "var=value"
 * @param http_request  Details about this HTTP request
*/
/**************************************************************************/
void file_not_found_generator (const char* url, const char* query, httppage_request_t* http_request)
{
  (void) url;
  (void) query;
  (void) http_request;

  httpserver.print("<html><body>");
  httpserver.print("<h1>Error 404 File Not Found!</h1>");
  httpserver.print("<br>");
  
  httpserver.print("Available pages are:");
  httpserver.print("<br>");
  
  // Show a link list of all available pages:
  httpserver.print("<ul>");
  for(int i=0; i<pagecount; i++)
  {
    httpserver.print("<li>");
    httpserver.print(pages[i].url);
    httpserver.print("</li>");
  }
  httpserver.print("</ul>");
  
  httpserver.print("</body></html>");
}

Registering the Pages

Once you have create your file list and implemented any dynamic page handlers, you must register your page list with the class via .addPages.

You must call the .addPages function BEFORE calling the .begin function which starts the HTTP server!
.addPages can be called multiple times before .begin if you wish to organize your page list into several sets, but be sure that the 'max_pages' value used in the constructor is big enough to accommodate all the pages.
// Configure HTTP Server Pages
Serial.println("Adding Pages to HTTP Server");
httpserver.addPages(pages, pagecount);

Serial.print("Starting HTTP Server ... ");
httpserver.begin(PORT, MAX_CLIENTS);
Serial.println(" running");

Starting/Stopping the HTTP Server

You can start the HTTP server using the .begin function (and stop it via .stop), with the following function signatures:

Make sure you call the .addPages function BEFORE calling the .begin function which starts the HTTP server!
bool begin(uint16_t port,
           uint8_t  max_clients, 
           uint32_t stacksize = HTTPSERVER_STACKSIZE_DEFAULT);

void stop(void);
  • port: The port number to expose the HTTP server on (generally 80 or 8080, but this can be any port you wish and you can even have multiple instances of the HTTP server running on different ports if you wish).
  • max_clients: The maximum number of client connections to accept before refusing requests. This should generally be kept as low as possible since there is limited SRAM available on the system. 3 is a good number if there will be multiple file requests at once, for example.
  • stacksize: This should generally be left at the default value, but if you require a larger stack for the HTTP server you can adjust the value here within the limit of available system resources.

Complete Example

The following code shows an example using the AdafruitHTTPServer class, but numerous examples are included as part of the library in the HTTPServer folder, and the latter may be more up to date.

To use this example, update the WLAN_SSID and WLAD_PASS fields, flash the sketch to the User Code section of your WICED Feather, and then open the Serial Monitor and wait for the connection to finish.  Once connected, the HTTP server will start and you can navigate to the IP address of your board to browse the pages added below.

/* This example uses the AdafruitHTTPServer class to create a simple webserver */
 
#include <adafruit_feather.h>
#include <adafruit_http_server.h>

#define WLAN_SSID            "yourSSID"
#define WLAN_PASS            "yourPassword"

#define PORT                 80            // The TCP port to use
#define MAX_CLIENTS          3

int ledPin = PA15;
int visit_count = 0;

void info_html_generator      (const char* url, const char* query, httppage_request_t* http_request);
void file_not_found_generator (const char* url, const char* query, httppage_request_t* http_request);

const char hello_html[] = "<html><body> <h1>Hello World!</h1> </body></html>";

HTTPPage pages[] = 
{
  HTTPPageRedirect("/", "/hello.html"), // redirect root to hello page
  HTTPPage("/hello.html", HTTP_MIME_TEXT_HTML, hello_html),
  HTTPPage("/info.html" , HTTP_MIME_TEXT_HTML, info_html_generator),
  HTTPPage("/404.html" , HTTP_MIME_TEXT_HTML, file_not_found_generator),
};

uint8_t pagecount = sizeof(pages)/sizeof(HTTPPage);

// Declare HTTPServer with max number of pages
AdafruitHTTPServer httpserver(pagecount);

/**************************************************************************/
/*!
 * @brief  Example of generating dynamic HTML content on demand
 *
 * Link is separated to url and query
 *
 * @param url           url of this page
 * @param query         query string after '?' e.g "var=value"
 *
 * @param http_request  This request's information
*/
/**************************************************************************/
void info_html_generator (const char* url, const char* query, httppage_request_t* http_request)
{
  (void) url;
  (void) query;
  (void) http_request;

  httpserver.print("<b>Bootloader</b> : ");
  httpserver.print( Feather.bootloaderVersion() );
  httpserver.print("<br>");

  httpserver.print("<b>WICED SDK</b> : ");
  httpserver.print( Feather.sdkVersion() );
  httpserver.print("<br>");

  httpserver.print("<b>FeatherLib</b> : ");
  httpserver.print( Feather.firmwareVersion() );
  httpserver.print("<br>");

  httpserver.print("<b>Arduino API</b> : "); 
  httpserver.print( Feather.arduinoVersion() );
  httpserver.print("<br>");
  httpserver.print("<br>");

  visit_count++;
  httpserver.print("<b>visit count</b> : ");
  httpserver.print(visit_count);
}

/**************************************************************************/
/*!
 * @brief  HTTP 404 generator. The HTTP Server will automatically redirect
 *         to "/404.html" when it can't find the requested url in the
 *         list of registered pages
 *
 * The url and query string are already separated when this function
 * is called.
 *
 * @param url           url of this page
 * @param query         query string after '?' e.g "var=value"
 * @param http_request  Details about this HTTP request
*/
/**************************************************************************/
void file_not_found_generator (const char* url, const char* query, httppage_request_t* http_request)
{
  (void) url;
  (void) query;
  (void) http_request;

  httpserver.print("<html><body>");
  httpserver.print("<h1>Error 404 File Not Found!</h1>");
  httpserver.print("<br>");
  
  httpserver.print("Available pages are:");
  httpserver.print("<br>");
  
  httpserver.print("<ul>");
  for(int i=0; i<pagecount; i++)
  {
    httpserver.print("<li>");
    httpserver.print(pages[i].url);
    httpserver.print("</li>");
  }
  httpserver.print("</ul>");
  
  httpserver.print("</body></html>");
}

/**************************************************************************/
/*!
    @brief  The setup function runs once when the board comes out of reset
*/
/**************************************************************************/
void setup()
{
  Serial.begin(115200);

  // Wait for the USB serial to connect. Needed for native USB port only.
  while (!Serial) delay(1);

  Serial.println("Simple HTTP Server Example\r\n");
  
  // Print all software versions
  Feather.printVersions();

  // Try to connect to an AP
  while ( !connectAP() )
  {
    delay(500); // delay between each attempt
  }

  // Connected: Print network info
  Feather.printNetwork();

  // Tell the HTTP client to auto print error codes and halt on errors
  httpserver.err_actions(true, true);

  // Configure HTTP Server Pages
  Serial.println("Adding Pages to HTTP Server");
  httpserver.addPages(pages, pagecount);

  Serial.print("Starting HTTP Server ... ");
  httpserver.begin(PORT, MAX_CLIENTS);
  Serial.println(" running");
}

/**************************************************************************/
/*!
    @brief  The loop function runs over and over again
*/
/**************************************************************************/
void loop()
{
  togglePin(ledPin);
  delay(1000);
}

/**************************************************************************/
/*!
    @brief  Connect to the defined access point (AP)
*/
/**************************************************************************/
bool connectAP(void)
{
  // Attempt to connect to an AP
  Serial.print("Please wait while connecting to: '" WLAN_SSID "' ... ");

  if ( Feather.connect(WLAN_SSID, WLAN_PASS) )
  {
    Serial.println("Connected!");
  }
  else
  {
    Serial.printf("Failed! %s (%d)", Feather.errstr(), Feather.errno());
    Serial.println();
  }
  Serial.println();

  return Feather.connected();
}

/**************************************************************************/
/*!
    @brief  TCP/HTTP disconnect callback
*/
/**************************************************************************/
void disconnect_callback(void)
{
  Serial.println();
  Serial.println("---------------------");
  Serial.println("DISCONNECTED CALLBACK");
  Serial.println("---------------------");
  Serial.println();

  httpserver.stop();
}
Last updated on 2016-09-14 at 11.44.15 AM Published on 2016-03-23 at 01.29.37 PM