Link here

Setting-up a Dynamic Embedded Web Server

Introduction to the dynamic webserver

The EtherSmart/WiFi Wildcard implements a dynamic webserver that accepts connections from your web browser, serving out static or dynamic web pages to implement web-enabled instrumentation. Such webservers allow you to "browse into" your instrument using a web browser running on an online PC to monitor the status of your instrument.

Most web pages are "static", meaning that their content does not change with each visit to the page. However, in the context of embedded computers, "dynamic" web pages that provide up-to-date status information about the computer’s state (inputs, outputs, memory contents, etc.) are very useful. The pre-coded driver functions enable you to serve both static and dynamic web pages.

By coding web content into your application, you can enable remote monitoring and control of your instrument from your web-connected PC. The "embedded web server" that runs when you execute the EtherSmart/WiFi driver code responds to information requests from your browser. You can create a set of web pages, each with a specified name, or "URL" (Uniform Resource Locator) and an associated "handler function" that serves out the static or dynamic web content for the requested page.

The EtherSmart/WiFi Wildcard’s dynamic webserver should not be confused with the static webserver built into the Lantronix XPort or WiPort that is used for configuration. As described in the "EtherSmart/WiFi Initialization, Configuration and Diagnostics" section, an additional "configuration webserver" is built into the Lantronix device for low-level configuration of the Lantronix firmware, and is available at TCP/IP port 8000. This configuration webserver is typically not used in conjunction with the EtherSmart/WiFi Wildcard, and should not be confused with the dynamic webserver discussed in this section.

 

Web service basics

Most web content is authored using "HTML" which stands for HyperText Markup Language. It is a "tagged" form of ASCII text, in which the tags instruct the web browser how to display the referenced text, data, and images. For example, the HTML line

<b>This is an important statement</b>

presents the text string "This is an important statement" in a bold font. The <b> tag marks the start of the bold text, and the </b> marks the end of the bold text. There are many excellent books and free online tutorials that explain how to "mark up" text into an HTML file that will display a nicely formatted web page in your browser window.

The dynamic webserver is available at the standard TCP/IP port 80 used by web browsers. This port is a "well known port" that is assigned to HTTP (HyperText Transfer Protocol) data exchanges between a web browser (typically running on a PC) and the webserver (in this case running on the EtherSmart Wildcard). HTTP defines the request format that comes from the browser, and the response format that comes from the server. To learn about the HTTP 1.1 standard in all its detail, type "RFC 2616" into Google and read the referenced document.

The primary HTTP "method" command from the browser is the "GET" keyword, and this is the only HTTP method supported by the EtherSmart/WiFi webserver. The GET keyword is followed by a "URL", or Uniform Resource Locator; it can also be referred to as a URI, or Uniform Resource Identifier. The URL is a web address on the target computer. For the purposes of this document, the URL does not include the target computer’s name, IP address or port. The URL starts with the / (forward slash) character and follows rules set out in the HTTP standard. A URL is a web page address as sent by the browser running on your desktop PC to the embedded web server. For the purposes of this document, the URL is the portion of the web address that is in the browser’s address bar after the IP address or computer name. For example, if you have assigned IP address 10.0.1.22 to the EtherSmart/WiFi Wildcard, and you type into your browser’s address bar:

10.0.1.22/index.html

then the URL as defined in this document is

/index.html

Each URL starts with a / character, and is case sensitive. Some URL’s include a "query field" that starts with the ? character and contains fieldname and value fields resulting from "forms" that are filled out by the user in the browser window. The software driver functions make it easy to extract data from these fields to direct the operation of the handler functions. In fact, form data from the browser provides an excellent way for the web interface to give commands to the embedded computer (to take data samples, extract data from memory and report the results to the browser, etc.)

The webserver contains a suite of functions that start with the HTTP_ prefix to implement the dynamic webserver. These functions make it easy to respond to the browser request with a properly formatted "HTTP header" and content. The HTTP header specifies the content type (text/html, image/bitmap, etc.) and whether the referenced page can be "cached" by the browser for fast display after the initial loading. "Static" web pages can be safely cached, as they do not change over time. "Dynamic" web pages, on the other hand, should not be cached because they contain dynamic data that changes with time under the control of the application program.

You configure the webserver by defining and posting a "handler function" for each web address that is implemented. When the browser requests the web address URL, the webserver automatically executes the handler which performs any required actions and transmits the web page to the browser. As described in this section, powerful functions make it easy to use HTML "forms" to request specified data or stimulate particular actions from the Mosaic controller.

Because the web service is running in the Ethernet task, the web requests are handled automatically without the need for intervention by the application task. The Ethernet task’s connection manager accepts the incoming request from the browser, identifies it as a web request by finding the "GET" keyword at the start of the first incoming line, matches the URL to those that have been declared, executes the corresponding handler to serve the requested web page, and closes the connection.

 

Your browser accesses the dynamic webserver

You’ll use a web browser running on your PC to interact with the webserver running on the EtherSmart/WiFi Wildcard. As explained in the "Browser Notes" section above, popular browsers include Microsoft Internet Explorer, Netscape based browsers such as Firefox and Mozilla, and other high quality free browsers such as Opera. All of these browsers work with the web demonstration program that comes with the EtherSmart/WiFi Wildcard (see the demonstration program source code listings in the Appendices).

Additional considerations can limit the performance of some of these browsers if your application needs to serve out more complex web pages that require more than one TCP/IP connection per web page. This can occur, for example, when mixed text and image data originating from the Wildcard are served out in a single web page. The Lantronix hardware on the EtherSmart/WiFi Wildcard supports only one active connection at a time. However, the HTTP/1.1 standard (and consequently all browsers in their default configuration) expect the webserver to be able to host two simultaneous connections. A default-configured browser will try to open a second connection when two or more content types (for example, HTML text and a JPEG image) are present in a single web page. The second connection will typically be refused by the Lantronix hardware, causing an incomplete page load. The solution is to configure the browser to expect only one connection at a time from the webserver.

