Thingsquare firmware SDK header file

This is the main header file for the Thingsquare firmware SDK. It
contains headers for the functions that a user application can call.

User applications may also use other APIs provided by the underlying
Contiki OS as well as the hardware libraries for the specific
hardware platform used.

Copyright 2012-2017 Thingsquare AB

#ifndef THSQ_H
#define THSQ_H

Include files

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "contiki.h"
#include "contiki-lib.h"
#include "contiki-net.h"
#include "net/rpl/rpl.h"

Thingsquare event callbacks

A user application may provide one or more callback functions that
are called by the Thingsquare system when events occur. Examples of
events is when a new variable is set, a new command is received, or
when GPIO input changes.

Callback functions are added with the thsq_add_callback() function.

Thingsquare event definitions

Events of the enum thsq_reason type are passed along with the
callbacks.

enum thsq_reason {
  THSQ_ERROR,
  THSQ_PONG_RECEIVED,
  THSQ_KEYVAL,
  THSQ_AUTH,
  THSQ_SERVER_CONNECTED,
  THSQ_COMMAND,
  THSQ_ZONEMSG,
  THSQ_PERIOD,
  THSQ_STATS,
  THSQ_GPIO_INPUT,
  THSQ_ACKED
};

Type definition of the callback function

typedef void (*thsq_callback_fn)(enum thsq_reason reason, const char *str, int len);

Internal structure of the list of thsq callbacks

This data structure is to be configured as an opaque structure.

struct thsq_callback {
  struct thsq_callback *next;
  thsq_callback_fn callback;
};

Add a callback function

This function sets up a callback function to be called whenvever something happens.
The struct thsq_callback must be statically allocated by the caller.

void thsq_add_callback(struct thsq_callback *callback, thsq_callback_fn function);

Set platform name

The thsq_set_platform() function is used to inform the frontend what
platform the current device is running.

Typically, each customer's device type has a unique name that is used to
identify specific device behavior and firmware updates for the device.

The platform name is a non-NULL pointer to a printable-ASCII-string
that is NULL-terminated, and is at max (THSQ_MAX_VALLEN) bytes long including
the terminator.

void thsq_set_platform(const char *platform);

Serial line module

The serialline module captures input from the UART and posts them as a
s variable serial and writes serial output from the sout command
to the UART.

void thsq_serialline_init(void);

The lighting control module

The lighting control module is a framework that provides zoned
lighting, lighting schedules, and prioritized triggers for light
switches, PIR sensors, and local controls.

The lighting module is initialized with thsq_lighting_init() and
thsq_lighting_set_lamp() (for lamps), and uses a complex set of local
and remote control mechanism to achieve
zoned lighting triggers across zones as well as remote control and
time-synchronized schedules.

The user hooks into the lighting control module with a callback
function that is registered with the thsq_lighting_set_callback()
function. The lighting callback receives a set of red, green, and
blue dimming values from 0 to 100. The callback should set the
output device's dimming levels accordingly.

Type definition of the lighting module callback function

typedef void (*thsq_lighting_callback_fn)(const uint8_t *rgb, int len, const char *reason);

void thsq_lighting_init(void);
void thsq_lighting_set_lamp(void);
struct thsq_lighting_callback {
  struct thsq_lighting_callback *next;
  thsq_lighting_callback_fn callback;
};
void thsq_lighting_add_callback(struct thsq_lighting_callback *c, thsq_lighting_callback_fn f);
void thsq_schedule_set_default(uint8_t *sched, int len);
void thsq_schedule_build_schedule_action(uint8_t *buf, int mon, int tue,
                                         int wed, int thu, int fri, int sat,
                                         int sun, uint8_t hours,
                                         uint8_t minutes, uint8_t red,
                                         uint8_t green, uint8_t blue);
void thsq_lighting_post_lights_variable(int active);

void thsq_lighting_lan_control_init(void);

Network time synchronization

If the device is time synchronized as indicated by thsq_has_time() == 1,
then thsq_get_time() returns the current Unix time (UTC) and
thsq_get_timezone_offset() the current timezone's seconds offset from UTC.
thsq_get_time_fine() returns the current Unix time in clock_time_t precision.

int thsq_has_time(void);
uint32_t thsq_get_time(void);
int32_t thsq_get_timezone_offset(void);
void thsq_set_time(uint32_t utc, int32_t tz_offset);
uint64_t thsq_get_time_high_precision(void);

