Skip to content

How the SWAP stack works

Daniel Berenguer edited this page Jul 28, 2015 · 4 revisions

Introduction

The SWAP stack is the firmware behind the scenes. It primarily consists of a public set of functions, variables and, the most important, all the automatic procedures that actually deal with the SWAP protocol.

Functionality

The stack is responsible of doing the following tasks:

  • Listening to the wireless SWAP network
  • Parsing incoming SWAP packets
  • Transmission of SWAP packets
  • Automatic generation of responses to incoming SWAP commands and queries
  • Management of registers, including updates and external control
  • Control of the internal state machine
  • Power management
  • Storage of configuration registers in EEPROM

All the above features provide a high degree of isolation between network communications, endpoint management and user code. In fact, the main goal of using this stack is letting the user concentrate on developing the application whilst the stack does the rest.

How it works

Stack and application run on a single-thread. Through the use of interruptions, the stack receives and parses wireless packets without interfering the normal operation of the user application. The RF hardware does an address check on each packet received. Thus, only packets addressed to our device are passed to the stack. Whenever a SWAP packet is received, the following mechanisms come into play:

  1. SWAP query addressed to our device The panStamp stack looks up the table of registers and responds with a status packet.
  2. SWAP command addressed to our device The panStamp stack looks up the table of registers, runs register's setter function (if exists) and responds with a status packet
  3. SWAP status packet broadcasted The panStamp stack calls a user callback function so that the status packet can be dispatched from the Arduino sketch.

With the above automatic procedures, a good degree of abstraction between stack and user application is achieved. At the same time, user application is released from having to deal with SWAP-related details. Which is the magics behind all this?

In order to make this engine work, developers have to build a table of registers for each device (each sketch). The first part of the table is common to all devices, formed by some standard registers as channel number, address or network id. The rest of registers are proprietary to each application.

Table of registers

regTable is the global table of registers. Each member of this table (REGISTER) has to be created using the its constructor.

For example, let's imagine that we want to create a custom register intended to store humidity and temperature values. In our example, temperature and humidity take one byte each one so we need a two-byte register:

// Allocating the two-byte register field
static byte dtTempHum[2];
// Definition of the register itself
REGISTER regTempHum(dtTempHum, sizeof(dtTempHum), &updtTempHum, NULL);

On the above example dtTempHum is the array that really stores the value of the regTempHum register. updtTempHum is the getter function, a callback function defined by the user and called whenever regTempHum.getValue() is called from the Arduino sketch. Thus, if you need to run ADC conversions and do other calculations over the register value, this callback function is the place to do it.

Following with the above example, let's suppose that we need to update the "temperature & humidity" register every 8 seconds and then go to sleep. We would do the following in the main loop:

/**
 * loop
 *
 * Arduino main loop
 */
void loop()
{
  // Update _updtTempHum_ register and send SWAP status packet
  swap.getRegister(REGI_HUMIDTEMP)->getData();
  
  // Sleep for 8 seconds
  panstamp.sleepSec(8);
}

Back to the above register definition, there is no pointer to a setter function (null). A setter function is another callback function to be defined by the user. However, unlike the getter function, a setter function is called from register.setValue(val). In other words, this function automatically runs whenever a SWAP command is addressed to the register. Since regTempHum is not controllable (it's just a couple of ADC inputs), there is no setter function associated to the register. As result, whenever a SWAP command is sent to our register the panStamp will respond with a SWAP status packet containing the value unaltered. That's all.

OK, once our custom register defined, let's define regTable, the complete array of registers:

/**
 * Initialize table of registers (regTable)
 */
DECLARE_REGISTERS_START()
  &regVoltSupply,
  &regTempHum
DECLARE_REGISTERS_END()

regTable is in fact an array of pointers to registers. This let us manipulate very diverse information and variable data-lengths without having to allocate any unused memory space.

The above macro automatically adds the main standard registers, common to all SWAP devices. The index of each register within regTable matches its ID so whenever a SWAP command or query is addressed to our device, the id is used to find the register in regTable and run the necessary operations with the help of the pre-defined callback functions. This would seem a bit complicated but it's fast, saves a lot of flash and prevents the user from having to deal with the SWAP protocol.

Other examples

Reviewing some real code is maybe the best way to understand these concepts. Any regtable.ino file from any of the available sample sketches will show you how to define custom registers and create your own getter/setter callback functions.

For example, from the "ntc" sample code: regtable.ino

More details

This page shows some additional details about how the panStamp stack works.