Appendix F explains how to reconfigure Internet Explorer to work with any web page that the EtherSmart or WiFi Wildcard can serve out. For the best solution, though, consider downloading the free Opera browser from www.opera.com and using it for all your interactions with the EtherSmart/WiFi Wildcard. It’s free, the download file is compact, and the install takes only a few seconds. Once Opera is installed, simply go to its Tools menu, and select:

Preferences->Advanced->Network->Max Connections Per Server

and enter 1 in the box. Now you’re ready to use Opera with the EtherSmart/WiFi Wildcard dynamic webserver.

 

Using the Image Converter to create web resources

The most convenient way to create HTML or text web page strings as well as web images is to use the "Image Converter" program that is part of your Mosaic development environment. This program converts one or more files, each containing a single string, image or other resource, into a named 32-bit xaddress and count that can be used by your application program.

To define a set of text or text/HTML web page as resources that can be passed to the HTTP data output routines such as HTTP_Send_Buffer, simply place each string into a separate file with the file extension .str or .htm or .html. The file name and extension (with the dot replaced by an underscore) will become a part of the symbol name. All files to be converted should be in a single directory. Make sure you use C-compatible filenames that include only alphanumeric and _ (underscore) characters, and do not start with a numeral. Invoke the Image Converter from your development environment. In the Image Converter control panel, select the controller platform, and check the "Web files" box. If you are programming in Forth, click the "Advanced" menu and select Forth as the programming language.

If your web pages reference images that are to be served out by the EtherSmart/WiFi Wildcard itself, simply place files and the image files into the same directory. Each image file must have a valid file extension; the allowed file types are png, gif, or jpg. Note that bitmap (bmp) files are not convertible as web resources by the Image Converter, as bitmap images are reserved for the GUI (Graphical User Interface) toolkit which is also supported by the Image Converter.

In the "Directory" area of the Image Converter control panel, select the directory that contains the specified file(s). Then click on "Convert Files Now". A pair of files named image_data.txt and image_headers.h will be created by the Image Converter. The image_data.txt contains S-records and operating system commands that load the string image into flash memory on the controller. Because flash memory is nonvolatile, you only need to download this file once to the controller; it need not be reloaded until the web pages or images are edited. The image_headers.h file declares the xaddress and count (size) constants to be used by your application program.

For example, let’s say that you have created a HTML "home page" and stored it in a file named home_body.html where the .html file extension indicates that the file contains HTML marked-up text. The image_headers.h file declares the following two macros with the appropriate numeric xaddress and count values:

#define HOME_BODY_HTML_XADDR 0x700360
#define HOME_BODY_HTML_SIZE 0x98E

Of course, the actual numeric xaddress value depends on the controller platform that you have selected and the other resources that are converted.

You can then use these macros as parameters passed to the HTTP_Send_Buffer function to specify the email body string.

Loading Your Program

See Appendix A for a discussion of how to load the resources and the kernel extension library files onto the controller board before loading your custom application program.
 

Using the dynamic webserver

A comprehensive suite of pre-coded driver functions is available to simplify the implementation of an embedded webserver by your application program. Most of these functions begin with the HTTP_ prefix to indicate that they are part of the web service suite. These can be classified as buffer management, HTTP header generation, data transfer, form processing, handler posting, and inter-task service management functions. Each of these is discussed in turn in the context of the constituent functions that implement the Ether_Web_Demo and WiFi_Web_Demo functions from the demo program. You can edit the definition of main in the demo source code file to call the version of the web demo that is appropriate for your Wildcard (EtherSmart or WiFi). Then running the web demo is as easy as typing main in the terminal window.

 

HTTP buffer management

Table 1-11 lists the web service buffer functions. The web service input and output buffers are called HTTP_Inbuf and HTTP_Outbuf. These can be used in any web handler function. The Ether_Connection_Manager routine that is running in the Ethernet task automatically stores the linefeed-delimited first line into the HTTP_Inbuf counted buffer, and examines the line to see if it starts with "GET ". If the first line starts with GET (which is the request keyword issued by a web browser), the connection manager invokes the web server.

Table 1-11 Web Service Buffer Management Functions.
Cat HTTP_Outbuf_Cat
HTTP_Inbuf HTTP_Outbufsize
HTTP_Inbufsize HTTP_OUTBUFSIZE_DEFAULT
HTTP_INBUFSIZE_DEFAULT HTTP_Set_Inbuf
HTTP_Outbuf HTTP_Set_Outbuf

Web connections are typically handled "in the background" without the need for intervention by the application program. This is very convenient, but it implies that dedicated HTTP buffers must be used for web service. Hard to diagnose multitasking errors can result if the HTTP buffers are used to implement other services (such as tunneling or email), or if the Ether_Inbuf or Ether_Outbuf buffers are used to implement web handlers as well as tunneling or email services. In other words, use separate buffers for web service as opposed to other Ethernet services.

Each of the buffers HTTP_Inbuf and HTTP_Outbuf has a default maximum size that is set at initialization time. In this context, the "size" is the maximum number of data bytes (not including the count stored in the first 2 bytes of the buffer) that can fit in the buffer. The default sizes of the HTTP_Inbuf and HTTP_Outbuf are given by the constants HTTP_INBUFSIZE_DEFAULT (254 bytes) and HTTP_OUTBUFSIZE_DEFAULT (1022 bytes) respectively. HTTP_Inbuf can be smaller because it only needs to hold one input line at a time, while HTTP_Outbuf should be able to accommodate multi-line strings to serve out web page content. The functions HTTP_Inbufsize and HTTP_Outbufsize return the sizes of the HTTP_Inbuf and HTTP_Outbuf, respectively. The Cat and HTTP_Outbuf_Cat functions that write to the buffers accept the maximum buffer size as an input parameter, and ensure that they never write beyond the allowed buffer size.

