Tos.py

From TinyOS Wiki
Jump to: navigation, search

Note: This tutorial describes the tos.py from the latest CVS.

Introduction

The official documentation of the serial communication in TinyOS 2.x is TEP113. Briefly, the higher level interface to the serial stack is provided by an AMSend interface provided by SerialAMSenderC and a Receive interface provided by SerialAMReceiverC.

Below the ActiveMessage interface there is a Dispatch Layer (SerialDispatcherC), a Protocol Layer (SerialP) and a Encoder/Framer Layer (HdlcTranslateC).

The Dispatch Layer adds one byte that indicates which type of packet is contained in the payload. The types defined in Serial.h are:

  • ActiveMessage (0)
  • CC1000 (1)
  • CC2420 (2)
  • Unknown (255)

The Protocol Layer adds one protocol byte and one sequence number byte in the header and a 2-bytes CRC in the footer. The protocol byte in the header is used to indicate the following types of the packets: acknowledge packet (67), data packet which needs to be acknowledged (68) and data packet which doesn't need to be acknowledged (69). The sequence byte is missing from the data packets which doesn't need to be acknowledged.

The Encoder/Framer Layer is implementing the HDLC framing which uses 0x7e as a frame delimiter and 0x7d as escape character. The encoding consists of replacing all occurrences of 0x7e with 0x7d 0x2e and all occurrences of 0x7d with 0x7d 0x2d. The frame delimiter is added both in header and footer.

All the platform independent code is in located in $TOSDIR/lib/serial.

Types of packets

All multi-byte fields are big-endian. The only exception is the CRC which is little-endian (RFC1662 "PPP in HDLC-like Framing" defines it this way).

Acknowledge Frame

  ________
 | | |  | |
 | | |  | |
 |_|_|__|_|
  F P CR F
 
 F       = Framing byte, denoting start of packet (0x7e): HdlcTranslateC
 P       = Protocol byte (67): SerialP
 S       = Sequence number byte: SerialP
 CR      = Two-byte CRC over S to end of Payload: SerialP
 F       = Framing byte denoting end of packet (0x7e): HdlcTranslateC

Data Frame

  ____________________________________________
 | | | | |                               |  | |
 | | | | |                               |  | |
 |_|_|_|_|_______________________________|__|_|
  F P S D         Payload                 CR F
 
 F       = Framing byte, denoting start of packet (0x7e): HdlcTranslateC
 P       = Protocol byte (68): SerialP
 S       = Sequence number byte: SerialP
 D       = Packet format dispatch byte: SerialDispatcherC
 Payload = Data payload (stored in SerialDispatcherC): SerialDispatcherC
 CR      = Two-byte CRC over S to end of Payload: SerialP
 F       = Framing byte denoting end of packet (0x7e): HdlcTranslateC

NoAck Data Frame

  ____________________________________________
 | | | | |                               |  | |
 | | | | |                               |  | |
 |_|_|_|_|_______________________________|__|_|
  F P S D         Payload                 CR F
 
 F       = Framing byte, denoting start of packet (0x7e): HdlcTranslateC
 P       = Protocol byte (69): SerialP
 S       = Sequence number byte: SerialP
 D       = Packet format dispatch byte: SerialDispatcherC
 Payload = Data payload (stored in SerialDispatcherC): SerialDispatcherC
 CR      = Two-byte CRC over S to end of Payload: SerialP
 F       = Framing byte denoting end of packet (0x7e): HdlcTranslateC

tos.py

The tos.py Python library provides two things:

  1. a simple way to send and receive Active Messages to motes connected to PCs or MIB600s
  2. a versatile way to encode/decode arbitrary packets.

For byte-level serial communication the PySerial Python library is used.

The classes provided by tos.py are:

  • Serial, SerialMIB600
  • HDLC
  • SimpleAM
  • AM
  • Packet
  • RawPacket
  • AckFrame, DataFrame, NoAckDataFrame
  • ActiveMessage

The class in italics is internal and the ones in bold are the ones usually used in the user programs.

Note: The tos.py file is located in $TOSROOT/support/sdk/python/.

Example: tos-dump.py