The general purpose I/O module

The general purpose I/O module provides a simple way to sample one
digital input pin and/or one analog input pin and to control one
digital output pin and/or one digital PWM output pin.

The general purpose I/O module uses a pin configuration setting that
can either be supplied via the d variable pinconf, which can be
set from the backend server, or from locally calling the function
thsq_gpio_set_default().

void thsq_gpio_init(void);
void thsq_gpio_set_default(const char *def);

Bootloader configuration

The booloader can beconfigured with
BOOTLOADERPARAMS_OVERRIDE_XMEM_CONF(), which sets
up the flash chip configuration for the bootloader.

On platforms using cc13xx or cc26xx, the call has the following syntax:
BOOTLOADERPARAMS_OVERRIDE_XMEM_CONF(MISO, MOSI, CLK, CS);
where each pin is named eg IOID_2 for the DIO_2 pin.
Don't forget to include #include "ti-lib.h".

struct thsq_bootloader_params
{
  uint8_t bootloaderparams_active; /* 0: false, 1: use config */

  /* xmem configuration */
  uint8_t xmem_conf; /* 0: use defaults, 1: use below, 2: disable */
  uint8_t xmem_miso; /* xmem SPI MISO */
  uint8_t xmem_mosi; /* xmem SPI MOSI */
  uint8_t xmem_clk; /* xmem SPI CLK */
  uint8_t xmem_cs; /* xmem SPI CS */

  uint8_t secure_boot_aes[16]; /* secure boot AES key */

  uint8_t reserved[9];
};

extern struct thsq_bootloader_params bootloaderparams;
#define BOOTLOADERPARAMS_OVERRIDE(active, conf, miso, mosi, clk, cs)    \
    struct thsq_bootloader_params bootloaderparams \
    __attribute__ ((section(".bootloaderparams")))   \
    __attribute__ ((used))                    \
    = {active, conf, miso, mosi, clk, cs, \
       { 0xff, 0xff, 0xff, 0xff, \
         0xff, 0xff, 0xff, 0xff, \
         0xff, 0xff, 0xff, 0xff, \
         0xff, 0xff, 0xff, 0xff }, \
       { 0xff, 0xff, 0xff, 0xff, \
         0xff, 0xff, 0xff, 0xff, \
         0xff }}

#define BOOTLOADERPARAMS_OVERRIDE_XMEM_CONF(miso, mosi, clk, cs) \
  BOOTLOADERPARAMS_OVERRIDE(1, 1, miso, mosi, clk, cs)

Server interaction functions

A device interacts with the backend server through a set of variables
and commands. Variables come in two forms: d variables and s
variables. d variables are shared between the device and the backend
and are synchronized when a device boots up. s variables are only
maintained on the server, but can be set from the device. Commands are
sent from the server to the device.

A device may set either d or s variables. After setting variables,
the new variables must be pushed to the server by calling the
thsq_push() function.

When a device received a new d variable, or a new value for an
existing variable, all application callbacks, that have been
previously registered with thsq_add_callback(), are called.

The value of a d variable may be read by calling the thsq_get() or
thsq_get_to_buf() functions.

A d variable is set with the thsq_dset() or thsq_dest_str()
functions. An s variable is set with the thsq_sset() or
thsq_sset_str() functions.

A variable must be at most THSQ_MAX_VALLEN bytes long.

void thsq_push(void);
int thsq_dset(const char *key, int val);
int thsq_dset_str(const char *key, const char *str);
int thsq_sset(const char *key, int val);
int thsq_sset_str(const char *key, const char *str);
int thsq_sset_printf(const char *key, const char *format, ...);
int thsq_get(const char *key);
int thsq_get_to_buf(const char *key, uint8_t *buf, int buflen);

The maximum size of a variable value

#define THSQ_MAX_VALLEN 128

Set device default mode

A device may be in either of three modes: feather, connected, and
deadleaf. The default mode is feather. In feather mode, the
device is responsive to variables and commands from the backend. In
connected mode, the device maintains a TLS connection to the
backend. This is primarily used to send firmware updates to individual
devices. In deadleaf mode, the device is off most of the time and
wakes up only now and then to receive new data from the backend. In
deadleaf mode, a device does not route packets through the mesh.

The mode of a device may be set from the backend by setting the d
variable called mode. If no mode variable is set, the device
defaults to the mode that has been set by the
thsq_set_default_mode() function

void thsq_set_default_mode(const char *mode);