If you need specify a new buffer base xaddress or size that is different from the values set at initialization time, use the functions HTTP_Set_Inbuf and HTTP_Set_Outbuf.

 

HTTP header generation

The HTTP standard specifies that a webserver should respond to a GET request with a properly formatted HTTP header followed by a single blank line followed by the content. This HTTP header should not be confused with the HTML <head> tag that specifies special content within an HTML page. The HTTP header tells the browser about the nature of the content that is about to be served by the embedded webserver. Table 1-12 lists the EtherSmart/WiFi Wildcard functions that make it easy to serve a valid HTTP header.

Table 1-12 HTTP Header Generation Functions.
HTTP_BINARY_DATA_CONTENT HTTP_Put_Headers
HTTP_IMAGE_BITMAP_CONTENT HTTP_JAVASCRIPT_CONTENT
HTTP_IMAGE_GIF_CONTENT HTTP_TEXT_HTML_CONTENT
HTTP_IMAGE_JPG_CONTENT HTTP_TEXT_PLAIN_CONTENT
HTTP_IMAGE_PNG_CONTENT HTTP_TEXT_CSS_CONTENT

The HTTP header specifies the content type (text/html, image/bitmap, etc.) and whether the referenced page can be "cached" by the browser for fast display after the initial loading. "Static" web pages can be safely cached, as they do not change over time. "Dynamic" web pages, on the other hand, should not be cached because they contain dynamic data that changes with time under the control of the application program.

In the demo program, the Opera_Config_Page shown in Listing 1-8 serves out an unchanging HTML web page. The Home_Page function discussed below, on the other hand, serves HTML text plus the elapsed time between system initialization and the time the web page is served. Thus the static Opera_Config_Page handler should have a header that tells the browser that it is allowed to cache the page and restore it from memory if it is re-requested. The Home_Page function, on the other hand, must have a header that tells the browser not to cache the contents, as this would render the time report inaccurate.

The Opera_Config_Page source code in Listing 1-8 provides an example of how to serve out a simple static web page whose text is defined in a *.html resource file.

Note that all web handler functions must accept a single input parameter (the modulenum) and return no parameters.

In this code excerpt, we first initialize some variables that are passed as parameters to Ether_Get_Line.

One of the rules of calling operating system or kernel extension functions is that no function nesting is allowed: it is illegal to invoke one of these functions within the parameter list of another.

By capturing the results returned by the HTTP_Outbuf and HTTP_Outbufsize functions into variables, we avoid the nesting prohibition when calling the EtherSmart driver functions invoked inside Opera_Config_Page.

Listing 1-8 A Handler Function from the Demo Program that Serves Static HTTP Header and HTML Text.

After initializing some local variables to the HTTP_Outbuf and HTTP_Outbufsize, there is a call to HTTP_Put_Headers. This puts the following standard HTTP headers into the specified LBuffer:

HTTP/1.1 200 OK
Server: Mosaic Industries EtherSmart Wildcard
Connection: close
Content-Type: text/html; charset=utf-8

The string count is stored in the first 2 bytes of the specified LBuffer, with the ASCII characters following. The first line announces the webserver as an HTTP/1.1 protocol server; the 200 OK means that the GET request has been received and a valid handler has been found that corresponds to the incoming URL. The second line announces the name of the webserver. The third line means that the webserver will close each connection after the GET request has been fulfilled, and "persistent connections" are not supported. The last line announces the content type. Note that one and only one empty line terminated by a carriage return/linefeed sequence must follow the headers. The call to HTTP_Put_Headers does this in Listing 1 8.

HTTP_Put_Headers expects as input parameters an LBuffer xaddress and maximum buffer size, a flag that is true if the URL is correct (should always be true for a non-default handler function), a flag that is true if the web content is dynamic or false if it is static, and a constant that specifies the content type to be announced. As shown in Table 1 12, there are constants defined for a variety of data types, including text/html, text/plain, image/gif, image/jpeg, image/png, and binary data. The PNG image type is noteworthy because it is a standard non-proprietary image format.

This announces the page as text/html content. It is left to the browser to decide whether to cache the page; most browsers will cache web content by default. If we had passed a true flag to the HTTP_Put_Headers to indicate that the web content was dynamic, the following header would have been produced:

HTTP/1.1 200 OK
Server: Mosaic Industries EtherSmart Wildcard
Connection: close
Content-Type: text/html; charset=utf-8
Cache-Control: no-cache

The last line of this HTTP header for dynamic content tells the browser not to cache the content, thereby forcing the browser to reload the page each time it is requested. This ensures that the latest version of the dynamically generated web content is presented in the browser window.

Now that the HTTP header is stored in the HTTP_Outbuf, it is ready to be sent out to the web browser.

Rule for Web Handler Functions

All web handler functions must accept a single input parameter (the modulenum) and return no parameters.
 

HTTP data transfer functions

The final two function calls in the Opera_Config_Page function in Listing 1-8 send out the HTTP header and the HTML web page content, respectively:

HTTP_Send_LBuffer(http_outbuf_base, modulenum);
// send out header, ignore #bytes_sent
HTTP_Send_Buffer(OPERA_CONFIG_HTML_XADDR, OPERA_CONFIG_HTML_SIZE, modulenum);
                 // send opera_config.html, ignore #bytes_sent

