Device control with Barracuda
C version

You are reading an archived tutorial for the public C API in the
Barracuda Embedded Web Server.

The Barracuda Embedded Web-Server is a tool specifically designed for remote device control. A regular web-server is designed to fetch HTML pages, images etc, when a browser requests such information. High-end web-servers such as Apache have the ability to add special plugins that allow for some form of device control. In contrast, Barracuda is designed solely for device control, but plugins make it possible for this web-server to also function as a regular web-server. These plugins are in fact device control units that extend the functionality of the web-server. So as Apache has plugins for doing device control, Barracuda has plugins for working as a regular web-server.


Barracuda, which is an object oriented C library, provides a C code API and a C++ code API. We have two versions of this white paper, one for C programmers and one for C++ programmers. This article assumes that you are a C programmer with some HTML knowledge.

You can also open the C and C++ source code in a separate window and use the two source code versions as a reference when reading this article.

We suggest that your read the introduction to Object Oriented programming in C code before you read the C version of this white paper.

The Barracuda Web-Server comes with a number of sophisticated tools that can automate much of the work in creating web-pages for device control. We have in this article purposely not used any of these tools in order to show you the foundation of the web-server and to keep the article as short as possible.

Use Barracuda to remotely set and get the system time

This article will demonstrate how one can use Barracuda to remotely control a device. The Barracuda Embedded Web-Server will be used to remotely set and get the system time in an embedded device powered by the SMX real-time operating system. The Barracuda Web-Server, which is a C library, is linked with the SMX real-time operating system and the set/get system time code. Barracuda runs on many operating systems, but we have in this paper used a few primitives from the SMX real-time operating system for setting and getting time etc.

The objective of this article is to make a simple web-interface, which we can use to obtain the current time in an embedded device or to change the time, if the time is incorrect.

The web-interface should have the following layout:

Current day and time is: Fri, 14 May 2004 09:04:14 GMT


When the user makes the browser send a HTTP GET request to the web-server, the C++ code we are going to design should get the current time from SMX, format the time into a human readable string and send the string to the browser. A HTML form will also be created and sent to the browser. The user can use this form for setting a new time in the embedded device.

The HTML form contains two input fields:

The Hyper Text Transfer Protocol (HTTP) is the network protocol of the Web. It is a protocol designed to transmit data from the client to the server and vice versa. The GET command sent from a browser (the client) to the server is the most common HTTP method; it says "give me this resource". This is the initial command sent to the server for getting the above web-interface. The set/get time logic we will design will get this request and dynamically create the above web-interface. Every time the user presses the refresh button, the browser sends a new GET request to the server and a new HTML page is dynamically created with the correct time formatted as a human readable string. You will therefore see the time change every time you press the refresh button. A POST command is used to send data to the server. The browser will automatically send a POST command when the user presses the "submit" button. The browser extracts the user data entered in the HTML form and sends this data embedded inside the POST command to the server. The set/get time logic will receive this data and set the new date and time based on the data received from the browser.

Assembling the web-server

As aforementioned, Barracuda is a C library, which you link with your application and you therefore have to write some support code in order to make a web-server. Barracuda is designed to run in its own SMX task, where it uses the TCP/IP interface and waits for client requests. When a client sends a command to the server, the server first parses the incoming data and from this data, the server uses the extracted HTTP path to locate the object (the page) that matches the path. When the object (the page) is located, the web-server sends an event to the object and it is this object's responsibility to handle the request from the client and to present the information requested by the client.

The Barracuda web-server uses a virtual file system. The path extracted from the client request is used when searching the virtual file system for the object (the page) requested by the user. If the object is found the web-server sends an event to this object, by calling a registered callback function in this object.