Disable the requirement for a valid digital signature for over-the-air firmware updates

By default, all binaries loaded over the air have a digital
signature that ensures that they were produced by the same entity
the produced the product they operate in. While this is essential
for deployed products, it may be problematic during product
development, before a solid key management policy has been
established.

This function allows the user application to override the
requirement for a valid digital signature. After calling this
function, the device will accept invalid signatures when loading
binaries over the air.

void thsq_disable_digital_signature(void);

Undocumented functions

These functions should not be used in production code as they may change.

Queue important variables module

The queue important variable module enables an application to queue up
timestamped important variables that are one-by-one sent to the server.
A queue is defined using the THSQ_QUEUE() macro, and must be initialized
using thsq_queue_init() before used. New data is added to the queue
with the thsq_queue_add() function.

#include "thsq.h"
#include "lib/list.h"
#include "lib/memb.h"
#include "sys/cc.h"

#define THSQ_QUEUE(name, variable, structure, elements) \
  struct CC_CONCAT(name,_meta) { \
    struct CC_CONCAT(name,_meta) *next; \
    clock_time_t timestamp; \
    structure data; \
  }; \
  MEMB(CC_CONCAT(name,_memb), struct CC_CONCAT(name,_meta), elements); \
  LIST(CC_CONCAT(name,_list)); \
  static struct thsq_queue name = { \
    NULL, \
    variable, \
    sizeof(structure), \
    0, \
    NULL, \
    &CC_CONCAT(name,_memb), \
    (list_t *)&CC_CONCAT(name,_list) \
  }

struct thsq_queue;
typedef int (* thsq_queue_send_callback_t)(struct thsq_queue *q, uint32_t timestamp, void *data); /* Returns 1 if sent */
struct thsq_queue {
  struct thsq_queue *next;
  char *variable;
  int size;
  uint16_t internal;
  thsq_queue_send_callback_t send_callback;
  struct memb *memb;
  list_t *list;
};

void thsq_queue_init(struct thsq_queue *q);
int thsq_queue_free(struct thsq_queue *q); /* Number of available queue elements */
int thsq_queue_used(struct thsq_queue *q); /* Number of used queue elements */
int thsq_queue_add(struct thsq_queue *q, void *element); /* Add new element to queue, returns 0 at success */
void thsq_queue_pause(struct thsq_queue *q, int paused); /* Pause sending data from queue */

void thsq_queue_send_aggressively(struct thsq_queue *q, int enabled); /* Send data towards server more aggressively */
void thsq_queue_send_reject_when_full(struct thsq_queue *q, int enabled); /* Reject elements when queue is full, instead of dropping oldest elements */
void thsq_queue_set_string_datatype(struct thsq_queue *q, int enabled); /* Parse element data as strings, cap at NULL terminator */
void thsq_queue_set_send_callback(struct thsq_queue *q, thsq_queue_send_callback_t cb);

The GPIO input module

enum thsq_gpi_pull_type {
  THSQ_GPI_PULL_UP,
  THSQ_GPI_PULL_DOWN,
  THSQ_GPI_PULL_NONE,
};
struct thsq_gpi_conf;
typedef void (* thsq_gpi_callback_t)(struct thsq_gpi_conf *c, int val);
struct thsq_gpi_conf {
  struct thsq_gpi_conf *next;
  uint32_t pin;
  volatile uint8_t triggered, triggered_val;
  uint8_t last_val;
  thsq_gpi_callback_t callback_interrupt;
  thsq_gpi_callback_t callback_user;
  clock_time_t debouncetimeout;
  struct etimer debouncetimer;
};
void thsq_gpi_cc26xx_cc13xx_conf(struct thsq_gpi_conf *c, uint32_t pin,
                                 enum thsq_gpi_pull_type pulltype,
                                 thsq_gpi_callback_t cb_interrupt,
                                 thsq_gpi_callback_t cb_user,
                                 clock_time_t debouncetimeout);
void thsq_gpi_unconf(struct thsq_gpi_conf *c);
int thsq_gpi_value(struct thsq_gpi_conf *c);

The GPIO output module

struct thsq_gpo_conf;
struct thsq_gpo_conf {
  uint32_t pin;
};
void thsq_gpo_cc26xx_cc13xx_conf(struct thsq_gpo_conf *c, uint32_t pin);
void thsq_gpo_unconf(struct thsq_gpo_conf *c);
void thsq_gpo_set_value(struct thsq_gpo_conf *c, int val);

