TOSSIM Lesson: Using the simulator to develop TinyOS components

TOSSIM, packet injection, packet sniffing, and dbg


Introduction

TOSSIM is the TinyOS simulator. TOSSIM compiles directly from TinyOS code, and is by default automatically compiled (make pc). TOSSIM has an extensible network, ADC, and spatial model interface. The default provided models are very simplistic, but the interfaces allow models of significant complexity if needed.

TOSSIM provides several mechanisms for interacting with the network; packet traffic can be monitored, packets can be statically or dynamically injected into the network, and TinyOS call invocation can be logged.

Because TinyOS applications run as native code under TOSSIM, traditional debugging tools can be used.

Building and Running the Application

TOSSIM is compiled by typing make pc in an application directory. In addition to the expected TinyOS components, a few simulator-specific files are compiled, such as external_comm.c, which contains the code for communicating with the simulator over network sockets.

Enter the apps/router/router directory. This application is an example use of a simple beacon-based ad-hoc routing algorithm. The program listens for two types of AM messages: type 5 for beacons and type 6 for data. When a mote hears a beacon, it checks to see if it currently has a network parent. If the mote does not currently have a parent in the network tree, it makes the source of the beacon its parent and retransmits it. If the mote has a parent already, it does nothing. After a few system clock ticks (currently 8), a mote reverts to a no-parent state.

When a component tries to send a data message, the routing component examines if it currently has a parent. If so, it sends the message to it. As the packet travels up the routing tree, route information is stored in the packet. A base station can then examine this information and see the last few (currently 5) hops the packet took, which provides information about the network topology.

The code for this algorithm resides in apps/router/shared/AM_ROUTE.c The apps/router/router application does not send any data messages; it only receives and retransmits beacons. There are additional applications in the apps/router directory, which we will not discuss here.

In the application directory, type make pc. You should see the simulator build. The simulator executable lives in the binpc subdirectory, and is named main. Type binpc/main. You should see a short usage notice with all of the simulator run options. Type binpc/main -h for a more descriptive list:


Usage: binpc/main [options] <num_nodes>
[options] are:
-h, --help Display this message.
-k <kb> Set radio speed to <kb> Kbits/s. Valid values: 10, 25, 50.
-r specifies a radio model (simple is default)
-l invocation logging: will block for connect on port 10583
-e <file> use <file> for eeprom; otherwise anonymous file is used
-p <sec> pause <sec> seconds on each system clock interrupt
<num_nodes> number of nodes to simulate
Known dbg modes: all, boot, clock, task, sched, sensor, led, route, am, crc, packet, encode, radio, logger, adc, i2c, uart, prog, sim, queue, simradio, hardware, simmem, usr1, usr2, usr3, temp, error, none

Most of these options are outside the scope of this tutorial; they're all discussed in the TOSSIM manual. Right now, the only important ones are -h and the required argument <num_nodes>.

Try typing "binpc/main 1" (without the quotes). This will run the simulation with a single mote. After the simulator boots (which may take a few moments in Cygwin), you'll see a lot of debugging information stream by. Typing control-C will stop the simulation. If you look at the simulator output, most of it pertains to events and radio clock ticks; this is because the radio clock ticks at 20KHz when listening for a packet. If you look carefully at the times stated for the tick events, you'll see that they're 50 microseconds apart. The interval of 200 means 200 clock cycles; since the processor is 4MHz, this equal to 50 microseconds. Sample output:


0: Popping event for mote 0 with time 0:0:0.11075450.

0: Setting TOS_LOCAL_ADDRESS to 0

0: RADIO: tick event handled for mote 0 at 0:0:0.11870450 with interval of 200.

0: Inserting event with time 0:0:0.11080450.

0: mote 0 in state 0

0: RFM: Mote 0 got bit 0

0: TOS_schedule_task: 0 empty

0: Popping event for mote 0 with time 0:0:0.11080450.

0: Setting TOS_LOCAL_ADDRESS to 0

0: RADIO: tick event handled for mote 0 at 0:0:0.11875450 with interval of 200.

0: Inserting event with time 0:0:0.11085450.

0: mote 0 in state 0

0: RFM: Mote 0 got bit 0

0: TOS_schedule_task: 0 empty

Time is represented as hours:minutes:seconds. The zero preceding each line states which mote is the source of the debugging statement; since the simulation only had one mote, this is always zero. Run "binpc/main 2", and you'll see both mote 0 and mote 1 printing output. In the simulator, motes start one after another, spaced by a short random period; this is to prevent perfect synchronization. Therefore, if you stop the simulator very soon after it starts, you might not see any of mote 1's messages, as it hadn't actually booted yet. Let the simulator run for a few seconds and you should see messages from mote 1.

Output Configuration