The web-server we design in this article will only have one resource. The set/get time resource is installed as "index.html" and the web-server knows that if a user requests a path to a directory and that directory contains an index.html, the web-server will forward the request to that page. The resource will be installed in the root directory thus by typing the IP address to the device in a browser, the resource will be activated. For example, assuming that the device has IP address 192.168.1.1, the URL http://192.168.1.1:8080 will activate our set/get time resource. The 8080 is the port number to the web-server. You have to specify the port number in the URL if the web-server is listening on a port different than the default HTTP port 80.

The Barracuda SMX task

Barracuda, which is run in a SMX task, must first be initialized. After the initialization, the task enters an infinite loop which waits for incoming HTTP requests.

The Barracuda SMX task does the following:

  1. Performs host platform initialization if run on Windows.
  2. Creates the necessary Barracuda Web-Server objects and bind the objects together.
  3. Checks if the initialization of the HTTP server listen object worked.
  4. Creates the virtual file system and insert the set/get time resource into the virtual file system.
  5. Runs the web-server.
136 void 137 barracudaMain() 138 { 139 140 HttpDispatcher dispatcher; 141 HttpServer server; 142 HttpServCon serverCon; 143 HttpDir rootDir; 144 SetGetTimePage setGetTimePage; 145 int port=8080; 146
1
147 #ifdef WIN32 148 WORD wVersionRequested; 149 WSADATA wsaData; 150 wVersionRequested = MAKEWORD(1, 1); 151 if(WSAStartup( wVersionRequested, &wsaData )) 152 perror("WSAStartup"); 153 #endif 154 155
2
156 /*Create the socket dispatcher object.*/ 157 HttpDispatcher_constructor(&dispatcher); 158 /*Create the Web-Server*/ 159 HttpServer_constructor(&server, &dispatcher); 160 /*Create a server socket listener that listen on all interfaces.*/ 161 HttpServCon_constructor(&serverCon, &server, port, AF_INET, 0, 0); 162
3
163 if( !HttpServCon_isValid(&serverCon) ) 164 { /*A primitive error handling*/ 165 assert(0); 166 return; 167 } 168
4
169 /*Root dir takes no parameters i.e. no name*/ 170 HttpDir_constructor(&rootDir,0, 0); 171 SetGetTimePage_constructor, &setGetTimePage); 172 HttpDir_insertPage(&rootDir, (HttpPage*)&se&setGetTimePage); 173 HttpServer_insertRootDir(&server, &rootDir); 174
5
175 #ifdef WIN32 176 for(;;) 177 { 178 struct timeval t; 179 t.tv_sec=0; 180 t.tv_usec=0; 181 HttpDispatcher_run(&dispatcher, &t); 182 Thread_sleep(50); /*50 mSec. */ 183 } 184 #else 185 HttpDispatcher_run(&dispatcher, 0);/*Never returns*/ 186 #endif 187 }

As you can see from the above code, Barracuda can also run on the SMX windows simulator. The Barracuda C library and advanced host tools facilitate rapid development using a Windows machine. You develop, run and test the code on Windows before you even deploy the code into an embedded device -- obviously, a huge time saver.


Creating the set/get time page class

A resource is an instance of the HttpPage class. The HttpPage class is in C++ terminology -- an abstract base class. This means that you cannot create an instance of this class without first making a derived class. The set/get time class inherits from HttpPage and implements the resource's service function. The service function is the callback function the web-server calls when the web-server delegates the request.

The set/get time page class:
9 typedef struct 10 { 11 HttpPage super; /* As if inherited */ 12 } SetGetTimePage; 13 14 15 void SetGetTimePage_constructor(SetGetTimePage* o); 16 void SetGetTimePage_service(HttpPage* o, 17 HttpRequest* request, 18 HttpResponse* response); 19 void SetGetTimePage_doGet(SetGetTimePage* o, HttpResponse* response); 20 void SetGetTimePage_doPost(SetGetTimePage* o, 21 HttpRequest* request, 22 HttpResponse* response); 23 void SetGetTimePage_setRTC(SetGetTimePage* o, struct tm* tm);
SetGetTimePage constructor:
28 void 29 SetGetTimePage_constructor(SetGetTimePage* o) 30 { 31 HttpPage_constructor((HttpPage*)o, 32 SetGetTimePage_service, 33 "index.html"); 34 }