Recall that the local variable named http_outbuf_base is declared as an xaddress holding a 32-bit extended address, and is initialized to hold the value returned by HTTP_Outbuf. modulenum is the input parameter passed to the Opera_Config_Page function. HTTP_Send_LBuffer sends out the HTTP header which has been placed into the HTTP_Outbuf. Then HTTP_Send_Buffer sends out the HTML web page. The HTML source for this page can be found in the file named opera_config.html in the \Resources subdirectory of the EtherSmart/WiFi demo folder. The Image Converter program was used to convert this page and the other resources into S-record data that is loaded into flash when the image_data.txt file is downloaded to the Mosaic controller. The image_headers.h file contains the definitions of the 32-bit xaddress OPERA_CONFIG_HTML_XADDR constant and the 16-bit OPERA_CONFIG_HTML_SIZE constant that refer to the web page resource in memory.

Now we’ve reviewed all of the functions needed to send out the static web page from Listing 1 8. It’s as easy as using HTTP_Put_Headers to create the header, and calling HTTP_Send_LBuffer and HTTP_Send_Buffer to send the header and web content.

Table 1-13 lists the HTTP data transfer functions that should be invoked inside web service handlers to send out the HTTP header and web content. HTTP_Numbytes_Sent returns the number of bytes sent by the last execution of HTTP_Send_Buffer or HTTP_Send_LBuffer.

Table 1-13 HTTP Data Transfer Functions.
HTTP_Get_Timeout_Msec_Ptr HTTP_Send_Buffer
HTTP_Numbytes_Sent HTTP_Send_LBuffer
HTTP_Send_Many HTTP_Timeout_Msec_Ptr

HTTP_Get_Timeout_Msec_Ptr returns the 32-bit xaddress of a 16-bit timeout variable used by the Ether_Connection_Manager during the attempt to identify an incoming web connection. The default value is 5000, corresponding to 5 seconds. If an incoming carriage-return/linefeed-delimited line is not available on the connection within the specified timeout, and if the contents of HTTP_Enable_Ptr are nonzero, the input operation will cease when the timeout is reached, and the HTTP identification will then proceed. Increasing the value of this timeout beyond its default value may improve the robustness of web service on some networks. Note, however, that an incoming serial tunneling connection that does not promptly send a carriage-return/linefeed-delimited line effectively delays the recognition of an incoming serial tunneling (as monitored by Ether_Connect_Status) by the value of this timeout parameter.

HTTP_Timeout_Msec_Ptr returns the xaddress that holds a 16-bit timeout for outgoing HTTP traffic in units of milliseconds. The maximum allowed timeout is 65,535. This timeout is used by HTTP_Send_Buffer, HTTP_Send_LBuffer, and the HTTP/GUI functions described in a later section. The default set by Ether_Setup_Default is 33000, corresponding to a 33 second timeout for outgoing HTTP traffic. You may need to increase this value if you are serving large files over slow or congested networks.

Note that you must use the paged store routine StoreInt to update the timeout parameters. For example, to change the HTTP incoming (Get) timeout to 3 seconds and the HTTP outgoing timeout to 40 seconds on module number 7, you could execute the following code:

xaddress http_get_timeout_msec_ptr = HTTP_Get_Timeout_Msec_Ptr(7);
xaddress http_timeout_msec_ptr = HTTP_Timeout_Msec_Ptr(7);
StoreInt(3000, http_get_timeout_msec_ptr);
StoreInt(40000, http_timeout_msec_ptr);
 

Web handlers are posted to the Autoserve Array

The EtherSmart/WiFi embedded webserver is configured by defining and posting a "handler function" for each web address that is implemented. The Opera_Config_Page source code in Listing 1 8 provides an example of a handler function. Note that all web handler functions must accept a single input parameter (the modulenum) and return no parameters.

When the browser requests the web address URL (Uniform Resource Locator), the webserver looks for a match to the URL in the "Autoserve Array". If a match is found, the webserver running in the Ethernet task automatically executes the associated handler function which performs any required actions and transmits the web page to the browser. If a match is not found, the HTTP_Default_Handler is executed. This reports a "404 Page Not Found" error to the browser.

Table 1-14 lists the routines used to post web service handler functions. The "Autoserve Array" is a data structure that holds a pointer to each URL string, and the corresponding function pointer (execution address) of the handler associated with that URL. HTTP_Autoserve_Ptr returns an address that contains the 32-bit base address of this array. The Autoserve Array is set up with 32 rows by Ether_Info_Init and WiFi_Info_Init and their calling functions as listed in Table 1 3. HTTP_AUTOSERVE_DEFAULT_ROWS equals 32. This sets the maximum number of URL/handler pairs that can be posted. If you need to post more handlers, use HTTP_Is_Autoserve_Array to declare and size a new version of the array; see its glossary entry for details. The functions HTTP_Add_Handler and HTTP_Add_GUI_Handler post the required information into this array, writing to the row pointed to by HTTP_Index_Ptr. Consequently, the programmer does not need to directly access the contents of this low-level data structure.

Table 1-14 HTTP Functions to Manage Posting of Web Page Handlers.
HTTP_Add_Handler HTTP_Default_Handler_Ptr
HTTP_AUTOSERVE_DEFAULT_ROWS HTTP_Index_Ptr
HTTP_Autoserve_Ptr HTTP_Is_Autoserve_Array
HTTP_Default_Handler

Listing 1-9 presents an excerpt from the demo program to illustrate how web handlers are posted. Simply follow this template, substituting your own web handlers for the sample handler functions in the demo program.

The first part of Listing 1-9 defines the URL strings for each web page handler in the demo program.

Note that:
  • all of the URLs start with the / (forward slash) character; and,
  • it is good practice to always define a single forward slash string as a URL associated with the home page.

If a user types a bare IP address or computer name into the browser’s address bar, the browser sends a single / character as the URL, and the correct response is typically to respond with the specified device’s home page.