A very simple example that is already in the tree is the tos-dump.py program. The code for it is the following:

#!/usr/bin/env python

import sys
import tos

if '-h' in sys.argv:
    print "Usage:", sys.argv[0], "serial@/dev/ttyUSB0:57600"
    print "      ", sys.argv[0], "network@host:port"
    sys.exit()

am = tos.AM()

while True:
    p = am.read()
    if p:
        print p

The program prints all the packets that comes from the mote. The AM looks for the way to talk to the mote in 3 places:

  • first parameter in the command line
  • the -comm options in the command line
  • the MOTECOM environment variable.

One thing that AM knows about is the AMs (AM type 100) send by the PrintfC component. Their contents is decoded and all the lines are printed with a 'PRINTF:' added in the front.

Note: The tos-dump.py file is located in $TOSROOT/tools/tinyos/misc/.

Example 2: oscilloscope.py

The apps directory contains two example applications called Oscilloscope and MultihopOscilloscope. In both there is now a script called oscilloscope.py that will pretty-print the values received by a mote running BaseStation applications.

The code for oscilloscope.py is the following:

#!/usr/bin/env python

import sys
import tos

AM_OSCILLOSCOPE = 0x93

class OscilloscopeMsg(tos.Packet):
    def __init__(self, packet = None):
        tos.Packet.__init__(self,
                            [('version',  'int', 2),
                             ('interval', 'int', 2),
                             ('id',       'int', 2),
                             ('count',    'int', 2),
                             ('readings', 'blob', None)],
                            packet)

if '-h' in sys.argv:
    print "Usage:", sys.argv[0], "serial@/dev/ttyUSB0:57600"
    sys.exit()

am = tos.AM()

while True:
    p = am.read()
    if p and p.type == AM_OSCILLOSCOPE:
        msg = OscilloscopeMsg(p.data)
        print msg.id, msg.count, [i<<8 | j for (i,j) in zip(msg.readings[::2], msg.readings[1::2])]

The OscilloscopeMsg shows how to describe a packet using the parent class Packet. Each of the tuples from the list indicates the name of the field, the type and the size. For the blob type indicates a list of bytes and the size can be None in which case all the rest of the packet will be allocated to the field.

AM

The constructor for the AM class has only one optional parameter which indicates the object to use to talk to a mote. This can be either a Serial or a SerialMIB600 object. If the parameter is missing the AM will try to figure out the how to talk to the mote using the first parameter from the command line, the '-comm' command line options and the 'MOTECOM' environment variable.

The two methods offered by an AM object are:

  • read(timeout=None): it returns None if no packet was received for the timeout period or an ActiveMessage otherwise. The Active Messages send by TinyOS 2.x are always NoAckDataFrame.
  • write(packet, amId, timeout=None, blocking=True): it sends a packet (derived from Packet). If the packet parameter is a list then it's assume to be exactly the payload. Otherwise the payload is assume to be a Packet and the real payload is obtain by calling its payload() method. TinyOS 2.x only accepts packets that required acknowledge. If the ack is not received during the timeout period the packet is resent. If the blocking is set to False the method will return without waiting for the ack. This can be used to implement a best-effort type of send.

mig is outdated. It is a lot easier to hand code the various python classes for sending message using the above examples. If you use mig, it might be extremely difficult for you to use am.write

SimpleAM

This is a support class for AM. Beside a read and write method SimpleAM also offers a way (setOobHook) to set a function that will be called for the packets that are received while trying to send an ack.

Serial and SerialMIB600

The constructor for the Serial class has the following parameters:

  • port: /dev/ttyUSB0, COM2, 2
  • baudrate: 115200, 57600, ...
  • flush=False: try to flush all the data first. The operation takes 1 second.
  • debug=False
  • timeout=None: set the timeout for reads to an arbitrary value in seconds.

The constructor for the Serial class has the following parameters:

  • host
  • port=10002
  • debug=False

Both classes offers 4 methods: getByte, putBytes, setTimeout and getTimeout. These are used by a HDLC object.

HDLC

This class is using a Serial of a SerialMIB600 to offer a packet-based read and write for the SimpleAM class.