The ADC module

struct thsq_adc_conf {
  uint32_t pin;
  uint8_t sampletime;
};
void thsq_adc_cc26xx_cc13xx_conf(struct thsq_adc_conf *c, uint32_t pin);
void thsq_adc_unconf(struct thsq_adc_conf *c);
uint16_t thsq_adc_value(struct thsq_adc_conf *c);

The I2C module

struct thsq_i2c_conf {
  uint8_t slave_address;
  uint8_t interface;
  uint32_t pin_sda;
  uint32_t pin_scl;
  uint32_t speed;
};
void thsq_i2c_cc26xx_cc13xx_conf(struct thsq_i2c_conf *c,
                                 uint8_t slave_address,
                                 uint32_t sda, uint32_t scl,
                                 uint32_t speed);
void thsq_i2c_unconf(struct thsq_i2c_conf *c);
int thsq_i2c_write_read(struct thsq_i2c_conf *c, uint8_t *out, int outlen,
                        uint8_t *in, int inlen);

The PWM module

The PWM module provides a simple way to configure pulse-width modulation (PWM) pins.

struct thsq_pwm_conf {
  uint8_t port;
  int pin;
  int dutycycle;
  uint8_t timer;
  uint32_t gpt_base;
  unsigned long freq;
  int configured;
  int running;
};

void thsq_pwm_cc2538_conf(struct thsq_pwm_conf *c, uint8_t port, uint8_t pin,
                          uint8_t timer, unsigned long freq, int dutycycle);
void thsq_pwm_cc26xx_cc13xx_conf(struct thsq_pwm_conf *c, int pin,
                                 unsigned long freq, uint32_t gpt_base,
                                 int dutycycle);
void thsq_pwm_set_dutycycle(struct thsq_pwm_conf *c, int dutycycle);
void thsq_pwm_set_freq(struct thsq_pwm_conf *c, int freq);

int thsq_dset_ptr(const char *key, const uint8_t *val, uint16_t vallen);
int thsq_sset_str_important(const char *key, const char *str);
int thsq_sset_printf_important(const char *key, const char *format, ...);
int thsq_sset_printf_important_timestamp(uint32_t timestamp, const char *key, const char *format, ...);
int thsq_sset_ulong(const char *key, uint32_t val);
int32_t thsq_get_long(const char *key);
int thsq_query(const char *key);
int thsq_exists(const char *key);

void thsq_set_default_rpl_mode(enum rpl_mode mode);
void thsq_format(void);
void thsq_bonding_release(void);
int thsq_bonding_is_bonded(void);
const char *thsq_platform(void);
void thsq_soft_release_bond(void);

void thsq_set_bond_key(const uint8_t *key);
void thsq_set_fingerprint(const char *host, const uint8_t *fingerprint);

/*
  flags field:

  +-------------------------------+
  | R | R | R | A | S | S | S | S |
  +-------------------------------+

  R: reserved
  A: auth
  S: state

  If A is set, the first 16 bytes of the data field contains the auth code.

  If state is 0, the first 4 bytes of the data field contains a random
  ID is to be used when inviting the device to the network. The
  remaining 22 bytes contains a the name of the platform.
*/

#define THSQ_BEACON_FLAG_AUTH                 0x10
#define THSQ_BEACON_FLAG_DATA                 0x20

struct thsq_beacon {
  uint8_t flags;
  uint8_t data[17];
};

/* Create a beacon message from the current thsq state in the supplied struct */
int thsq_beacon_create(struct thsq_beacon *b);
int thsq_beacon_create_databeacon(struct thsq_beacon *b,
                                  uint8_t *data, int datalen);

void thsq_beacon_send_advbeacon(uint8_t *advdata, int advlen);
void thsq_beacon_send_beacon(uint8_t *beacondata, int beaconlen);

#define THSQ_BEACON_MAX_PAYLOAD 31

/* 
 * Check the current state of the device. May be used for UI purposes, eg show
 * an error, or unbonded device. The current state can be retrieved
 * with `thsq_get_current_state()`. The following list is the set of states.
 */
