Difference between revisions of "The TinyOS printf Library"

From TinyOS Wiki
Jump to: navigation, search
(The TinyOS <code>printf</code> Library)
(Using the TinyOS <code>printf</code> Library)
Line 50: Line 50:
 
=Using the TinyOS <code>printf</code> Library=
 
=Using the TinyOS <code>printf</code> Library=
  
To help guide the process of using the <code>printf</code> library, a <code>TestPrintf</code> application has been created. At present, this application is not included in the official TinyOS distribution (<= 2.0.2). If you are using TinyOS from a cvs checkout, you will find it located under <code>apps/tutorials/Printf</code>. Otherwise, you can obtain it from cvs by running the following set of commands from a terminal window:
+
To help guide the process of using the <code>printf</code> library, a tutorial application has been created. Navigate to the <code>tinyos-2.x/apps/tutorials/Printf</code> directory to follow along.
cd $TOSROOT/apps/tutorials
+
<br>
cvs -d:pserver:anonymous@tinyos.cvs.sourceforge.net:/cvsroot/tinyos login
 
cvs -z3 -d:pserver:anonymous@tinyos.cvs.sourceforge.net:/cvsroot/tinyos co -P -d Printf tinyos-2.x/apps/tutorials/Printf
 
Just hit enter when prompted for a CVS password. You do not need to enter one.
 
  
If you are not using cvs, you will also have to apply the patch found [http://sing.stanford.edu/klueska/tinyos-2.0-printf.patch here] in order to allow the <code>printf</code> library to compile correctly for atmega128x based platforms (i.e. mica2, micaz):
+
The <code>Printf</code> application demonstrates everything necessary to use the <code>printf</code> library. Go ahead and open the <code>TestPrintfAppC</code> configuration to see how simple the wiring is.  Notice that unlike previous versions of <code>printf</code>, no explicit wiring to printf specific components are necessary.
cp tinyos-2.0-printf.patch $TOSROOT/..
+
  configuration TestPrintfAppC{
cd $TOSROOT/..
+
  }  
patch -p0 < tinyos-2.0-printf.patch
+
  implementation {
Note that you may have to use 'sudo' when applying the patch if you run into permission problems.
+
    components MainC, TestPrintfC;
 
+
    TestPrintfC.Boot -> MainC;
----
+
  }
 
+
All that is needed is to wire the application's <code>Boot</code> interface into <code>MainC</code>. <code>MainC</code> takes care of all the wiring necessary to bring up the Printf service and allow you to make calls to <code>printf()</code> and <code>printfflush</code>. As mentioned before, <code>printf()</code> and <code>printfflush()</code> commands can be called from any component as long as they have included the <code>"printf.h"</code> header file.
The <code>TestPrintf</code> application demonstrates everything necessary to use the <code>printf</code> library. Go ahead and open the <code>TestPrintfAppC</code> configuration to see how the various interfaces provided by the <code>PrintfC</code> component have been wired in. You will want to do something similar in your own applications.  
 
configuration TestPrintfAppC{
 
}
 
implementation {
 
  components MainC, TestPrintfC, LedsC;
 
  components PrintfC;
 
 
  TestPrintfC.Boot -> MainC;
 
  TestPrintfC.Leds -> LedsC;
 
  TestPrintfC.PrintfControl -> PrintfC;
 
  TestPrintfC.PrintfFlush -> PrintfC;
 
}
 
First, the <code>PrintfControl</code> interface has been wired in to enable turning on and off the service providing <code>printf</code> functionality. Turning on the <code>Printf</code> service implicity turns on the serial port for sending messages. Second, the <code>PrintfFlush</code> interface has been wired in to allow the application to control when <code>printf</code> messages should be flushed out over the serial line. In this application, all <code>printf()</code> commands are called directly within the <code>TestPrintfC</code> component. In general, <code>printf()</code> commands can be called from any component as long as they have included the <code>"printf.h"</code> header file.
 
  
 
----
 
----
Line 83: Line 67:
  
 
To install the application on the mote, run the following set of commands.
 
To install the application on the mote, run the following set of commands.
  cd $TOSROOT\apps\tests\TestPrintf
+
  cd $TOSROOT\tutorials\Printf
 
  make telosb install bsl,/dev/ttyUSBXXX
 
  make telosb install bsl,/dev/ttyUSBXXX
You will notice during the installation process that a pair of java files are compiled along with the TinyOS application. The first java file, <code>PrintfMsg.java</code>, is generated by <code>mig</code> to encapsulate a TinyOS <code>printf</code> message received over the serial line (for more information on mig and how it generates these files, please refer to the section entitled [[Mote-PC_serial_communication_and_SerialForwarder#MIG:_generating_packet_objects|"MIG: generating packet objects"]] in [[Mote-PC_serial_communication_and_SerialForwarder|lesson 4]]). The second file, <code>PrintfClient.java</code> is used to read <code>printf</code> messages received from a mote and print them to your screen.
 
  
To see the output generated by <code>TestPrintf</code> you need to start the <code>PrintfClient</code> by running the following command:
+
To see the output generated by the <code>Printf</code> tutorial application you need to start the <code>PrintfClient</code> by running the following command:
cd $TOSROOT\apps\tests\TestPrintf
+
  java net.tinyos.tools.PrintfClient -comm serial@/dev/ttyUSBXXX:telosb
  java PrintfClient -comm serial@/dev/ttyUSBXXX:telosb
 
 
After resetting the mote, the following output should be printed to your screen:
 
After resetting the mote, the following output should be printed to your screen:
 
  Hi I am writing to you from my TinyOS application!!
 
  Hi I am writing to you from my TinyOS application!!
Line 95: Line 77:
 
  Here is a uint16: 12345
 
  Here is a uint16: 12345
 
  Here is a uint32: 1234567890
 
  Here is a uint32: 1234567890
I am now iterating: 0
 
I am now iterating: 1
 
I am now iterating: 2
 
I am now iterating: 3
 
I am now iterating: 4
 
This is a really short string...
 
I am generating this string to have just less than 250
 
characters since that is the limit of the size I put on my
 
maximum buffer when I instantiated the PrintfC component.
 
Only part of this line should get printed bec
 
 
Note that the 'tty' device (i.e. COM port) specified when starting the PrintfClient MUST be the one used for communicating with a mote over the serial line. On telos and mica motes this is the same port that the mote is programmed from. Other motes, such as eyesIFX, have one port dedicated to programming and another for communication. Just make sure you use the correct one.
 
Note that the 'tty' device (i.e. COM port) specified when starting the PrintfClient MUST be the one used for communicating with a mote over the serial line. On telos and mica motes this is the same port that the mote is programmed from. Other motes, such as eyesIFX, have one port dedicated to programming and another for communication. Just make sure you use the correct one.
  
Line 113: Line 85:
 
Go ahead and open up <code>TestPrintfC</code> to see how this output is being generated.
 
Go ahead and open up <code>TestPrintfC</code> to see how this output is being generated.
  
Upon receiving the booted event, the <code>Printf</code> service is started via a call to <code>PrintfControl.start()</code>
+
Upon receiving the booted event, the <code>Printf</code> service has already been started up and initialized and we are ready to start printing messagesThe following code prints the messages shown above and flushes them out over the serial line.
event void Boot.booted() {
+
  event void Boot.booted() {
  call PrintfControl.start();
+
        printf("Hi I am writing to you from my TinyOS application!!\n");
  }
+
        printf("Here is a uint8: %u\n", dummyVar1);
Once the <code>Printf</code> service has been started, a <code>PrintfControl.startDone()</code> event is generated. In the body of this event the first four lines of output are generated by making successive calls to <code>printf</code> and then flushing the buffer they are stored in.
+
        printf("Here is a uint16: %u\n", dummyVar2);
event void PrintfControl.startDone(error_t error) {
+
        printf("Here is a uint32: %ld\n", dummyVar3);
  printf("Hi I am writing to you from my TinyOS application!!\n");
+
        printfflush();
  printf("Here is a uint8: %u\n", dummyVar1);
+
  }
  printf("Here is a uint16: %u\n", dummyVar2);
+
 
  printf("Here is a uint32: %ld\n", dummyVar3);
+
Remember that the default printf buffer size is only 250 bytes, so don't try to print more than this at one time or you may end up cutting off the end of your string. This buffer size is configurable, however, by specifying the proper CFLAGS option in your Makefile.
  call PrintfFlush.flush();
+
  CFLAGS += -DPRINTF_BUFFER_SIZE=XXX
}
 
Once these first four lines have been flushed out, the <code>PrintfFlush.flushDone()</code> event is signaled. The body of this event first prints the next 5 lines in a loop, followed by the last five lines. Finally, once all lines have been printed, the <code>Printf</code> service is stopped via a call to <code>PrintfControl.stop()</code>.
 
event void PrintfFlush.flushDone(error_t error) {
 
  if(counter < NUM_TIMES_TO_PRINT) {
 
    printf("I am now iterating: %d\n", counter);
 
    call PrintfFlush.flush();
 
  }
 
  else if(counter == NUM_TIMES_TO_PRINT) {
 
    printf("This is a really short string...\n");
 
    printf("I am generating this string to have just less <font color="red">...</font>
 
    printf("Only part of this line should get printed bec <font color="red">...</font>
 
    call PrintfFlush.flush();
 
  }
 
  else call PrintfControl.stop();
 
  counter++;
 
}
 
Notice that the last line of output is cut short before being fully printed. If you actually read the line printed above it you can see why. The buffer used to store TinyOS <code>printf</code> messages before they are flushed is by default limited to 250 bytes. If you try and print more characters than this before flushing, then only the first 250 characters will actually be printed. This buffer size is configurable, however, by specifying the proper CFLAGS option in your Makefile.
 
CFLAGS += -DPRINTF_BUFFER_SIZE=XXX
 
Once the the <code>Printf</code> service has been stopped, the <code>PrintfControl.stopDone()</code> event is signaled and Led 2 is turned on to signify that the application has terminated.
 
event void PrintfControl.stopDone(error_t error) {
 
  counter = 0;
 
  call Leds.led2Toggle();
 
  printf("This should not be printed...");
 
  call PrintfFlush.flush();
 
}
 
Notice that the call to <code>printf()</code> inside the body of the <code>PrintfControl.stopDone()</code> event never produces any output. This is because the <code>Printf</code> service has been stopped before this command is called.
 
  
 
=Conclusion=
 
=Conclusion=

Revision as of 15:44, 23 June 2008

This lesson demonstrates how to use the newest version of the printf library located in tos/lib/printf to debug TinyOS applications by printing messages over the serial port.

This tutorial replaces older versions of the tutorial written for previous versions of TinyOS.

Legacy versions are listed below:


Overview

Anyone familiar with TinyOS knows that debugging applications has traditionally been a very arduous, if not stressful process. While simulators like TOSSIM can be used to help verify the logical correctness of a program, unforseen problems inevitably arise once that program is deployed on real hardware. Debugging such a program typically involves flashing the three available LEDs in some intricate sequence or resorting to line by line analysis of a running program through the use of a JTAG.

It is common practice when developing desktop applications to print output to the terminal screen for debugging purposes. While tools such as gdb provide means of stepping though a program line by line, often times developers simply want to quickly print something to the screen to verify that the value of a variable has been set correctly, or determine that some sequence of events is being run in the proper order. It would be absurd to suggest that they only be allowed three bits of information in order to do so.

The TinyOS printf library provides this terminal printing functionality to TinyOS applications through motes connected to a pc via their serial interface. Messages are printed by calling printf commands using a familiar syntax borrowed from the C programming language. In order to use this functionality, developers simply need to include a single component in their top level configuration file (PrintfC), and include a "printf.h" header file in any components that actually call printf().

Currently, the printf library is only supported on msp430 and atmega128x based platforms (e.g. mica2, micaZ, telos, eyesIFX). In the future we hope to add support for other platforms as well.

The TinyOS printf Library

This section provides a basic overview of the TinyOS printf library, including the components that make it up and the interfaces they provide. In the following section we walk you through the process of actually using these components to print messages from a mote to your pc. If you dont care how printf works and only want to know how to use it, feel free to skip ahead to the next section.


The entire printf library consists of only 4 files located in the tos/lib/printf/2_0_2 directory: one module, one configuration, one interface file, and one header file.

  • MainC.nc -- A shadowed version of the MainC system component that wires in and automatically powers up the printf subsystem
  • PrintfC.nc -- Configuration file providing printf functionality to TinyOS applications
  • PrintfP.nc -- Module implementing the printf functionality
  • printf.h -- Header file specifying the printf message format and size of the flush buffer


Printf functionality can be added to any application by simply #including the 'printf.h' header file. After this header file has been included, printf commands can be invoked by calling printf(), and flush commands can be invoked by calling printfflush.

Below is a graph of the MainC and PrintfC configurations that make this all possible. The MainC component redirects the Boot.booted() event through the PrintfC component so that it can power up the serial port and get the printf buffer initialized for printing:

Error creating thumbnail: File missing
Error creating thumbnail: File missing

Figure 1: The component graph of the MaincC and PrintfC configurations.

Conceptually, the operation of the TinyOS printf library is very simple. Developers supply strings to printf() commands in a distributed fashion throughout any of the components that make up a complete TinyOS application. These strings are buffered in a central location inside the PrintfP component and flushed out to a PC in the form of TinyOS SerialMessages upon calling a printfflush() command. No nesC interfaces are required, and both the printf() and printfflush() commands can be called in c-fashion by simply including the 'printf.h' header file.

By encapsulating the strings produced by calls to printf() inside standard TinyOS SerialMessages, applications that use the serial stack for other purposes can share the use of the serial port. Alternate implementations were considered in which printf would have had exclusive access to the serial port. In the end, we felt it was better to give developers the freedom to decide exactly when messages should be printed, as well as allow them to send multiple types of SerialMessages in a single application.

A circular buffer is used to store the strings supplied to printf() before flushing them. This means that while the buffer is being flushed, calls to printf will still continue to succeed. The default buffer size is 250 bytes, and flushing is automatically performed whenever this buffer becomes more than half full. Explicit flushing is also possible by making calls to printfflush(). Most applications can get away with the automatic flushing capabilities, but explicit flushing is still recommended in applications where the sections of code under examinatation are very timing sensitive (e.g. inside the CC2420 radio stack).

Using the TinyOS printf Library

To help guide the process of using the printf library, a tutorial application has been created. Navigate to the tinyos-2.x/apps/tutorials/Printf directory to follow along.

The Printf application demonstrates everything necessary to use the printf library. Go ahead and open the TestPrintfAppC configuration to see how simple the wiring is. Notice that unlike previous versions of printf, no explicit wiring to printf specific components are necessary.

 configuration TestPrintfAppC{
 } 
 implementation {
   components MainC, TestPrintfC;
   TestPrintfC.Boot -> MainC;
 }

All that is needed is to wire the application's Boot interface into MainC. MainC takes care of all the wiring necessary to bring up the Printf service and allow you to make calls to printf() and printfflush. As mentioned before, printf() and printfflush() commands can be called from any component as long as they have included the "printf.h" header file.


Before examining the TestPrintfC component, first install the application on a mote and see what kind of output it produces. Note that the instructions here are only valid for installation on a telosb mote on a linux based TinyOS distribution. For installation on other systems or for other mote platforms, please refer to lesson 1 for detailed instructions.

To install the application on the mote, run the following set of commands.

cd $TOSROOT\tutorials\Printf
make telosb install bsl,/dev/ttyUSBXXX

To see the output generated by the Printf tutorial application you need to start the PrintfClient by running the following command:

java net.tinyos.tools.PrintfClient -comm serial@/dev/ttyUSBXXX:telosb

After resetting the mote, the following output should be printed to your screen:

Hi I am writing to you from my TinyOS application!!
Here is a uint8: 123
Here is a uint16: 12345
Here is a uint32: 1234567890

Note that the 'tty' device (i.e. COM port) specified when starting the PrintfClient MUST be the one used for communicating with a mote over the serial line. On telos and mica motes this is the same port that the mote is programmed from. Other motes, such as eyesIFX, have one port dedicated to programming and another for communication. Just make sure you use the correct one.

If for some reason you do not receive the output shown above, please refer to lesson 4 to verify you have done everything necessary to allow serial communication between your pc and the mote. Remember that when using the MIB510 programming board that the switch on the very front of the board must be set to the OFF position in order to send messages from the mote to the pc.


Go ahead and open up TestPrintfC to see how this output is being generated.

Upon receiving the booted event, the Printf service has already been started up and initialized and we are ready to start printing messages. The following code prints the messages shown above and flushes them out over the serial line.

 event void Boot.booted() {
       printf("Hi I am writing to you from my TinyOS application!!\n");
       printf("Here is a uint8: %u\n", dummyVar1);
       printf("Here is a uint16: %u\n", dummyVar2);
       printf("Here is a uint32: %ld\n", dummyVar3);
       printfflush();
 }

Remember that the default printf buffer size is only 250 bytes, so don't try to print more than this at one time or you may end up cutting off the end of your string. This buffer size is configurable, however, by specifying the proper CFLAGS option in your Makefile.

 CFLAGS += -DPRINTF_BUFFER_SIZE=XXX

Conclusion

A few points are worthy of note before jumping in and writing your own applications that use the functionality provided by the printf library.

  1. In order to use the printf library, the tos/lib/printf/2_0_2 directory must be in your include path. The easiest way to include it is by adding the following line directly within the Makefile of your top level application:
  2. CFLAGS += -I$(TOSDIR)/lib/printf/2_0_2 
    
  3. Remember that changing the printf buffer size is done similarly:
  4. CFLAGS += -DPRINTF_BUFFER_SIZE=XXX
    
  5. You MUST be sure to #include "printf.h" header file in every component in which you would like to call the printf() command. Failure to do so will result in obscure error messages making it difficult to identify the problem.

Hopefully you now have everything you need to get going with the TinyOS printf library. All questions (or comments) about the use of this library should be directed to tinyos-help mailing list.

Enjoy!!



< Previous Lesson | Top | Next Lesson >