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(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:
/**************************************************************************/ /*! * @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
.
// 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:
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(); }
Page last edited September 14, 2016
Text editor powered by tinymce.