Wireless Embedded Internetworking Short Course

David E. Culler

University of California at Berkeley

Department of Electrical Engineering and Computer Science

 

 

Lab 6 Reporting

 

 

 

 

Periodic Reporting and Alarms

 

In lab 5 we built an UDP/IP based embedded network application in which the infrastructure essentially polled the embedded device by sending requests to it.  In this lab we are going to develop a very different network architecture.  In many embedded network settings the embedded device samples periodically are reports its readings to a controller, log, analysis, or other infrastructure resource.  Alternatively, it may send alarm when some condition is detected.  Or it may be a combination of the two.  In lab 2 we implemented periodic sample, regular actuation, and alarm inputs.  Let’s extend those ideas into a network form. 

 

If you feel comfortable with the UDP request/response server that you built in Lab 5, feel free to run with that.  If instead you would like a little head start with this lab, we have built a partial solution in $TOS/rreport.  The client peer to this is udpreporter.c in either IPv4 or IPv6.  This version is very simple as it discards any message sent by the client, but uses its IP address for the following reports.  Notice that the classic internet tools, like telnet and nc (and your web browser) don’t quite work for this because they expect request/response.  Here there is a configuration request followed by a stream of reports. 

 

Our example also illustrates some important kernel interfaces, in particular the GlobalTime interface.  The localtime method is really uptime since the mote booted.  The globaltime is an embedded version of what NTP provides.  Because correlated time is so important in embedded system there have been much research into time synchronization.  This one provides synchronized time as an option that is piggybacked on the IPv6 router advertisements.  The value is unix time – the number of seconds elapsed since midnight Coordinated Universal Time (UTC) of January 1, 1970, not counting leap seconds. This only works if the router has NTP.

 

We hope that you will start to run with your own ideas in this lab, but here’s some thoughts to get you started.

 

  • You could separate the two infrastructure parts and have the configuration request specify the IP address and port of a dedicated listener.  Or, you could hardwire the listener address:port in the node.  This separation enables two opportunities.
    • Collective listener: a whole network of motes can send reports to the same udp listener.  It just collects them all on one socket.
    • Mobility. Nodes can sample and record when they are off and not part of any network.  When they hear a router advertisement they can just send their data off the listener.  IP routing takes care of the rest.
  • The best instrumentation standards provide a minimum and maximum reporting interval.  This is especially important when reports are sent as a result of some event or threshold, rather than just periodically.  The maximum intervals says that a report of the current value should be made even if no event is detected.  This means that if the event is lost the application state eventually converges.  For example, if the input is a door open/close sensor.  The event may occur on each edge, but if it is missed you will still determine eventually whether the door is open.  Also, this provides a heartbeat that indicates that the node is still healthy and well.  The minimum interval is to deal with the case where the sensor bounces and a very rapid sequence of transitions occurs.  It may not even be possible to count all the edges.  It also keeps the network for being overloaded when all the nodes detect some broad event.

 

Parameterized Interfaces

 

In addition to transforming your application into a rich reporting service, let’s slip a much more complete and robust driver infrastructure underneath of it.  This will give us an opportunity to introduce you to one of the powerful composition tools in TinyOS that is provided by NesC – Parameterized interfaces.

 

If you look a little bit carefully at rreport/ReportC.nc you will notice that we have replaced the User button component.  ReportP.button is provided by UserC.Button.  Let’s take a look in drivers/UserC.nc.  It isn’t a module that does a bunch of bit twiddling, it is a configuration that builds the button interface out of a very general purpose driver that provides access to each and every pin on the MSP430.  The UserInt button just happens to be the thing attached to Port 2, Pin 7.

 

configuration UserC {

  provides interface Button;

}

 

implementation {

 

  components UserP;

  Button = UserP.Button;

 

  components KernelC;

  UserP.Boot -> KernelC.Boot;

 

  components Msp430PortsC;

  UserP.Pin    -> Msp430PortsC.Port2[7];    /* UserInt */

  UserP.PinInt -> Msp430PortsC.Port2Interrupt[7];

}

 

The mapping from pins to buttons is provided by the helper module, UserP.nc.  It is reproduced here.

 

module UserP {

 

  provides interface Button;

 

  uses interface Boot;

  uses interface Msp430Port as Pin;

  uses interface Msp430PortInterrupt as PinInt;

}

 

