Level: Intermediate Lewin Edwards (sysadm@zws.com), Design Engineer, Freelance
29 Aug 2006 Add a Web-based user interface to a previously developed multimedia client in this episode of the
Multifunction multimedia machine series
. Author Lewin Edwards looks both at user-interface and back-end design issues, and shows how local browser functionality is an interesting alternative to requiring a remote browser.
In episode 5 of the
Multifunction multimedia machine series
, you add the following two important pieces of functionality
to your multimedia appliance and examine the implications from the point of
view of a commercially fielded appliance:
- The ability to remote-control the device using any convenient Web
browser.
- A local user interface provided using the ELinks or Links2
embedded Web browser.
I have a lot of material to cover in this episode, so I am not going to
use text space going line by line through the source code. Please refer to
the comments in the code for more details on how I achieve the features I
describe below (see the Download section for the code itself).
The appliance you have assembled to date, assuming of course that you've been
following along with these articles, already has most of the infrastructure
for item 1: you already have a functional Web server (thttpd) and a main
application (ibmslides), so all you really need is a control interface on
the application part and a way of connecting the Web server to this
control interface.
CGI for fun and profit
A simple way of achieving this is to build a CGI back-end program, called by
the Web server, that communicates data to the main program through a named
pipe. As a minor administrative detail, I've chosen to implement this CGI
program inside the ibmslides program itself. If you examine the Makefile in
the downloaded source tarball, you will see that "make install" now installs a copy of the ibmslides program at /web/cgi-bin/admin.cgi. The main.c
file checks argv[0] to see how the program was invoked; if the substring
"cgi" is found in that path, the program assumes its CGI persona, grabs the
input, and talks to the named pipe /tmp/slidepipe.
Note that the above hack works on the assumption that you are using the
/etc/thttpd.conf configuration file I left you with last episode. If you
modified that file so CGI scripts don't live in */cgi-bin/*, then make sure
you call the alias something.cgi to make sure it trips the dual-personality
logic correctly.
I won't go into a whole bunch of detail on the mechanics of how to acquire
and process CGI data submitted in a Web form because I covered the
introductory details a while ago in "Migrating from x86 to PowerPC,
Part 4"
(see Resources). This piece of code uses the same technology; it
just applies it in a slightly different way. The interesting part is that
instead of simply generating a Web page directly, the CGI back-end program
uses that named pipe to talk to the main application and transfer state and
control information through that mousehole.
If you look at cgi.c (again, linked in the Download section),
you'll see all the code in its full glory. The "main
program" side of ibmslides forks a second (child) process that sits around
listening to the named pipe. When something of interest comes in, that
child process messes around inside the address space of the main program to carry out whatever task needs to be accomplished. The "CGI
grabber" side of ibmslides runs only when you click a link on the Web
page;
it analyzes what you did on the Web page and generates the appropriate
strings to send to the named pipe to converse with the other half of its brain.
To test it out, simply extract the source tarball, enter the
directory thus created, then:
Listing 1. Installing the sample code
# make
# make install
# cp index.html /web
|
Now point your browser at http://a.b.c.d/ (where a.b.c.d
is the IP address of your multimedia machine) and you can play with some
simple remote control functionality on the mad multimedia appliance.
Advanced trickery
An extra tip for people with degrees in advanced cleverness: The CGI program
I've written for you detaches from the named pipe every time it exits. This
means that it is possible for another program or programs to write to the
same pipe and thereby access whatever control interfaces you expose through
this system. If you have an off-the-wall or simply custom piece of input
hardware, you can exploit this design feature by writing a daemon that
reads your hardware and posts appropriate messages to the named pipe. You
can even have the system enumerate what's attached to it at power up, and
decide which "shim" daemon to run accordingly.
The most obvious example of where this might be useful is if you want to
add an infrared remote to your widget. Infrared remote protocol decoding
and encoding functionality is provided in Linux® through LIRC (see Resources).
LIRC consists of several modules. At the lowest level are some
installable kernel modules that access various sorts of infrared hardware,
and above that layer is lircd, the daemon that decodes incoming IR protocol
data. Many people will run lircmd on top of lircd; lircmd takes the decoded
outputs from lircd and transmutes them into user interface messages such as
mouse movement or keyboard press and release events. However, it's entirely
possible to write your own program that listens directly to lircd output
and does something intelligent with it.
By the way, the original Mac mini doesn't include infrared support, so
perhaps a
Bluetooth remote might be more feasible on this platform, but the same
general principle applies.
Some downright fox-like readers will doubtless envision the
possibility of controlling farms of multimedia devices over that pipe
interface using NFS mount points. Unfortunately, I have to burst your
bubble on that one -- named pipes don't work between machines across NFS --
ten points for thinking, though. If you want to achieve the kind of
functionality I just described, instead of using a named pipe you should
use a socket. However, you'd also need to write a custom application on the
remote computer side to talk to this socket; using a general-purpose Web
interface is preferable, at least for the home-user scenario.
Usability? What's that?
At this point, a slight diversion into commercial practicality is in order,
because I'd like to bring to your attention the fact that simply
Web-enabling an appliance does not automatically make it end-user
friendly.
The modern mantra says that today's consumer understands the Internet and
hyperlinks. Therefore, adding a Web server to your product wraps its user
interface in a familiar skin, thereby inherently making your product
user-friendly. There's a large kernel of truth in this, but (if I may
continue the nut analogy) it's a macadamia kernel. For those of you who
don't know what this implies, read the appropriate link in Resources; the
salient point is that a macadamia shell takes 300lb per square inch of
pressure before it cracks.
In the good old days when I used to work on commercially produced
appliances like the one described here, the single largest waster of
telephone support hours was customers having difficulty connecting to the
device. I warn you from personal experience that it is excruciating to talk
customers through operating their routers and other networking equipment;
you're usually looking at a very large and complex problem through a very
small keyhole.
If you intend to field a device that contains TCP/IP networking features, I
strongly suggest you think about how to implement a fallback mechanism and
methods for the customer to test that all the various parts of the network
are operating correctly.
Autodetection and configuration of TCP/IP devices
Ensuring usability is a multifaceted task, but three underlying issues are:
- Connectivity (an ability for A to talk to B)
- Discovery (A determining automatically that B exists, and where it
is), and
- Service enumeration (A determining automatically what B is capable of
doing, and what protocol to use for issuing commands)
I'll start with the connectivity issue. In these articles, I've been
telling you to go do things to IP address a.b.c.d (not specified, since it
will vary according to your network setup) and assumed that you know how to
find out your multimedia machine's IP address, perhaps by looking at the
DHCP lease summary page of your router. I can get away with this by relying
on the fact that either you are all developers and technical persons who
know what to do, or you're reading for leisure and won't actually try this so it won't
matter if you can't get it working.
Most home and small-business users are going to plug your device into a
small consumer-grade router designed to share a broadband Internet
connection. This router will have a DHCP server in it to issue IP addresses
on your local subnet. Unfortunately, all sorts of problems can crop up with
these devices, especially if you're trying to attach an embedded system. It
seems that manufacturers of consumer computer peripherals take a perverse
delight in seeing just how far they can deviate from RFCs and still have
a Windows® or Mac system recognize their appliance without complaints.
How do you handle the situation where your device can't acquire an IP
address from a DHCP server? This occurrence might mean either that there is
a problem with the server, or it's incompatible in some bizarre way, or it
might simply mean that there is no DHCP server at all. Generally what you
will do is have a manually configured fallback IP address, netmask, and
gateway; if you don't get anything from a DHCP server, use the fallback
parameters. Ideally these parameters should be user-configurable.
For remote troubleshooting purposes, you should supply an Ethernet crossover cable with your device. If you have any
question about functionality, you can establish whether the device is
working very simply: just tell the user to unplug his or her computer and
the appliance in question from the network, connect them directly to one
another using the crossover cable, and restart both machines.
What IP address range should you use under such circumstances? Modern
operating systems by default implement APIPA (see Resources) so that they
can provide at least rudimentary network functionality in an unmanaged
network environment with no DHCP server. In brief, APIPA specifies that if
an IP address cannot be acquired, the device will assign itself a random
address in the range 169.254.0.1 through 169.254.255.254. The device is
supposed to use ARP to verify that the random address it chose is not
already spoken for. In practice, I have seen devices that choose IP
addresses based on other factors, say, the lowest 16 bits of the MAC
address.
These devices simply assume that they are not going to collide
with anyone else, which is a fairly safe assumption if you're only using
this feature as a special one-to-one test mode with only one PC and one
appliance on the network. Otherwise, sometimes it's not so safe...
Detecting services
By whatever means, you now have your hypothetical consumer appliance
connected to a network in such a way that it can be contacted by a computer
on the same network. However, this brings you to the next two problems, namely,
discovery and service enumeration. The holy grail of discovery protocols is
to be able to plug a new appliance into the network, have an icon for it
appear on the user's desktop, and arrange things so that double-clicking
the icon brings up the embedded Web interface of the appliance, or some
other interface appropriate to the particular device.
There are several attempts to standardize on methods for discovering and
interacting with appliances on disparate sorts of networks (see Resources). However, at
this time I'd characterize them all as basically unsupported in the real
world. (Yes, some appliances support these protocols, but the
market is very fragmented. It is fair to say that none of these standards
has yet achieved critical mass.)
The Universal Plug'n'Play (UPnP) protocol was supposed to be the be-all and
end-all of discovery, but in my opinion the standard overreaches and fails
to preserve either simplicity or ease of interoperability with other
systems. (I wrote a scathing article about these issues five years ago; see
Resources. From studying the UPnP home page today, I can see that almost
none of my original annoyances have been dealt with in the interim.)
Apple's Bonjour protocol is another system that enjoys some market
penetration, though its use seems to be confined mainly to networked
printers. There are several others, as well -- take your pick, according to
what industry you work in. UPnP is the only such protocol supported
natively by Windows, but most users run with it disabled for security reasons
(see Resources for more information).
Besides general-purpose discovery protocols, you can also roll your own
proprietary system. (The software shipped with the Kuro Box, which I
use in the aforementioned Migrating x86 to PowerPC series, works this way.) In brief: you write a
proprietary application for whichever host operating systems you care
about, and that sends a magic broadcast message to the entire network. When one
of your appliances hears the magic broadcast, it holds off for a random
time (to reduce collisions), then sends a "hi, I'm here" packet back to the
computer that originated the broadcast. The computer picks up that response
packet and then uses proprietary communications methods to connect to the
appliance.
Some routers let themselves be "discovered" by the interesting technique of
emulating a DNS -- if you go to your browser and enter "router," the router
itself intercepts that DNS lookup (rather than passing it upstream) and
returns its own IP address. This option isn't really available to appliances
like the one in this series, but it's an interesting idea and might be useful to you.
Local is just a
special kind of remote
So much for remote control, for a while, anyway, and on to local
user interfaces. Way, way back in the introduction to this series, I waxed
quite lyrical on the importance of choosing the right user interface for
the right job. I also mentioned that a nifty, but rarely used method of
achieving the same UI on the appliance as on the remote connection is
simply to build an embedded browser into the appliance itself. Since you now
have a functional HTTP-based user interface, you can exploit that by
installing a local Web browser on your box.
Obviously you need some kind of input device to invoke and interact with
this browser. Since this series works with off-the-shelf hardware, I don't want to get into too many of the exotic options you might
have for physical interface methods. In particular, I want to stick with
code that you can actually run on hardware you're likely to have lying
around, without requiring you to purchase special-purpose peripherals.
Hence, for the purposes of this discussion, I assume that you're
using a touchscreen monitor, and you've configured X to work with it as a
pointing device. The crafty part of this decision is that if you don't in
fact own such hardware, you can simulate it with a mouse. (Note, by the
way, that this is why I designed the interface page to use only clickable
items, without any text entry fields.)
You might consider a plethora of Web browsers, but you probably
don't want to leap for a full-featured browser like Firefox. A big browser
like that takes a relatively long time to launch, which is annoying -- if
the user presses a button on a physical piece of hardware, a response is
typically expected more or less immediately. One workaround for this is to
keep the browser loaded at all times and merely bring it to the foreground
when you need it -- but this has a big downside in terms of the RAM or
paging file hit (or both) required to keep such a large program permanently resident.
Two small browsers
The two Web browsers I've linked in Resources are ELinks and Links2. Both
of these are derivatives of the faithful lynx; they're both tiny and
elegant, and they are both a good choice for embedded use. They have
slightly different applications, however -- ELinks is a simple text browser,
good for very constrained systems or systems that lack a graphical display.
I'd recommend ELinks (adapted) if your system has multiple display elements
and you need to run the browser on, say, a smaller monochrome or
text-only display. For a large graphical display like the one we're using,
Links2 is more attractive (and more closely approximates what the user will
see remotely on a regular desktop browser).
You don't actually need to build ELinks, since it's included in the Linux
distribution you installed back in the first article. However, if you wish
to build a newer version, just unpack the tarball and run:
Listing 2. Installing ELinks
# ./configure
# make
# make install
|
To build Links2, simply unpack the source tarball and run:
Listing 3. Installing Links2
# ./configure --enable-graphics
# make
# make install
|
The configure script will automatically detect framebuffer, SDL, and X
support on the system as you have built it so far.
I've modified ibmslides so that when you click the mouse button (the left
button, if your mouse has more than one), the program tries to run
/usr/local/bin/links -- if that's not found, then it attempts to run
/usr/bin/elinks. The slide show itself is paused while the browser runs,
otherwise the graphics rendering code would overwrite the screen contents
and obliterate the browser window. Normal operation resumes after you quit
the browser.
Note that the exact command line used to run Links2 is:
Listing 4. Invoking Links2
/usr/local/bin/links -g http://127.0.0.1/ -mode 1024x768 -enable-javascript 1
|
This forces the browser to run in graphical mode.
Stay tuned for next time!
That's it for this episode. In the next, and possibly last, installment of
this series, I'll talk about adding security to embedded
appliances for this application specifically and also some more general
commentary. In a world where every sample project has an offhanded comment
that "a real system should be more secure," it'll make an interesting change.
Downloads
The downloads for this article are being updated. Please try to download later.
Resources Learn
Get products and technologies
-
Download the source code referenced in this article from the table above.
The usual
warning applies to Internet Explorer users - make sure to save the file as
something.tar.gz!
-
Download the latest version of ELinks from the
ELinks home page.
The specific version I used was elinks-0.11-20060703.
-
Download the latest version of Links2 from
the Links2 home page. In this text, I am using links-2.1pre22. The download page includes a lot of dire warnings about dependencies and such -- don't worry, all the
libraries you need are already included in the Linux distribution we're using.
Discuss
About the author  | |  | Lewin A.R.W. Edwards works for a Fortune 50 company as a wireless security/fire safety device design engineer. Prior to that, he spent five years
developing x86, ARM and PA-RISC-based networked multimedia appliances at
Digi-Frame Inc. He has extensive experience in encryption and security
software and is the author of two books on embedded systems development. |
Rate this page
|