The SetGetTimePage constructor calls the HttpPage constructor. The second argument is a pointer to the service function declared on line 16 and the third argument is the name of the page. The page name and the function pointer to the service function are used by the virtual file system when locating and calling the page service function.

The service function:

The web-resource service callback function is called by the web-server (via the virtual file system) when the web-server delegates either a GET or a POST request to an instance of the SetGetTimePage class. Recall that we registered the service callback function when we called the HttpPage Constructor (line 31).

36 void 37 SetGetTimePage_service(HttpPage* o, 38 HttpRequest* request, 39 HttpResponse* response) 40 { 41 42 HttpResponse_write(response, 43 "<html><head>" 44 "<title>Set or get SMX time</title>" 45 "</head><body>", 46 -1, TRUE); 47 48 if(HttpRequest_getMethodType(request) == HttpRequest_Post) 49 SetGetTimePage_doPost((SetGetTimePage*)o,request,response); 50 else 51 SetGetTimePage_doGet((SetGetTimePage*)o,response); 52 53 HttpResponse_write(response,"</body></html>", -1, TRUE); 54 }

The first argument is the "this" pointer, but we have to typecast the "this" object to the derived class.

The second argument, the request object, contains all the information you can possibly get from the client. This information is pre-processed and presented as sub-objects within the request object. For example, a client sending data to the server using a POST command is pre-parsed by the web-server and stored as objects in the request object.

The third argument, the response object, is used when sending the response message back to the client. For example, line 42 uses the "write" member function to send the initial part of the HTML text back to the client, and line 53 sends the closing HTML page tags to the client.

Line 48 detects if this is a POST request or a GET request. Recall that GET is used to fetch a new page and POST is used to send data from the client to the server. As you can see, we have made one function to handle the GET request and one function to handle the POST request. This is not necessary, but it sometimes gives a cleaner design.

Dynamically create the web-interface

The doGet function is called when the user types in the device URL in the browser or press the refresh button.

The doGet function does the following:

  1. Gets the current time from SMX.
  2. Formats the time into a human readable string.
  3. Sends the string to the client.
  4. Sends the HTML form to the client.