implementation {

 

  event void Boot.booted() {

    call Pin.setDirection(0);     /* Input */

    call PinInt.edge(1);     /* Rising edge, button release */

    call PinInt.enable(1);   /* Enable interrupts */

  }

 

 

/* Button processing task */

  task void fire() {

    signal Button.pressed(); /* Signal event to upper layers */

  }

 

  async event void PinInt.fired() {

    post fire();

  }

}

 

This introduces two new interfaces, Msp430Port and Msp430PortInterrupt.  The first can be used with any GIO pin, whereas only ports 1 and 2 provide interrupts.  The setDirection command determines whether the pin is an input or output.  We leave the interrupt sensitive to the rising edge.  This corresponds to releasing the button.  Finally, we enable interrupts. 

 

Question: How would we modify this abstraction if we wanted it to signal both the press and release?

 

This simple example shows some of the power of component composition.  We introduced a powerful driver infrastructure – the Msp430PortsC component - and then wired in an adapter module – UserP – in a configuration that preserved the old button interface.

 

The Msp430PortsC component shows an additional level of power.  First, look at the declaration section of Msp430PortsC.nc.  It is reproduced here.

 

configuration Msp430PortsC {

 

  provides interface Msp430Port as Port1[ uint8_t pin ];

  provides interface Msp430Port as Port2[ uint8_t pin ];

  provides interface Msp430Port as Port3[ uint8_t pin ];

  provides interface Msp430Port as Port5[ uint8_t pin ];

 

  provides interface Msp430PortInterrupt as Port1Interrupt[ uint8_t pin ];

  provides interface Msp430PortInterrupt as Port2Interrupt[ uint8_t pin ];

}

 

The parameter to the interface in square brackets indicates that this is an array of interfaces and that the interface index is associated with the variable pin of type 8 bit unsigned integer.  The actual interfaces that are present in this array is determined at compile time by what gets wired to it.  

 

For each port, the configuration passes the entire array of interfaces to the Msp430PortsP implementation module.  This does all the low level bit twiddling and atomicity management.  In addition, the interrupt signals are wired to the kernel.  Looking back up at UserC, we see that user button is wired to the pin at index 7 of port 2.  This wiring instantiates that interface.

 

Looking down into Msp430PortsP.nc, we see a generalization of what we did for LEDs and Buttons.  In the declaration section it provides all the parameterized interfaces that were made visible in Msp430PortsC and it uses the parameterized HplSignal interfaces that are provided by the kernel.

 

module Msp430PortsP {

 

  provides interface Msp430Port as Port1[ uint8_t pin ];

  provides interface Msp430PortInterrupt as Port1Interrupt[ uint8_t pin ];

 

  provides interface Msp430Port as Port2[ uint8_t pin ];

  provides interface Msp430PortInterrupt as Port2Interrupt[ uint8_t pin ];

 

  provides interface Msp430Port as Port3[ uint8_t pin ];

  provides interface Msp430Port as Port4[ uint8_t pin ];

  provides interface Msp430Port as Port5[ uint8_t pin ];

  provides interface Msp430Port as Port6[ uint8_t pin ];

 

  uses interface HplSignal as HplSignalPort1;

  uses interface HplSignal as HplSignalPort2;

}

 

The implementation of the commands and events for each of the ports is parameterized by the pin.  For example, the following get command is similar in function to what we saw previously, but the bit it selects out of the P1IN register is determined by the interface index.

 

implementation {

 

  async command bool Port1.get[ uint8_t pin ]() {

    return (P1IN >> pin) & 1;

}

}

 

The interrupt handler is especially interesting.  It scans through all the bits in the interrupt flag register for the port (P1IFG) and signals an event to whatever handlers are wired to it.  In effect, we have added a layer of interrupt dispatch, so higher level components can take the view that pins have individual interrupts.  Note that this signal is still in interrupt context, so those higher level handlers must do their work quickly, post a task and return.

 

The default handler for the fired event catches any interrupts for pins that do not have a higher level component wired to them.

 

async event void HplSignalPort1.fired() {

    int pin;

    for ( pin = 0; pin<8; pin++ ) {

      if ( P1IFG & (1 << pin) ) {

    signal Port1Interrupt.fired[ pin ]();

    P1IFG &= ~(1 << pin);

      }

    }

  }

 

 default async event void Port1Interrupt.fired[ uint8_t pin ]() {}

 

You will certainly want to switch your application over to this more capable infrastructure.  You may even want to skip the UserC interface and go directly to Msp430Port and Msp430PortInterrupt.