#define THSQ_STATE_WAITING_FOR_BOND    0
#define THSQ_STATE_WAITING_FOR_MUCHA   1
#define THSQ_STATE_WAITING_FOR_RPL     2
#define THSQ_STATE_WAITING_FOR_MASTER  3
#define THSQ_STATE_WAITING_FOR_PONG    4
#define THSQ_STATE_WAITING_FOR_DNS     5
#define THSQ_STATE_WAITING_FOR_TLS     6
#define THSQ_STATE_WAITING_FOR_AUTH    7
#define THSQ_STATE_WAITING_FOR_NOTHING 8
#define THSQ_STATE_WAITING_FOR_CABLE   9
#define THSQ_STATE_WAITING_FOR_NETWORK 10

int thsq_get_current_state(void);

/*
 * Apply lighting settings from before last reboot, if such can successfully be
 * retrieved. This depends on for how long the device was powered off or in reset.
 */
void thsq_lighting_apply_last(void);

void thsq_lighting_set_lights(int prio, int r, int g, int b, uint32_t dur);

void thsq_lighting_set_default_zone_control(int prio, uint32_t duration);
void thsq_lighting_set_default_switch(int prio, uint32_t duration);
void thsq_lighting_set_default_pir(int prio, uint32_t duration, int red, int green, int blue);
void thsq_lighting_set_default_schedule(int prio);

const char *thsq_zones_get_zone(void);

/* lighting event types */
enum thsq_lighting_type {
  THSQ_LIGHTING_PIR,
  THSQ_LIGHTING_SWITCH,
  THSQ_LIGHTING_LC,
  THSQ_LIGHTING_IDFY,
};

int thsq_lighting_send(enum thsq_lighting_type t,
                       const uint8_t *zones, int zoneslen,
                       const uint8_t *data, int datalen, int via_parent);

void thsq_gpio_push_input_events(int enabled); /* s.gpi */
void thsq_gpio_push_adc_periodically(int enabled); /* s.adc */
rtimer_clock_t thsq_gpio_input_duration_high(void);
rtimer_clock_t thsq_gpio_input_timestamp(void);
void thsq_gpio_input_duration_reset(void);
void thsq_gpio_input_invert(int enabled);

/* Make the GPIO module listen to events from the lighting module. */
void thsq_gpio_attach_lighting(void);
void thsq_gpio_trigger_pir_on_gpio(int trigger);
void thsq_gpio_trigger_switch_on_gpio(int trigger);

struct thsq_pwm_conf *thsq_gpio_get_pwm_red(void);
struct thsq_pwm_conf *thsq_gpio_get_pwm_green(void);
struct thsq_pwm_conf *thsq_gpio_get_pwm_blue(void);

int thsq_gpio_get_adc(void);
int thsq_gpio_get_gpio(void);
int thsq_gpio_set_gpio(int high);

void thsq_sniffer_init(void);

long thsq_get_long_arg(const char *key, int argnum, long na);

enum thsq_power_level {
  THSQ_POWER_LEVEL_LOW,
  THSQ_POWER_LEVEL_HIGH
};
enum thsq_power_level thsq_get_power_level(void);
void thsq_set_power_level(enum thsq_power_level p);
void thsq_set_default_power_level(enum thsq_power_level p);

enum thsq_freq_conf {
  THSQ_FREQ_CONF_DEFAULT,
  THSQ_FREQ_CONF_ETSI,
  THSQ_FREQ_CONF_FCC
};
enum thsq_freq_conf thsq_get_freq_conf(void);
void thsq_set_freq_conf(enum thsq_freq_conf f);
void thsq_set_default_freq_conf(enum thsq_freq_conf f);

void thsq_peui_init(void);
void thsq_stats_dbgstats_init(void);

struct thsq_chunk;

typedef void (* thsq_chunk_callback_t)(struct thsq_chunk *c, void *ptr, uint32_t count, int maxsize);

struct thsq_chunk {
  struct thsq_chunk *next;
  const char *name;
  void *ptr;
  thsq_chunk_callback_t callback;
  uint32_t count;
};

void thsq_chunk_init(struct thsq_chunk *c, const char *name, thsq_chunk_callback_t callback, void *ptr);
int thsq_chunk_send(struct thsq_chunk *c, const uint8_t *data, int datalen);
int thsq_chunk_send_timestamp(struct thsq_chunk *c, const uint8_t *data, int datalen, uint32_t timestamp);

void thsq_enable_invites(void);
void thsq_enable_configurable_server_addresses(void);

void thsq_invite(uint8_t txpower, char *bondid);
void thsq_listen_for_bond_messages(clock_time_t duration);

void thsq_txpower_init(void);

#endif /* THSQ_H */