The GET request function:
57 void 58 SetGetTimePage_doGet(SetGetTimePage* o, HttpResponse* response) 59 { 60 static const char* wd[7] = { 61 "Sun","Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 62 }; 63 static const char* months[12] = { 64 "Jan","Feb", "Mar", "Apr", "May", "Jun", 65 "Jul","Aug", "Sep", "Oct", "Nov", "Dec" 66 }; 67 struct tm tm;
1
68 U32 time = get_stime(); 69 httpTime2tm(&tm, time); 70 HttpResponse_write(response,"<p>Current day and time is: ",-1,TRUE);
2 and 3
71 HttpResponse_printf(response, 72 "%s, %02d %s %d %02d:%02d:%02d GMT", 73 wd[tm.tm_wday], 74 tm.tm_mday, 75 months[tm.tm_mon], 76 tm.tm_year+1900, 77 tm.tm_hour, 78 tm.tm_min, 79 tm.tm_sec); 80 HttpResponse_write(response,"</p>", -1, TRUE);
4
81 HttpResponse_write(response, 82 "<p>" 83 "<form method='post'>" 84 "<input type='text' name='time' size='30'/>" 85 "<br/>" 86 "<input type='submit'/>" 87 "</form>" 88 "</p>", 89 -1, TRUE); 90 }

Setting a new time in the device

The doPost function is called when the user press the "submit" button.

The doGet function does the following:

  1. Extracts the form data (the date and time string sent from the client to the server).
  2. Converts the date and time string to number of seconds since January 1, 1970.
  3. Converts number of seconds to a tm structure.
  4. Sets the new time in the device.
  5. Sends a command to the client that makes the browser do an automatic GET request.

The POST request function:
93 void 94 SetGetTimePage_doPost(SetGetTimePage* o, 95 HttpRequest* request, 96 HttpResponse* response) 97 { 98 HttpTime time; 99 struct tm tm;
1
100 const char* timeStr = HttpRequest_getParameter(request,"time");
2
101 if(timeStr == NULL || (time=httpParseDate(timeStr)) == 0) 102 { 103 HttpResponse_printf( 104 response, 105 "<p>" 106 "Cannot parse date string. " 107 "Date string must be in RFC1123 or RFC850 format.<br/>" 108 "<a href='%s'>Try again</a>" 109 "</p>", 110 HttpPage_getName((HttpPage*)o)); 111 } 112 else 113 {
3
114 httpTime2tm(&tm, time);
4
115 setRTC(o, &tm);
5
116 HttpResponse_sendRedirect( 117 response, 118 HttpResponse_encodeRedirectURL( 119 response, 120 HttpPage_getName((HttpPage*)o))); 121 } 122 }

You may have noticed that the sendRedirect function, which only sends a HTTP header and no body text, is called after we had already started sending data to the client. For example, we sent the beginning of the HTML body on line 42. This works because the web-server does not immediately send data to the client. The data is buffered internally in the response object and flushed to the client when the buffer is full. Calling the sendRedirect member function discards any data in this buffer and immediately sends the redirect HTML header to the client. The sendRedirect member function also disables the response write functions. For example, the write on line 53 will have no effect after calling the sendRedirect member function. At this point, you might not understand the enormous amount of aid provided by the Barracuda web-server, but you will come to appreciate these features when you design complex user interfaces for device control. In the end, you will not understand how you could ever survive without Barracuda.

You might wonder why we send a HTTP redirect response header to the client instead of sending a response body in the POST request. There are two reasons for this:

  1. We already have the logic for sending the web-interface HTML code to the client when the client sends a GET request.
  2. A browser might stay in "POST mode" unless the browser is specifically doing a GET after the HTTP POST response. This means that pressing the "refresh" button in the browser would have sent another POST request to the server. The redirect response makes the browser do a GET after the POST request. This is transparent to the user.

You can get the full source code for the set/get time code here.

Conclusion

We have shown you how to implement a resource by sub-classing the HttpPage class. A HttpPage resource can do all kinds of sophisticated device control. The Barracuda framework, which models the framework in J2EE makes it extremely easy to design web-interfaces for controlling your device.

A HttpPage is similar in functionality to a Java-servlet, except for that a HttpPage is written in C or C++. This gives you an enormous advantage since a good Java-servlet book, with its interface resembling the Barracuda interface, can help you leverage the true power of the Barracuda framework.

We also mentioned that one could overload the functionality in an HttpDir (the virtual file system directory node). Barracuda comes with many classes that sub-class the HttpDir class. These plugins make it possible for the Barracuda web-server to read HTML pages from a regular file system, view a ZIP file as a read only file system, implement authentication and authorization, etc..

Writing a web-page class such as the SetGetTimePage in this article is normally not necessary as the Barracuda web-server comes with a number of development tools that can automate this task. One of these tools is a compiler than can take an HTML file and automatically convert the HTML file to either a C or C++ web-page class. This HTML file can use special server side tags, which we call CSP tags. The CSP tag language, which means C Server Pages, is similar in functionality to ASP. The ASP tag language invented by Microsoft makes it possible to insert Visual Basic within special server side HTML tags in an HTML file. Our CSP tag language makes it possible to insert C or C++ within the same server side tags. Here is an example of the above code designed in CSP.

The host tools that comes with Barracuda can help you convert an entire web-site with sub-directories, gif images, etc. into a virtual file system, thus you do not have to manually create the virtual directory structures as we did in this example.

Please see our CSP tag language white paper for more information on how to design and do rapid development of advanced device control logic using the CSP compiler.