4.3. SCAP LTP user manual

4.3.1. Introduction

This user manual gives a short overview on how to develop an application using SCAP/LTP. A prerequisite is "SCAP/LTP: features" which explain basic concepts of SCAP/LTP.

4.3.2. Logical module view

The hl2gener SDK contains several modules. Two of them are described here:

  • hl2-base: the base definitions. This is shared among other modules

  • hl2-scap-ltp: the SCAP/LTP specific code

Each module presents the same global architecture :

A module is connected to the platform through the HAL and exposes an API

4.3.3. Logical main repository view

The main repository contains everything required to build a stand-alone application. It is a makefile-based group of several entities (compiler, MCU, etc) which are instantiated by several objects (e.g. the compiler may be GCC arm-gcc-4.8.3 or avr-gcc-8.2.0). Each entity requires some definitions provided by the previous entity:

Project entities

The hl2-scap-ltp may be build as a library instead of a stand-alone application. In this case, it requires the "chip" definition which relies on "MCU", "Compiler" and "OS". The process is:

  • The scap-ltp dedicated makefile calls the main.mk file

  • Main.mk looks for the chip module.mk file

  • Main.mk looks for the chip required HALs module.mk file in the following directories

    • chip\/\<target chip>/hal-hl2 (i.e. the base module)

    • chip\/\<target chip>/hal-scap-ltp

  • The chip mk files defines which MCU is used. main.mk looks for MCU module.mk

  • Main.mk looks for the defined compiler and OS module.mk

  • Main.mk compiles the required target to build/output

An entity may define a default variable which may be overwritten by an entity used earlier. For example, the chip may define a default compiler but the application level makefile may overwrite this to another one.

4.3.4. Files

.
 board               // Contains board-specific applications
  pld             //
  pldv2           //
  ...             //
 build               //
  compiler        // All compilers and their different versions
  output          // The compilation output directory
  uploader        // The uploader tools
 chip                //
  atmega328p      //
  cmwx1zzabz      //
  ...             //
 doc                 //
 hl2-base            // hl2-base module, contains the shared interfaces
  inc             //
  module.mk       //
  src             //
 hl2-scap-ltp        // SCAP/LTP module
  lib             // Target specific precompiled libraries
  sl.h            // public device-side API
  slHal.h         // public server-side HAL definition
 Main.mk             // Main Makefile
 mcu                 // Contains the definitions of all the known MCUs
     atmega328p      //
     efm32g          //
     stm32l0x2czyx   //
     stm32l151cc     //
     stm32l476rg     //

The application does not necessarily belong to this tree. It may be stored elsewhere, it only needs to know where is the SDK root. This path is referenced by the HL2_EMBEDDED_SDK env variable by the existing projects. In this document, the SDK root is written '/'.

4.3.5. Git

The main Git repository contains submodules:

The leaves of this tree are submodules.

.
 hl2-at
 hl2-leaf
 board
  pld
  pldv2
 chip
  atmega328p
  cmwx1zzabz
  mm002
  td1208
 build
     compiler
         gcc
             arm-4.8.3
             arm-4.8.4
             arm-6.3.1
             avr-8.2.0

These examples are given assuming you already have the file tree and you are using a supported chip or board. Adding MCU/chip/board is not covered in this document but the existing examples are a good starting point.

The following examples are given in two forms:

  • Using the C public API

  • Using PLDuino, a SCAP/LTP AT client written in C++

4.3.6. Sending data

To send data, the application code has to construct a stream and send it through a route. Defining a route is discussed later.

The most used stream type is BIN : a list of 1 to 32 scalar variables.

Send a binary stream using the C API.

#include <sl.h>

/* The stream payload is an external buffer which life span must be greater than the sending function */

#define PAYLOAD_SIZE 20

uint8_t stream_payload[PAYLOAD_SIZE];

hl2_buffer_t stream_buffer =
{
    stream_payload,
    PAYLOAD_SIZE
};

void sendBinStream()
{
    /* Create an uplink object */
    hl2_uplink_t uplink;
    /* Initialize it and attach the external buffer */
    sStreamInitialize(&uplink.stream, &stream_buffer);
    /* Set the route to auto */
    uplink.route = HL2ROUTE_FLAG_AUTO;
    /* Define the content */
    uplink.stream.scap.object_type = HL2_SCAPOT_BIN; /* stream type: BIN */
    uplink.stream.scap.object.bin.id = 4; /* Stream ID */
    uplink.stream.scap.object.bin.count = 2; /* variable count */
    /* First variable: bool, true */
    uplink.stream.scap.object.bin.types[0] = HL2_SCAP_VAR_DESC_BOOL;
    uplink.stream.scap.object.bin.variables.values[0].b = true;
    /* Second variable: uint64, 0x0123456789ABCDEF */
    uplink.stream.scap.object.bin.types[1] = HL2_SCAP_VAR_DESC_UINT64;
    uplink.stream.scap.object.bin.variables.values[1].u64 = 0x0123456789ABCDEF;
    /* send data */
    slUplinkPush(&uplink);
}

Send a binary stream using the PLDuino client.

#include <PLDuino.h>