The next section of Listing 1-9 defines function pointers for each of the handler functions. This is done differently depending on the type of compiler being used. First we describe the pointer initialization for V4.xx systems (QED Board, QCard, QScreen, Mosaic Handheld) which use the Fabius C compiler. Then we will handle V6.xx *(PDQ) systems programmed using the GCC C compiler.

 

Defining pointers to web handler functions using the V4.xx/Fabius C environment

For V4.xx systems (QED Board, QCard, QScreen, Mosaic Handheld) which use the Fabius C compiler we must define pointers that include both address and page information in a 32-bit pointer value. This is a bit tricky because the processor has a native 16-bit address space. While the Forth compiler always uses 32-bit paged xaddresses, the Fabius C compiler typically tracks addresses as 16-bit non-paged quantities. To obtain a 32-bit xaddress that contains both the page and the address of the handler function, we follow the procedure shown in the first part of Listing 1 9 after the #ifdef __FABIUS__ directive. The statements

#pragma option init=.doubleword    // declare 32-bit function pointers in code area
#include </mosaic/gui_tk/to_large.h>

tell the compiler to go into a mode in which 32-bit function pointers can be generated and stored in non-volatile flash memory. Then the function pointers are defined and initialized using the declaration syntax as shown. Note that the initialization must occur outside of any function definition in the Fabius C compiler environment. For example, the 32-bit variable opera_config_page_ptr is initialized to equal the 32-bit execution xaddress of the Opera_Config_Page routine defined in Listing 1 8 using the statement:

xaddr (*opera_config_page_ptr)(void) = Opera_Config_Page;

After the nonvolatile 32-bit function pointers are captured, the #pragma and to_large.h compiler directives are reversed to return to normal compilation mode before compiling any functions. The Init_Web_Handler_Pointers function is a do-nothing dummy routine that is defined to maintain parallel code for both the Fabius and GCC compilation environments.

 

Defining pointers to web handler functions using the V6.xx/GCC C environment

The new PDQ line of products run the V6.xx operating system and the GCC tool chain, and use a different system to extract the pointers to the handler functions. This compiler automatically creates a "trampoline" function in common memory that invokes the handler in paged memory. The compiler insists that function pointers must be initialized at runtime, from inside a function definition. In the demo program, we declare the pointers as xaddr variables, and initialize them inside the Init_Web_Handler_Pointers function:

xaddr home_page_ptr          ;
xaddr opera_config_page_ptr  ;
xaddr form_entry_page_ptr    ;
xaddr form_response_page_ptr ;
xaddr logo_response_page_ptr ;
 
void Init_Web_Handler_Pointers( void )
// call this at the start of the Web_Handler_Installation() function to perform
// GCC's required runtime initialization of the pointers to the web handlers.
{    home_page_ptr          = (xaddr) Home_Page;
    opera_config_page_ptr  = (xaddr) Opera_Config_Page;
    form_entry_page_ptr    = (xaddr) Form_Entry_Page;
    form_response_page_ptr = (xaddr) Form_Response_Page;
    logo_response_page_ptr = (xaddr) Logo_Response_Page;
}
 

Invoking the web handler installation function

In summary, to properly declare and initialize the web handler pointers, simply follow the appropriate template code for the C compiler that you are using.

Then the Web_Handler_Installation function is defined to post the handler functions as shown in Listing 1 9. This function returns a nonzero error flag if the number of handlers posted exceeds the number of rows in the Autoserve Array (32 by default). We OR the error value returned by each invocation of HTTP_Add_Handler to produce the overall error result. For example, the third call to HTTP_Add_Handler the Web_Handler_Installation function posts the handler for the Opera_Config_Page routine:

error |= HTTP_Add_Handler(STRING_XADDR(opera_url_str), strlen(opera_url_str),
            opera_config_page_ptr, modulenum);

The STRING_XADDR macro is defined or included at the top of the demo program. It converts the 16-bit string address to a 32-bit xaddress, inserting the page at the time the calling function runs. This macro is discussed in more detail in the "EtherSmart/WiFi Driver Data Structures: Passing String Extended Addresses as Function Parameters" section above. The strlen function is a standard C routine that returns the length of the specified string. The opera_config_page_ptr parameter is the execution address (function pointer) to the handler routine. The modulenum completes the input parameter list in the call to HTTP_Add_Handler in Listing 1-9.

Listing 1-9 Posting the Web Handler Functions in the Demo Program.

 

Web form processing

It is easy to create "forms" in HTML. Forms provide a way for a user to enter data in text boxes. They allow a user to select items from drop-down menus, radio button lists, or check boxes that are presented in the web browser window. The user’s selections are encoded as a set of "query fields" appended to the URL after a ? character. A set of pre-coded form processing functions in the EtherSmart/WiFi driver enable the application program to parse the query fields, extract the data, and take actions based on the results.

The demo program serves out a simple HTML form page as illustrated in Figure 1-12. This page is served out by the Form_Entry_Page handler function whose source code is shown in Listing 1-11. The form asks the user to select from radio buttons labeled "man", "woman" or "child", to type in their name, and to select their favorite color from a drop-down menu of colors "red", "blue", "yellow", or "green". Clicking the "Submit" button encodes the form results as a set of query fields appended to the form_response.cgi URL. This URL is handled by the Form_Response_Page function in Listing 1-11. The .cgi file extension on the response URL refers to the "Common Gateway Interface" which deals with query fields. Note that the file names and extensions chosen for the URL are arbitrary (you can choose anything reasonable that you like), but it is a good idea to select file names and extensions that indicate the nature of the web service content or action.

While the form shown in Figure 1-12 is somewhat whimsical, it is meant to be suggestive of very useful capabilities. For example, your application could serve out a web form that asks the user to select a set of digital output channels and the corresponding values to which they should be set (high or low). The form could ask the user to specify a set of analog data acquisition channels, and the application program could respond by printing a table of the latest values for the specified channels. In other words, forms provide a means to instruct the application program to take specified actions, as well as a means to request specified data.

