Basic EmStar Example Program: Ping

In this first example, we'll see how to run one of the example programs provided with EmStar: ping. It actually consists of two pieces. The first a ping client (ping), which broadcasts a ping requests every second and prints out any replies that it receives. The second is a ping daemon, or server (pingd) that listens to the network and replies to ping requests whenever it receives one. Unlike the ping client, which sends broadcasts, the pingd sends unicast replies directed at the source of the ping request.

All EmStar programs that want to use the network to send or receive packets use what is called the link interface. This is a simple API for sending and receiving packets that is independent of the underlying physical network. The network might be a MoteNIC, a simulator, or (as in this example) a binding to a real Ethernet network. (Ethernet is not often used in the sensor network itself, but is very useful for debugging -- like we're doing now.) By writing against the link interface, programs don't have to change as they move from simulation to reality and back again.

The ping application is a useful example because it demonstrates how to create an application-layer protocol implemented above the link layer. That is, ping defines its own application layer protocol headers, protocol semantics, and so forth. This is a technique that almost any application will need to use.

The example also demonstrates emrun, the emstar run control program -- if you want to run multiple daemons, which have interdependencies, this program runs all the programs in the right order, and is a central control for debug/logging and other useful things as we'll see later.

To try running ping/pingd, pick 2 computers (say, dragon and tobiko). First, run "udpd" on both machines -- this is a program that creates a binding between the generic EmStar link interface and a real IP network, using UDP. After you run the build (check out the code, and type "make"), it'll be in obj.i686-linux/link/udpd. After UDPd is running, run obj.i686-linux/link/examples/pingd on one machine, and finally obj.i686-linux/link/examples/ping on the other. The ping process should report that it is seeing ping replies. When you hit Ctrl-C, you'll get summary statistics.

Now, in this simple case, manually running udpd and then pingd afterwards is not too much of a pain, but you can imagine that it'll get confusing once we have 5, 10, or 20 daemons running (e.g., routing daemons, time synchronization, acoustic localization, sensor calibration, data acquisition, aggregation, distributed storage... all running at the same time). That's where 'emrun' comes in. emrun is a program that takes a config file describing all the processes you want, and their interdependencies, and it runs them in the right order (waiting for one to finish booting before running the next one).

In the emstar/link/examples/ping directory is a file called 'pingtab', which is an example configuration file for emrun. This file tells emrun that we want to run UDPd first, then run pingd after UDPd is ready. To use it, log into both machines again, and type:

  1. cd into the obj.i686-linux directory
  2. Type emrun/emrun ../link/examples/ping/pingtab
emrun/emrun is the path to the emrun program itself; the argument is the path to the configuration file. Emrun will tell you that it is booting, and run both daemons.

Now, from another shell, type "cat /dev/emrun/status". You will see a display, generated by the emrun program, that describes what programs it is running and their current status. Finally, run the obj.i686-linux/link/examples/ping program again; you should see similar results as before. Now, hit control-C and emrun will terminate; it'll shut down both UDPd and pingd, and the /dev/emrun/status file will magically disappear.

These programs should, hopefully, serve as good examples for how you might get started in writing your own software that interacts over the network. The source for the ping and pingd example programs are in the emstar/link/examples/ping directory. They are extensively commented, so hopefully are not too confusing. (Tip: use the LXR interface to browse the code; it'll make it easy for you to go from a function call to that function's definition.) ping and pingd are extensively commented, so hopefully are not too confusing.

Basically, you use link_open to open a new link. You pass it a link_opts struct that specifies things like the callback function you'd like to have activated every time a packet arrives. You can also (optionally) specify what packet types you'd like (similar to port numbers in UDP), and other things that aren't as important for now.

If link_open succeeds, it gives you a pointer to a "link_context_t". You can later use this as an argument to "link_send()" if you want to send a packet.

Sending packets is done by constructing the packet you want to send, including its headers, then calling link_send. The link layer API is centered around a header called "link_pkt_t" (in link/include/link.h). This is the struct that you pass to the link layer when you want to send a packet, and that it passes back to you when a packet is received. When sending a packet, you should fill in the packet type and destination fields. The link_pkt_t header can be followed by as much data as you like, up to 64K. The 3rd argument to link_send is the amount of data you are sending (not including the sizeof the link_pkt_t header itself).

You can use g_dev_timer_add() to create periodic (timer-based) callbacks. And, you can specify a callback that gets run when the user asks the program to shut down. These all are commented in the example code.

Of course, right now, it might not seem that there is much advantage to using this link interface rather than something more familiar (such as Berkeley sockets). But, as we'll see in the next section, the link interface has an important advantage: it can be easily run as part of a simulation, without changing any of the code.



Last modified by jelson, 28 January 2003