If you look at the time values being printed out, the simulator is running really slowly; we've been able to simulate 100 motes at real life speed (on a Linux desktop) . With so much debugging output, almost all of the simulator processing is printf(). Also, most of the data is usually not needed. For example, if you're debugging a packet routing algorithm, radio bit events aren't very useful. For this reason, you can specify which output messages should be displayed with the environment variable DBG. When the simulator boots, it reads the environment variable and sets which messages will be printed and which won't (it's a bit-mask).

The available debugging modes are at the end of the simulator help message:

Known dbg modes: all, boot, clock, task, sched, sensor, led, route, am, crc, packet, encode, radio, logger, adc, i2c, uart, prog, sim, queue, simradio, hardware, simmem, usr1, usr2, usr3, temp, error, none

These modes can be combined by using a comma-delimited string. For example, typing "export DBG=boot,clock" (in bash) will enable boot and system clock messages. usr1, usr2, and usr3 are intended for use by applications. Try setting DBG to clock,boot and running the simulator with one mote. You can see components being initialized, then system clock interrupts being fired twice a second; note the difference in the rate the simulator runs at from when radio clock events were being displayed. The simulator is compiled with no optimizations by default; this is to make debugging easier (optimized code can be hard to debug).

Injecting Packets into the Network

Currently, our simulated motes do very little. The beacon-based routing algorithm requires the root of the routing tree to generate an initial beacon. However, all motes in TOSSIM run the same code image; we need a way to create an initial seed beacon in the network. We can do so by using TOSSIM's external communication functionality.

Run the simulator for a single mote with DBG set to sim. You'll see a bunch of messages pertaining to the different models (RFM, ADC, etc.), followed by a series of messages about connections. These refer to TCP sockets the simulator either opens or tries to open on boot. The exact use of each of these sockets is described in the TOSSIM user's guide, which exists in the doc/ directory of the release. The one we're interested in here is port 10579, which the simulator message says is for "dynamic packet injection." Dynamic packet injection allows you to connect to the simulator and inject a packet into the network as it's running. Static packet injection reads in a a series of packets once, at boot, and injects them at their specified times. The former is good to poke at your program and see its behavior, while the latter is good for repeatable results. Keep the simulator running, or if you stopped it, run it again.

There's a Java GUI tool that makes injecting packets into TOSSIM fairly easy. Compile net/tinyos/tossim/NetworkInjector.java and run it. A window with two tab panes will pop up. The different tab panes refer to different packet types. Don't worry about the BLESS packets, which are for a protocol built on top of AM; we want to send an AM packet. You can see the four AM fields specified, as well as the AM data payload of 32 bytes. (An AM packet is 38 bytes; two go to the destination, two to the CRC, one to the group and one to the type.) The simulator bypasses the CRC check for injected messages, so you don't have to calculate the CRC.

If you recall, router uses two AM types: 5 and 6. Beacon messages are type 5. Change the AM_type field in the NetworkInjector to 5, and click the "Inject" button. The simulator should say that the incoming packet had a specified time of 0; this causes the simulator to make it arrive immediately, which is not at time 0. We have our debugging output set to only simulator messages; while we can see the packet arrive at the mote, but have no idea what happened. Let's take a look at application level messages.

Quit the NetworkInjector, then stop the simulator. It's important you do it in this order; otherwise, the TCP port enters a waiting state on the OS, and you can't reuse it for a minute or so. If you get the order wrong, the simulator will block until it can open the server port 10579, retrying every 10 seconds. Change your DBG settings to usr1,sim,am. Don't run the simulator just yet; we want to see what debug messages usr1 will give us.

Open AM_ROUTE.c. Look for dbg statements. You'll notice they're of the form dbg(flag, (message)), where message is a standard printf(3) argument list. There are a few statements, mostly dealing with the acquisition or rejection of parents from beacon messages. The clock event is bound to usr2, while all the others are bound to usr1. A complete list of the flags and their DBG environment bindings can be found in tos/include/dbg_modes.h. Try adding a DBG_TEMP message at component initialization. Recompile the application, set your DBG mode to temp and see your message appear.

Since we've set DBG to include usr1, we should see debugging messages associated with that mode. Start up the simulator again, then the NetworkInjector. Try injecting a message of AM type 0 (the default). You'll see the simulator output that mote zero received an AM packet, but the application component didn't do anything with it; it uses type 5 for beacons. Change the AM type to 5 and inject again. You should see these messages:

0: got messaged number 5 for dispatch
0: route set to 0
0: Sending message: ffffffff, 5

If you look back at AM_ROUTE.c, you can see where route set to 0 comes from.

Try injecting two packets in succession. If a long enough time has passed, the mote should have forgotten its parent; the first will set the route, and the second will result in a different message:

0: got messaged number 5 for dispatch
0: route already set to 0 0: Sending message: ffffffff, 5

Since the mote already has a parent, it doesn't pay attention to the second message and doesn't transmit a beacon.

So far, we've only been simulating a single (or a pair) of motes. The default simulator settings allow up to a thousand motes to be simulated at once. Let's try simulating a small network of 10 motes. Since the AM messages are lengthy, change your DBG setting to just sim,usr1. Run the simulator with ten motes: binpc/main 10. Start up the NetworkInjector and inject a single type 5 packet. You'll see that mote 0 receives the packet, then transmits a beacon. All of the other motes here mote 0's beacon and make it their parent. The default radio model for TOSSIM is a single cell with no bit errors; all motes hear one another perfectly. The format of routing beacon messages means that sending the default AM message (filled with zeroes) makes mote 0 think it's heard itself as a parent; in the real world, we'd have a base station which has different logic (knows it's the root).

Visualizing Network Traffic

We could turn on AM debugging messages to see the packets being sent, but that would obscure other data about what's going on. There's a Java GUI tool that allows us to sniff the network traffic and see what's being sent. It needs to be started before the simulator; the simulator tries to connect to a certain port at startup. Compile and run net/tinyos/tossim/TossimGUI.java.

TossimGUI visualizes packets that have been sent, not received, in the network. The simulator hasn't started, so there shouldn't be anything displayed. Start up the simulator with ten motes, then the NetworkInjector. Inject a single packet of type 5. You should see ten messages sent; the first is from mote 0, when it sends out its beacon, then one for each of the other nine motes. You can see the data being sent in the beacon. Atmel processors are little-endian, so the more significant byte comes first. Click on the packet that mote 0 sent. You should see the AM broadcast address (0xffff), followed by the type (0x05), followed by your local group. The next field in the packet is the tree depth. Mote 0 thinks it is at depth 1, so the next two bytes are 0x01 and 0x00. The two bytes after that contain the mote's address, which is zero. This is more clear on one of the subsequent beacons. For example, if you look at the beacon that mote 7 sent, you can see that it's depth field says 2. The first address stored is mote 0; the second is mote 7 (0x07 0x00).

Try injecting another packet. If the network has timed out, you can see the beacon broadcast again; if not, you'll see simulator debug messages saying that the motes already have routes set.

This simple radio model doesn't really let us see complex behavior; we automatically form a depth 2 tree. The simulator has two additional radio models, static and space. We're going to use the former; the latter is just a demonstration that a spatial-based connectivity model can be implemented with the TOSSIM radio model abstraction.

Static Routes

The static radio model allows you to specify an undirected graph of mote connectivity. As with the simple model, bits are perfectly transmitted, and there is no network noise. At startup, the simulator tries to read in the file cells.txt; if no such file exists, it defaults to the simple radio model. The format of cells.txt is a set of colon-delimited pairs A:B. Each pair means that A and B can hear one another. For example, a file that looked like this:

0:1 1:2 2:3 3:4 4:5 5:0
would create a ring of 6 motes. Create this file in apps/router/router/cells.txt. Start up TossimGUI, then the simulator, but specify this option: -r static (like this: binpc/main -r static 6). Inject a beacon packet into the network.

For some reason, mote 3 never transmits a beacon. If you look at the simulator output, it never set a route. Why? It's connected to mote 4 and 2, both of which sent beacons.

Mote 3 is victim of the hidden node problem. If you look at the timestamps in the packet visualization, you can see that mote 4 and 2 send their packets very close to one another. Mote 3 is, at one point, hearing the union of their bits.

Quit all the applications (remember: NetworkInjector before simulator). Set your DBG mode to usr1,crc. This will provide us with information about the packet-level CRC checks. If mote 3 is hearing two packets at once, it's unlikely the bytes heard will pass CRC.

Start up the applications as before, and inject a packet. You should see messages similar to:

0: route set to 0
0: CRC: 1132
5: route set to 0
5: CRC: 37b4
1: route set to 0
1: CRC: ffffa672
2: route set to 1
2: CRC: ffffc16b
4: route set to 5
4: CRC: ffffe2a7
1: route already set to 0
3: crc check failed: ffffa24c, ffffd56b
5: route already set to 0

Part of the cause of this is that the random wait before transmitting is much shorter than the time it takes to transmit a packet at 10Kb. The simulator can also simulate a 25 or 50 Kb radio stack; it still uses the GENERIC_COMM stack, though. This functionality allows you to simulate a high-speed mica stack while still using the standard 10Kb stack components. At 50Kb, the random wait time overwhelms the transmission time. Try running the simulator as so: binpc/main -kb 50 -r static 6. Inject a beacon packet, and mote 3 can associate with the network, because the packets from 2 and 4 are spaced out enough in time.

Try changing the topology to a seven mote ring; every mote should join the routing tree with a 10Kb stack.