embedded webserver

Figure 1-12 Form entry web page served out by the EtherSmart/WiFi Demo Program.

Listing 1-10 presents the HTML source text that generates the web page in Figure 1-12. This page is compiled as a resource string by the image converter, resulting in constant definitions FORM_PAGE_HTML_XADDR and FORM_PAGE_HTML_SIZE that refer to the string. These constants are used in the Form_Entry_Page handler function that serves out the web page as shown in Listing 1-11.

Listing 1-10 Contents of the form_page.html Resource File.

Listing 1-10 provides an example of how to write the HTML code that generates a form web page. The <html> tag starts the file, and is balanced by the </html> tag at the end of the file. These tags tell the browser that the enclosed text is HTML. The <head> and </head> tags declare the HTML header area of the file. In the HTML header, the title of the web page is declared as "EtherSmart Form Entry". It is good practice to declare an informative title for each web page, as many browsers display the title.

The <body> tag starts the displayed body of the web page in Listing 1-10. The <H3> and </H3> tags declare the start and end of a heading style for the welcome message. The <p> tag means "start a new displayed paragraph" and results in the display of a blank line in the browser window; note that there is no </p> tag in HTML.

The link to the Mosaic Industries web site is generated by the HTML <a> "anchor" tag, and the href keyword specifies the link destination:

<a href="http://www.mosaic-industries.com">Mosaic Industries</a>

The start of the form portion of the page that is to be completed and submitted by the user is declared using the HTML code:

<form method=GET action="/form_response.cgi">

The "GET" method is the only method that is supported by the EtherSmart/WiFi Wildcard. The action keyword declares the URL of the routine that responds to the form request. This URL invokes the Form_Response_Page function whose source code is presented in Listing 1 11.

The first part of the form declares a set of radio buttons. A radio button set allows only one of the choices to be active; use check boxes if you want the user to be able to select multiple items from a single set of options. Each radio button declaration is of the form:

<input type=radio name=classification value="man"> Man

The name keyword declares the fieldname, and the value keyword specifies the value. The browser passes the fieldname followed by an = character followed by the selected value as part of the query field in the URL. Note that the text outside the <input > tag boundaries is simply HTML text that is displayed in the form. There is no requirement that the value field declared in the tag be identical to the displayed text. In this example, the value field and the displayed text differ only in their capitalization.

The next portion of the form declares a textbox using the line:

<p>Name: <input type=text name=name_id size=32 maxlength=80>

This declares a textbox 32 characters wide, with a maximum number of 80 characters accepted; the textbox scrolls if the number of characters exceeds 32. The fieldname is name_id, and the field value will equal the contents typed in by the user. There are some subtleties, though. The HTTP standard requires that certain characters may not be included in a URL and must be "escaped" by encoding the character as <html>&#037;</html>hexhex, where hexhex represents the two hexadecimal characters that specify the ASCII value of the desired character. In addition, the browser converts each space character (ASCII 0x20) into the + character. The following is a minimal list of characters that are escaped by the browser:

; / ? : @ = & < > “ # % { } | \ ^ ~ [ ] ‘ 

To make it easy to deal with this situation, the EtherSmart/WiFi driver contains the HTTP_Unescape and HTTP_Plus_To_Space functions that make it easy to recover the original text as typed by the user.

The next part of the form in Listing 1-10 is a drop-down menu that is started with the tag:

<select name=color size=1>

The name keyword defines the fieldname, and the size keyword specifies how many items are visible at one time to the user. The optional multiple keyword, if included in the select tag, allows the user to select more than one option.

The form is ended with the tags:

<input type=submit></form>

When the user clicks the "Submit" button, the selected choices are encoded as query fields appended to the URL. For example, if the user selects "man" as the classification, types "Randy Newman!" in the name box, and chooses "yellow" as the favorite color, the following URL is sent by the browser to the EtherSmart/WiFi Wildcard:

/form_response.cgi?classification=man&name_id=Randy+Newman%21&color=yellow

This URL illustrates how the query fields are formatted by the browser. The query fields start with the ? character, and each subsequent field is separated by the & delimiter. Within each field is the fieldname followed by the = character, followed by the value field. Note that the exclamation point after the name is escaped as %21, and the space between "Randy" and "Newman" is converted to a plus sign in the URL. The Form_Response_Page function calls HTTP_Unescape and HTTP_Plus_To_Space to process the URL query fields and recover the original text as typed by the user. This URL invokes the Form_Response_Page function in Listing 1 11 which parses the query fields following the ? character in the URL, and serves out a response page. For the URL shown above, the response presented in the web browser looks like this:

 **Thanks for your response!**
It must be great to be a man with a name like Randy Newman! .
It's good to hear that yellow is your favorite color!

Table 1-15 presents the pre-coded EtherSmart/WiFi form processing functions that are used to parse and respond to the query fields in the URL. HTTP_Parse_URL is automatically called by the webserver and is typically not directly called by the programmer. It sets up the initial parsing pointers to point to the first query field (if present) in the URL. HTTP_Parse_URL initializes the HTTP_URL_Ptr to return the xaddress of the start of the URL in the HTTP_Inbuf. It sets the HTTP_URL_Base_Count to equal the number of characters before the start of the query field, and sets the HTTP_URL_Full_Count to the total number of characters in the URL. If there is no query field, then the HTTP_URL_Base_Count equals the HTTP_URL_Full_Count.

The HTTP_Fieldname_Ptr and HTTP_Fieldname_Count return the xaddress and count, respectively, of the next fieldname. In the example URL, the first fieldname is classification and its count is 14 (the number of characters in classification). The HTTP_Value_Ptr and HTTP_Value_Count return the xaddress and count, respectively, of the next value which appears after the = character. In the example URL above, the first value is man and its count is 3 (the number of characters in man).

