ICI Description

An ICI application is always running on the module on top of the Platform Image, to tailor the modules behaviour to the customers unique requirements. The ICI application is written in a high-level C-language, using a powerful API that’s available in the SDK. The API removes the need for the developer to understand the underlying architecture and resources in the module. In its simplest form, the ICI application is just configuring the radio network, the modules hardware interfaces and defining when to read and write to those interfaces. This can typically be done with less than 100 lines of code and within a few hours. Examples included in the SDK are normally a good starting point. The ICI application also have the capability of including complex data processing and advanced features, such as averaging and threshold detection using one or many sensors in combination or to create complex sensor interfaces. The flash space available for the ICI application is 32 kB.

Prerequisite is a basic knowledge of C programming, but no expertise. No expertise in wireless networking or device specific knowledge is required. The goal of this guide is to reduce the complexity and confusion of embedded wireless to something simple and easy for everybody.

Everything in ICI is event-driven. It’s up to the ICI user application to define the events and the handlers for these events.

Think of the ICI user application as a list of event handlers : if-this-then-that. If this event occurs, then do that in an event handler.

As an example, a simple wireless sensor ICI application could be defined by the following event behaviors.

  • If network state is offline then turn off the LED

  • If network state is online then turn on the LED

  • Every 20 seconds, read data from the sensors on I2C

  • After every sensor reading, send sensor data as CoAP message to a given IP address

Of course, a lot more is possible. Sensors and actuators can be accessed using different interfaces (I2C, ADC, GPIO, SPI, UART..). Periodic or one-shot timer events can be created. Custom actions can be triggered by network commands from the Border Router. Global variables can be used to track internal states. Data can be saved to non-volatile memory to preserve them across power resets. Algorithms can be implemented to process or filter data.

Underneath, ICI silently takes care of the security and reliability of the network, any over-the-air updates, and the powerful real-time operating system (RTOS) that schedules the events.

The user code is composed of RIIM_SETUP() and a list of event handlers. RIIM_SETUP() is the only function that must be implemented, and it’s called from the application framwork when the node starts up. From within RIIM_SETUP(), you can define events and register handlers for these events - for example when you receive a message, or when a timer expires.

You can also chain events based on other events by defining an event within another event handler. For example, you can start a timer when a GPIO edge triggers.

An example ICI application

We will show you an example of a wireless voltage sensor using the built-in ADC.

This sensor will have the following behaviors:

  • Finds and joins a network automatically

  • Every 10 seconds, read data from the ADC

  • Print out the measured value onto console

Take a look at the code implementation below. Only basic C knowledge is required. And keep in mind that the entire code is composed of RIIM_SETUP() and a list of event handlers.

ADC sensor example
#include "RIIM_UAPI.h"

static const uint32_t timerPeriod=10000;
static uint8_t sensorTimerHandle;
static uint32_t milliVolts;

static void readSensor()
{

    ADC.convertToMicroVolts(ADC0, &milliVolts);
    milliVolts /= 1000;

    // Print directly to console to show the latest values
    Util.printf("ADC sensor read: Voltage(mV): %i\n",milliVolts);

    return;
}

RIIM_SETUP()
{
    Util.printf("# Starting RIIM Sensor Board Node\n");

    // Initialize variables.
    milliVolts=0;

    // Set up ADC
    ADC.init(ADC0, ADC_FIXED_REFERENCE, ADC_SAMPLING_DURATION_10_6_US);

    // Create timer that sends the CoAP PUT message
    sensorTimerHandle=Timer.create(PERIODIC, timerPeriod, readSensor);
    Timer.start(sensorTimerHandle);

    // Start the node
    const uint8_t nwKey[16]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
    Network.setNWKey((uint8_t*)nwKey);
    Network.startLeaf();
    Network.setChannel(32);

    // Print out our setup
    Debug.printSetup();

    // Return UAPI_OK to indicate setup went well
    return UAPI_OK;
}

This showss how easy it’s to define and register a handler for an event. In a little more than 30 lines of actual code you have created a full CoAP and Mesh-enabled ADC sensor.

ICI limitations and differences to “regular” C programming

ICI can use almost all features of the standard C programming language as you are used to. The exceptions to this are:

  1. Timer resolutions

    Timer resolution is approximately 8 ms, even though you can specify timers down to 1 ms. This applies to all regular timers (using the Timer API) and timeouts such as when using the UART with reception timeout.

  2. Keep code execution time short

    Execution time of an ICI function (event handler) should be kept short. If excessive time is used in a function, timing of other tasks such as system tasks may be off. A function taking more than 5 seconds to complete, may result in a full module reset, as the system will think the program has frozen.

  3. Variables should generally be global or static

    // Instead of this:
    void Func1()
    {
        NetworkState state;
        state=Network.getNetworkState();
        .....
    }
    
    // Use this:
    void Func1()
    {
        static NetworkState state;
        state=Network.getNetworkState();
        .....
    }
    
  4. Variables without the const keyword can’t be directly initialized when defined

    void Func1()
    {
        // This is illegal!
        static int a=10;
    
        // This is legal
        static int a;
        a=10;
    
        // This is also legal
        const int a=10;
    }
    

Programming the Border Router

The Border Router is programmed just as any other node. However, to simply enable the routing, the following program is enough for basic connectivity between the nodes and the Border Router:

RIIM_SETUP()
{
    Util.printf("Starting RIIM Border Router\n");

    // This example does not use the Ethernet. We provide only NULL
    // as IPv6 and IPv4 configuration
    Network.startBorderRouter(NULL, NULL, NULL, NULL);

    return UAPI_OK;
}