void sendBinStream()
{
    /*
     * construct a stream with id = 4 (implicite auto route),
     * add variables with the stream operator,
     * send it
     * */
    PLD.modem().send(LPWAN::Stream(4)
        << true
        << 0x0123456789ABCDEF
    );
}

It is also possible to send raw buffers.

Send a raw stream using the C API.

#include <sl.h>

#define PAYLOAD_SIZE 10

void sendRawStream()
{
    /* define a payload to send. The static keywords gives an infinite life span */
    static uint8_t raw_payload[PAYLOAD_SIZE] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    hl2_buffer_t stream_buffer =
    {
        raw_payload,
        PAYLOAD_SIZE
    };
    /* Create an uplink object */
    hl2_uplink_t uplink;
    /* Initialize it and attach the external buffer */
    sStreamInitialize(&uplink.stream, &stream_buffer);
    /* Set the route to "LORIOT" */
    uplink.route = slRoutesGetIndex("LORIOT");
    /* The buffer defines its capacity, specify the used size */
    uplink.stream.scap.object.raw.payload.size = PAYLOAD_SIZE;
    /* stream type: RAW */
    uplink.stream.scap.object_type = HL2_SCAPOT_RAW;
    /* send data */
    slUplinkPush(&uplink);
}

Send a raw stream using the PLDuino client.

#include <PLDuino.h>

void sendRawStream()
{
    /* define a payload to send */
    uint8_t raw_payload[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    /* Construct a stream, implicite id = 0, route = "LORIOT" */
    LPWAN::Stream stream;
    /* Add a raw payload */
    stream.setRawValue(data, 10);
    /* Send the stream */
    PLD.modem().send(stream);
}

In order to send a degraded stream (i.e. excluding SCAP/LTP features), replace 'sStreamInitialize' with 'slStreamInitializeDegraded'.

4.3.7. Creating a route

Create a route using the C API.

#include <sl.h>

void createRoute()
{
    hl2_route_lorawan_t loriot =
    {
        /* Profile EUI */
        0x70B3D582100000FD,
        /* Name */
        "LORIOT",
        /* Interface type */
        HL2_IFACE_LORAWAN,
        /* Region */
        HL2_REGION_EU868,
        /* Disable security */
        HL2_SCAPSF_SEC_DIS,
        /* Automatic TX power (set with the ADR) */
        HL2_LORAWAN_TX_POWER_AUTO,
        /* Automatic datarate (ADR) */
        HL2_LORAWAN_DATARATE_ADR,
        /* AppKey */
        {
            0xc3, 0x0b, 0x65, 0x8a, 0x9f, 0x29, 0x64, 0x4a,
            0xfb, 0x7b, 0x33, 0x9f, 0x07, 0x24, 0x96, 0x8f,
        },
        /* AppEUI */
        0x70B3D58210000100,
        /* Join timeout in seconds */
        20,
        /* Force rejoin after this number of uplinks to refresh keys */
        50000,
        /* Force rejoin after this number of failed uplinks */
        2,
        /* Custom MTU */
        50,
        /* LoRaWAN class A operation mode */
        HL2_LORAWAN_CLASS_A,
        /* Always request an ACK */
        HL2_STRATEGY_ALWAYS,
        /* DO not join this route at start up but when the first uplink is requested */
        HL2_LORAWAN_JOIN_HOOK_FIRST,
        /* On uplink failure, retry two times */
        2,
        {
            /* 1st retry: use a lower datarate and a higher TX power if possible */
            {HL2_LORAWAN_RELATIVE | 1, HL2_LORAWAN_RELATIVE | 1},
            /* 2nd retry: use datarate 0 and maximum TX power */
            {0, 0}
        }
    };
    slRouteAdd((hl2_route_t*)&loriot);
}

Create a route using the PLDuino client.

#include <PLDuino.h>

void createRoute()
{
    Route route = {
        "70B3D582100000FD",                        /* profile */
        "LORIOT",                                  /* name */
        Route::EU,                                 /* region */
        Route::lorawan,                            /* interface */
        false,                                     /* secured */
        { .lorawan = {                             /* LoRaWAN specific parameters */
                0x70B3D58210000100,                /* AppEUI */
                {},                                /* AppKey */
                Route::Interface::LoRaWAN::always, /* always request an ACK */
                {                                  /* retries with ACK parameters */
                    5,                             /* retries count */
                    {                              /* retries RF parameters: */
                        { 0, true, 0, true },      /* same DR, same TX power */
                        { 0, true, 0, true },      /* same DR, same TX power */
                        { 0, false, 14, false },   /* DR 0, TX power 14 dBm */
                        { 0, false, 14, false },   /* DR 0, TX power 14 dBm */
                        { 0, false, 14, false }    /* DR 0, TX power 14 dBm */
                    }
                },
                20,                                /* join timeout in s */
                10000,                             /* renew join after this number of uplinks */
                10,                                /* renew join after this number of FAILED uplinks */
                LORAWAN_TX_POWER_AUTO,             /* TX power */
                LORAWAN_DATARATE_ADR,              /* datarate */
                'A',                               /* LoRaWAN class */
                true,                              /* join on first uplink ? */
                235                                /* no MTU specified */
            }
        }
    };

    PLD.modem().addRoute(route);
}