Table 1-15 HTTP Form Processing Functions.
HTTP_Fieldname_Count HTTP_Unescape
HTTP_Fieldname_Ptr HTTP_URL_Base_Count
HTTP_Parse_URL HTTP_URL_Full_Count
HTTP_Plus_To_Space HTTP_URL_Ptr
HTTP_Status_Ptr HTTP_Value_Count
HTTP_To_Next_Field HTTP_Value_Ptr

To see an example of how these form processing functions work, we can take a close look at the source code of the Form_Response_Page function in Listing 1-11. As usual, we first define and load a set of local variables to avoid nesting of operating system function calls. These local variables capture the starting values of the return values for the HTTP_Outbuf, HTTP_Outbufsize, HTTP_Value_Ptr, and HTTP_Value_Count for the specified modulenum. The standard procedure for HTTP header generation is followed, invoking HTTP_Put_Headers and HTTP_Send_LBuffer to send the HTTP header, declaring the content as static HTML text. Then HTTP_Send_Buffer is used to send the form_response_str, with the STRING_XADDR macro used in the standard fashion to convert the string base address to a 32-bit xaddress, with strlen providing the string count. At this point, the Form_Response_Page function has served out the HTTP header and displayed the string:

**Thanks for your response!**
It must be great to be a

Next, Store_Int is invoked by Form_Response_Page to zero the 16-bit count at the start of the HTTP_Outbuf, thereby starting a new string in the LBuffer. If the first value was entered by the user (that is, if it has a non-zero count), the value is appended to the HTTP_Outbuf. Otherwise, the neutral classification string "person" is added to the buffer using HTTP_Outbuf_Cat. This completes the processing of the man/woman/child part of the form.

HTTP_To_Next_Field is called by Form_Response_Page to advance to the name_id field associated with the textbox, and the return values of HTTP_Value_Ptr and HTTP_Value_Count are stored in the local variables.

Then, to avoid confusing the parsing routines when HTTP_Unescape is called, HTTP_To_Next_Field is called again to advance to the color field. Then the HTTP_Value_Ptr and HTTP_Value_Count return values are stored in the next_http_value_ptr and next_http_value_count variables. This technique protects us against a situation in which the user types any of the query field delimiters such as & or = as part of the name entered into the textbox. When we unescape the textbox string, these delimiters would appear in the URL and confuse the parsing routines. We "look ahead" to avoid this problem.

If the user entered a name in the textbox, Form_Response_Page calls HTTP_Plus_To_Space and HTTP_Unescape to convert the text in the URL back into the characters that the user typed. In the example URL above, these steps deal with the + between "Randy" and "Newman", and recover the ! character that the user typed. Note that we capture the count returned by HTTP_Unescape, as this routine shortens the textbox substring by two characters for each escape sequence that it processes. If the user did not enter anything in the textbox, then we add the neutral term "yours" to the HTTP_Outbuf using HTTP_Outbuf_Cat.

The next few lines in the Form_Response_Page function add the favorite color string to the HTTP_Outbuf using HTTP_Outbuf_Cat. A line of text containing the ending tags </body></html> is added to the HTTP_Outbuf, and the contents of HTTP_Outbuf are sent to the browser using HTTP_Send_LBuffer. This completes the form response, and the browser displays a message that summarizes the form entries that were made.

While this example is purely text-based in its response, keep in mind that the same coding techniques can be applied to instruct the Mosaic controller to perform sophisticated data acquisition, control, and status reporting in response to the inputs received via a web form.

Listing 1-11 Form Processing Web Request and Response Pages.

 

HTTP inter-task service management functions

The service management functions that run the webserver are the same as those described in the section titled "Serial Tunneling Inter-Task Service Management Functions"; they are listed in Table 1 10. Unlike the other services that have been discussed such as serial tunneling and email, the web service is performed autonomously by the Ethernet task. After the handlers are posted to the Autoserve Array, non-GUI web service requests are handled by the Ethernet task with no intervention by the application task. The only exception is the implementation of a GUI (Graphical User Interface) "remote front panel" feature which must be synchronized to the application task as discussed in a later section.

 

An example of a dynamic web page with a remote image reference

The EtherSmart home page (served out by both the EtherSmart and WiFi Wildcards) is shown in Figure 1 13 as it appears in a browser. It presents some welcoming and explanatory text, provides a link to the form page discussed in the prior section, and invites the user to link to a stand-alone image as discussed in the next section. The home page presents a link to the Mosaic Industries website at www-mosaic-industries.com, and displays an image that is hosted by the Mosaic site. It invites the user to link to the Opera browser website at www.opera.com, and links to an informative page that describes the advantages of Opera and tells how to configure it for use with the EtherSmart/WiFi Wildcard. It recommends the use of the Putty TCP/IP terminal program for serial tunneling and interactive TCP/IP communications with the EtherSmart/WiFi Wildcard. Finally, it displays a dynamically generated elapsed time since system initialization.

It is easy to implement these features using HTML and a bit of software. Listing 1 12 shows the HTML source code that generates the home page. This HTML file is processed as a resource by the Image Converter program as explained above and in Appendix A. Many of the HTML tags in this file have already been discussed in earlier sections, including the following tags:

<html> </html>  <head> </head>  <title> </title>  <H3> </H3>  <p>

The link to the form entry page is coded as follows:

While this simple <a href="/form_entry.html">form example</a> summarizes

This uses the standard <a> </a> "anchor" tags, with the href keyword specifying the target URL, and the remaining text inside the tag specifying the link text displayed by the browser ("form example" in this case).

A similar link to the stand-alone logo image is served out by the following HTML:

The EtherSmart webserver can serve out a stand-alone image like
<a href="/logo_response.png">this one</a>.

This anchor invokes the /logo_response.png URL; recall that PNG is a convenient public domain image compression format. The Logo_Response_Page function that is associated with this URL is described in the next section.

embedded ethernet homepage

Figure 1-13 Home page served out by the EtherSmart/WiFi Wildcard as seen in the browser window.

The following HTML presents a link to the Mosaic Industries website, and displays a picture:

It is also easy to serve out images that reside on other servers by simply
including a reference to the image in your HTML code.  The following picture
is an example; it is hosted by Mosaic's website at
<a href="http://www.mosaic-industries.com">www.mosaic-industries.com</a>:
<p><img src="http://www.mosaic-industries.com/ESmart_Images/webbanner_logo1.jpg">

The link to the Mosaic Industries website uses the standard anchor tag syntax as described above. The <img> tag refers to the image called webbanner_logo1.jpg which resides on the Mosaic Industries website. While the home page text in Figure 1-13 image is served directly by the EtherSmart/WiFi Wildcard, the displayed image actually comes from another webserver. You can use this technique to serve large, colorful images without burdening the embedded controller itself. This technique uses the power of the Internet to enhance the visual appeal of web pages served by the EtherSmart/WiFi Wildcard.

Listing 1-12 Contents of home_body.html Resource File.

Listing 1-13 presents the source code that serves out the EtherSmart home page. Most of the code follows the procedures outlined in earlier sections, capturing the buffer xaddress and count into local variables, serving the HTTP headers using HTTP_Put_Headers and HTTP_Send_LBuffer. The unique aspect of this web page is that it inserts dynamically generated data, reporting the elapsed time since system initialization. The ReadElapsedSeconds function returns a 32-bit time count that is captured into the elapsed_sec variable. Then the standard sprintf C function is invoked in the argument list of HTTP_Outbuf_Cat to convert the time to a string and append it to the HTTP_Outbuf buffer. HTTP_Send_LBuffer serves out the dynamically generated elapsed time to the browser.

This is a simple example of dynamically generated web content. Using these techniques, your application can dynamically display the status of your instrument to a remote operator via the web.

Listing 1-13 Dynamic Web Page Source Code.

 

Serving out a stand-alone image

The home page served out by the EtherSmart/WiFi Wildcard provides a link to the /logo_response.png URL, a stand-alone image of the Mosaic Logo. The logo image is in a file named mosaic_logo.png, and the Image Converter program converts it into a resource specified by the constants MOSAIC_LOGO_PNG_XADDR and MOSAIC_LOGO_PNG_SIZE. The handler for this URL is the Logo_Reponse_Page function shown in Listing 1 14. Because the web page contains only one type of data (in this case, a PNG image), it can be properly rendered by any browser in the browser’s default configuration. The response function shown in Listing 1 14 is simple. After capturing the HTTP_Outbuf and HTTP_Outbufsize return results in local variables, HTTP_Put_Headers is called to create the header. The content type is specified by the constant HTTP_IMAGE_PNG_CONTENT, and the image is declared as static because it does not change over time. The size of the HTTP header is retrieved out of the first 16 bits of the HTTP_Outbuf by the Fetch_Int function; recall that because the buffer is in paged memory, the paged fetch operator must be used. Note that the starting xaddress of the HTTP header is specified by http_outbuf_base+2, as the first 2 bytes of this LBuffer contain the number of bytes stored. The MOSAIC_LOGO_PNG_XADDR and MOSAIC_LOGO_PNG_SIZE constants specify the logo image data that comprises the content of the served page.

Listing 1-14 Handler Function that Serves Out an Image.

 

Initializing the demonstration program

Listing 1-15 shows the high level startup functions of the Demo program. Ether_Web_Demo initializes the EtherSmart Wildcard, starts the Ethernet task, and runs the web demo. WiFi_Web_Demo initializes the WiFi Wildcard, starts the Ethernet task, and runs the web demo. These two functions are very similar: the former invokes Ether_Task_Setup_Default, while the latter invokes WiFi_Task_Setup_Default. main can be edited to call either Ether_Web_Demo or WiFi_Web_Demo. Each web demo function invokes Web_Handler_Installation which was discussed in the Section titled "Web Handlers Are Posted to the Autoserve Array".

The main function can be edited to call either Ether_Web_Demo or WiFi_Web_Demo. You can run the demonstration program by compiling it and loading it as described in Appendix A, and then typing main at the QED Terminal.

As described in earlier sections, you can interactively type the following commands after executing main; all work for both EtherSmart and WiFi Wildcards:

Ether_Task_Setup_Default(  )  // full initialization
Ether_Set_Defaults(  )  // sets mosaic factory defaults; returns error code
Ether_Set_Local_IP(  int my_ip1, int my_ip2, int my_ip3,  int my_ip4 )  // sets IP
Ether_IP_Report(  )   // summarizes the IP address, gateway address, and netmask.
Ether_Ping(  int remote_ip1, int ip2, int ip3,  int ip4)  // prints ping report
Ether_Monitor_Demo( int modulenum )  // runs interactive monitor on modulenum
Tunnel_Test(  )  // waits for incoming connection via Putty or other Ether terminal
Email_Test(   )  // sends a test email; you must edit function’s source code first

Recall that when interactively typing these commands, there is no space between the function name and the ( character, and there must be at least one space after the ( character. All of these functions have been described in the text of this document.

Listing 1-15 High Level Initialization Functions in the Demo Program.



See also → Setting-up Dynamic Embedded Web Server

 
This page is about: Web Handlers, Web Form Processing, Serving Out Image, Web Service Basics – The EtherSmart/WiFi Wildcard implements a dynamic webserver that accepts connections from your web browser, serving out static or dynamic web pages to implement web-enabled instrumentation. Such webservers allow you to "browse into" your instrument using a web browser running on an online PC to monitor the status of your instrument.
 
 
Navigation