1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

sys/senml: add SenML modules

Add a basic SenML module and submodules with support for:

- Encoding SenML values as CBOR using NanoCBOR.
- Converting from Phydat to SenML.
- Reading and encoding SAUL sensors.
This commit is contained in:
Silke Hofstra 2021-04-23 11:32:36 +02:00 committed by Francisco Molina
parent 4fb2394f1d
commit 9d61bdbb06
32 changed files with 1989 additions and 0 deletions

View File

@ -116,3 +116,6 @@ ether
# crate (Rust's package format) => create
crate
# VAs (volt ampere second) => was
vas

View File

@ -0,0 +1,23 @@
# name of your application
APPLICATION = senml_saul_example
# If no BOARD is found in the environment, use this default:
BOARD ?= native
# This has to be the absolute path to the RIOT base directory:
RIOTBASE ?= $(CURDIR)/../..
# we want to use SAUL and SenML
USEMODULE += saul_default
USEMODULE += senml_saul
USEMODULE += fmt
# Comment this out to disable code in RIOT that does safety checking
# which is not needed in a production environment but helps in the
# development process:
DEVELHELP ?= 1
# Change this to 0 show compiler invocation lines by default:
QUIET ?= 1
include $(RIOTBASE)/Makefile.include

View File

@ -0,0 +1,9 @@
BOARD_INSUFFICIENT_MEMORY := \
arduino-duemilanove \
arduino-leonardo \
arduino-nano \
arduino-uno \
atmega328p \
atmega328p-xplained-mini \
nucleo-l011k4 \
#

View File

@ -0,0 +1,52 @@
/*
* Copyright (C) 2021 Silke Hofstra
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup examples
* @{
*
* @file
* @brief Short SenML SAUL example
*
* @author Silke Hofstra <silke@slxh.eu>
*
* @}
*/
#include <stdio.h>
#include <string.h>
#include "senml/saul.h"
#include "fmt.h"
static uint8_t cbor_buf[1024];
void print_hex(uint8_t *a, size_t len)
{
for (size_t i = 0; i < len; i++) {
print_byte_hex(a[i]);
}
}
int main(void)
{
size_t len = senml_saul_encode_cbor(cbor_buf, sizeof cbor_buf, saul_reg);
if (len == 0) {
print_str("SenML/SAUL error\n");
return 1;
}
print_str("CBOR (");
print_u32_dec(len);
print_str(" B): ");
print_hex(cbor_buf, len);
print_str("\n");
return 0;
}

View File

@ -186,6 +186,9 @@ PSEUDOMODULES += scanf_float
PSEUDOMODULES += sched_cb
PSEUDOMODULES += sched_runq_callback
PSEUDOMODULES += semtech_loramac_rx
PSEUDOMODULES += senml_cbor
PSEUDOMODULES += senml_phydat
PSEUDOMODULES += senml_saul
PSEUDOMODULES += shell_hooks
PSEUDOMODULES += slipdev_stdio
PSEUDOMODULES += slipdev_l2addr

View File

@ -75,6 +75,7 @@ rsource "rtc_utils/Kconfig"
rsource "saul_reg/Kconfig"
rsource "schedstatistics/Kconfig"
rsource "sema/Kconfig"
rsource "senml/Kconfig"
rsource "seq/Kconfig"
rsource "shell/Kconfig"
rsource "test_utils/Kconfig"

View File

@ -386,6 +386,10 @@ ifneq (,$(filter saul_default,$(USEMODULE)))
USEMODULE += saul_reg
endif
ifneq (,$(filter senml%,$(USEMODULE)))
include $(RIOTBASE)/sys/senml/Makefile.dep
endif
ifneq (,$(filter phydat,$(USEMODULE)))
USEMODULE += fmt
endif

526
sys/include/senml.h Normal file
View File

@ -0,0 +1,526 @@
/*
* Copyright (C) 2021 Silke Hofstra
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup sys_senml SenML
* @ingroup sys
* @brief Basic SenML types.
*
* The `senml` module contains the building blocks for using
* [SenML](https://www.rfc-editor.org/rfc/rfc8428).
* This module provides the basic types that can be used with (for example)
* @ref sys_senml_cbor for encoding measurement data.
*
* Some attributes defined in SenML need to be enabled explicitly,
* see @ref senml_attr_t for details. To enable all attributes, set:
*
* ```
* CFLAGS += -DCONFIG_SENML_ATTR_SUM=1
* CFLAGS += -DCONFIG_SENML_ATTR_VERSION=1
* CFLAGS += -DCONFIG_SENML_ATTR_UPDATE_TIME=1
* ```
*
* @{
*
* @file
* @brief Basic SenML types.
*
* @author Silke Hofstra <silke@slxh.eu>
*/
#ifndef SENML_H
#define SENML_H
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include "kernel_defines.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Enable the SenML 'sum' and 'base sum' attributes.
*/
#ifndef CONFIG_SENML_ATTR_SUM
#define CONFIG_SENML_ATTR_SUM 0
#endif
/**
* @brief Enable the SenML 'version' and 'base version' attributes.
*/
#ifndef CONFIG_SENML_ATTR_VERSION
#define CONFIG_SENML_ATTR_VERSION 0
#endif
/**
* @brief Enable the SenML 'update time' attribute.
*/
#ifndef CONFIG_SENML_ATTR_UPDATE_TIME
#define CONFIG_SENML_ATTR_UPDATE_TIME 0
#endif
/**
* @brief SenML units and secondary units
*
* This list contains the SenML units and secondary units as assigned by
* [IANA](https://www.iana.org/assignments/senml/senml.xhtml).
* Units in italic are not recommended to be produced by new implementations.
* Secondary units include the equivalent primary unit.
*/
typedef enum {
/* SenML units from RFC8428 */
SENML_UNIT_NONE, /**< No unit specified */
SENML_UNIT_METER, /**< meter (m) */
SENML_UNIT_KILOGRAM, /**< kilogram (kg) */
SENML_UNIT_GRAM, /**< *gram* (g) */
SENML_UNIT_SECOND, /**< second (s) */
SENML_UNIT_AMPERE, /**< ampere (A) */
SENML_UNIT_KELVIN, /**< kelvin (K) */
SENML_UNIT_CANDELA, /**< candela (cd) */
SENML_UNIT_MOLE, /**< mole (mol) */
SENML_UNIT_HERTZ, /**< hertz (Hz) */
SENML_UNIT_RADIAN, /**< radian (rad) */
SENML_UNIT_STERADIAN, /**< steradian (sr) */
SENML_UNIT_NEWTON, /**< newton (N) */
SENML_UNIT_PASCAL, /**< pascal (Pa) */
SENML_UNIT_JOULE, /**< joule (J) */
SENML_UNIT_WATT, /**< watt (W) */
SENML_UNIT_COULOMB, /**< coulomb (C) */
SENML_UNIT_VOLT, /**< volt (V) */
SENML_UNIT_FARAD, /**< farad (F) */
SENML_UNIT_OHM, /**< ohm (Ohm) */
SENML_UNIT_SIEMENS, /**< siemens (S) */
SENML_UNIT_WEBER, /**< weber (Wb) */
SENML_UNIT_TESLA, /**< tesla (T) */
SENML_UNIT_HENRY, /**< henry (H) */
SENML_UNIT_CELSIUS, /**< degrees Celsius (Cel) */
SENML_UNIT_LUMEN, /**< lumen (lm) */
SENML_UNIT_LUX, /**< lux (lx) */
SENML_UNIT_BECQUEREL, /**< becquerel (Bq) */
SENML_UNIT_GRAY, /**< gray (Gy) */
SENML_UNIT_SIEVERT, /**< sievert (Sv) */
SENML_UNIT_KATAL, /**< katal (kat) */
SENML_UNIT_SQUARE_METER, /**< square meter (area) (m2) */
SENML_UNIT_CUBIC_METER, /**< cubic meter (volume) (m3) */
SENML_UNIT_LITER, /**< *liter (volume)* (l) */
SENML_UNIT_METER_PER_SECOND, /**< meter per second (velocity) (m/s) */
SENML_UNIT_METER_PER_SQUARE_SECOND, /**< meter per square second (acceleration) (m/s2) */
SENML_UNIT_CUBIC_METER_PER_SECOND, /**< cubic meter per second (flow rate) (m3/s) */
SENML_UNIT_LITER_PER_SECOND, /**< *liter per second (flow rate)* (l/s) */
SENML_UNIT_WATT_PER_SQUARE_METER, /**< watt per square meter (irradiance) (W/m2) */
SENML_UNIT_CANDELA_PER_SQUARE_METER, /**< candela per square meter (luminance) (cd/m2) */
SENML_UNIT_BIT, /**< bit (information content) (bit) */
SENML_UNIT_BIT_PER_SECOND, /**< bit per second (data rate) (bit/s) */
SENML_UNIT_LATITUDE, /**< degrees latitude (lat) */
SENML_UNIT_LONGITUDE, /**< degrees longitude (lon) */
SENML_UNIT_PH, /**< pH value (acidity; logarithmic quantity) (pH) */
SENML_UNIT_DECIBEL, /**< decibel (logarithmic quantity) (dB) */
SENML_UNIT_DBW, /**< decibel relative to 1 W (power level) (dBW) */
SENML_UNIT_BEL, /**< *bel (sound pressure level; logarithmic quantity)* (Bspl) */
SENML_UNIT_COUNT, /**< 1 (counter value) (count) */
SENML_UNIT_RATIO, /**< 1 (ratio e.g., value of a switch) (/) */
SENML_UNIT_RATIO_2, /**< *1 (ratio e.g., value of a switch)* (%) */
SENML_UNIT_RELATIVE_HUMIDITY_PERCENT, /**< Percentage (Relative Humidity) (%RH) */
SENML_UNIT_REMAINING_BATTERY_PERCENT, /**< Percentage (remaining battery energy level) (%EL) */
SENML_UNIT_REMAINING_BATTERY_SECONDS, /**< seconds (remaining battery energy level) (EL) */
SENML_UNIT_RATE, /**< 1 per second (event rate) (1/s) */
SENML_UNIT_RPM, /**< *1 per minute (event rate, "rpm")* (1/min) */
SENML_UNIT_BEAT_PER_MINUTE, /**< *1 per minute (heart rate in beats per minute)* (beat/min)) */
SENML_UNIT_BEATS, /**< *1 (Cumulative number of heart beats)* (beats) */
SENML_UNIT_SIEMENS_PER_METER, /**< Siemens per meter (conductivity) (S/m) */
/* SenML units from RFC8798 */
SENML_UNIT_BYTE, /**< Byte (information content) (B) */
SENML_UNIT_VOLT_AMPERE, /**< volt-ampere (Apparent Power) (VA) */
SENML_UNIT_VOLT_AMPERE_SECOND, /**< volt-ampere second (Apparent Energy) (VAs) */
SENML_UNIT_VOLT_AMPERE_REACTIVE, /**< volt-ampere reactive (Reactive Power) (var) */
SENML_UNIT_VOLT_AMPERE_REACTIVE_SECOND, /**< volt-ampere-reactive second (Reactive Energy) (vars) */
SENML_UNIT_JOULE_PER_METER, /**< joule per meter (Energy per distance) (J/m) */
SENML_UNIT_KILOGRAM_PER_CUBIC_METER, /**< kilogram per cubic meter (mass density, mass concentration) (kg/m3) */
SENML_UNIT_DEGREE, /**< *degree (angle)* (deg) */
/* SenML units from ISO7027-1:2016 */
SENML_UNIT_NEPHELOMETRIC_TURBIDITY_UNIT, /**< Nephelometric Turbidity Unit (NTU) */
/* SenML secondary units from RFC8798 */
SENML_UNIT_MILLISECOND, /**< millisecond (ms, equivalent to 1/1000 s) */
SENML_UNIT_MINUTE, /**< minute (min, equivalent to 60 s) */
SENML_UNIT_HOUR, /**< hour (h, equivalent to 3600 s) */
SENML_UNIT_MEGAHERTZ, /**< megahertz (MHz, equivalent to 1000000 Hz) */
SENML_UNIT_KILOWATT, /**< kilowatt (kW, equivalent to 1000 W) */
SENML_UNIT_KILOVOLT_AMPERE, /**< kilovolt-ampere (kVA, equivalent to 1000 VA) */
SENML_UNIT_KILOVAR, /**< kilovar (kvar, equivalent to 1000 var) */
SENML_UNIT_AMPERE_HOUR, /**< ampere-hour (Ah, equivalent to 3600 C) */
SENML_UNIT_WATT_HOUR, /**< watt-hour (Wh, equivalent to 3600 J) */
SENML_UNIT_KILOWATT_HOUR, /**< kilowatt-hour (kWh, equivalent to 3600000 J) */
SENML_UNIT_VAR_HOUR, /**< var-hour (varh, equivalent to 3600 vars) */
SENML_UNIT_KILOVAR_HOUR, /**< kilovar-hour (kvarh, equivalent to 3600000 vars) */
SENML_UNIT_KILOVOLT_AMPERE_HOUR, /**< kilovolt-ampere-hour (kVAh, equivalent to 3600000 VAs) */
SENML_UNIT_WATT_HOUR_PER_KILOMETER, /**< watt-hour per kilometer (Wh/km, equivalent to 3.6 J/m) */
SENML_UNIT_KIBIBYTE, /**< kibibyte (KiB, equivalent to 1024 B) */
SENML_UNIT_GIGABYTE, /**< gigabyte (GB, equivalent to 1e9 B) */
SENML_UNIT_MEGABIT_PER_SECOND, /**< megabit per second (Mbit/s, equivalent to 1000000 bit/s) */
SENML_UNIT_BYTE_PER_SECOND, /**< byte per second (B/s, equivalent to 8 bit/s) */
SENML_UNIT_MEGABYTE_PER_SECOND, /**< megabyte per second (MB/s, equivalent to 8000000 bit/s) */
SENML_UNIT_MILLIVOLT, /**< millivolt (mV, equivalent to 1/1000 V) */
SENML_UNIT_MILLIAMPERE, /**< milliampere (mA, equivalent to 1/1000 A) */
SENML_UNIT_DECIBEL_MILLIWATT, /**< decibel (milliwatt) (dBm, equivalent to -29 dBW) */
SENML_UNIT_MICROGRAM_PER_CUBIC_METER, /**< microgram per cubic meter (ug/m3, equivalent to 1e-9 kg/m3) */
SENML_UNIT_MILLIMETER_PER_HOUR, /**< millimeter per hour (mm/h, equivalent to 1/3600000 m/s) */
SENML_UNIT_METER_PER_HOUR, /**< meter per hour (m/h, equivalent to 1/3600 m/s) */
SENML_UNIT_PARTS_PER_MILLION, /**< parts per million (ppm, equivalent to 1e-6 '/') */
SENML_UNIT_PERCENT, /**< percent (/100, equivalent to 1/100 '/') */
SENML_UNIT_PERMILLE, /**< permille (/1000, equivalent to 1/1000 '/') */
SENML_UNIT_HECTOPASCAL, /**< hectopascal (hPa, equivalent to 100 Pa) */
SENML_UNIT_MILLIMETER, /**< millimeter (mm, equivalent to 1/1000 m) */
SENML_UNIT_CENTIMETER, /**< centimeter (cm, equivalent to 1/100 m) */
SENML_UNIT_KILOMETER, /**< kilometer (km, equivalent to 1000 m) */
SENML_UNIT_KILOMETER_PER_HOUR, /**< kilometer per hour (km/h, equivalent to 1/3.6 m/s) */
/* SenML secondary units from CoRE-1 */
SENML_UNIT_PARTS_PER_BILLION, /**< parts per billion (ppb, equivalent to 1e-9 '/') */
SENML_UNIT_PARTS_PER_TRILLION, /**< parts per trillion (ppt, equivalent to 1e-12 '/') */
SENML_UNIT_VOLT_AMPERE_HOUR, /**< volt-ampere-hour (VAh, equivalent to 3600 VAs) */
SENML_UNIT_MILLIGRAM_PER_LITER, /**< milligram per liter (mg/l, equivalent to 1/1000 kg/m3) */
SENML_UNIT_MICROGRAM_PER_LITER, /**< microgram per liter (ug/l, equivalent to 1e-6 kg/m3) */
SENML_UNIT_GRAM_PER_LITER, /**< gram per liter (g/l, equivalent to 1 kg/m3) */
} senml_unit_t;
/**
* @brief SenML numeric value types.
*
*/
typedef enum {
SENML_TYPE_NUMERIC_UINT, /**< Unsigned integer */
SENML_TYPE_NUMERIC_INT, /**< Integer */
SENML_TYPE_NUMERIC_FLOAT, /**< Floating point number */
SENML_TYPE_NUMERIC_DOUBLE, /**< Double-precision floating point number */
SENML_TYPE_NUMERIC_DECFRAC, /**< Decimal fraction */
} senml_value_type_t;
/**
* @brief Decimal fraction containing a value in the form of m * 10^e.
*/
typedef struct {
int32_t e; /**< Exponent */
int32_t m; /**< Mantissa */
} senml_decfrac_t;
/**
* @brief SenML numeric value.
*
* Various SenML attributes (see @ref senml_attr_t) may contain any 'numeric'
* types. This struct is used to contain these.
*/
typedef struct {
senml_value_type_t type; /**< Type of the value */
union {
uint64_t u;
int64_t i;
float f;
double d;
struct { int32_t e; int32_t m; } df /** Decimal fraction */;
} value; /**< Value data */
} senml_numeric_t;
/**
* @brief SenML common record attributes.
* All of these values are optional: empty or 0 values will not be encoded.
* Note that some attributes need to be enabled explicitly.
*/
typedef struct {
const char *base_name; /**< Base Name */
senml_numeric_t base_time; /**< Base Time */
senml_unit_t base_unit; /**< Base Unit */
senml_numeric_t base_value; /**< Base Value */
#if IS_ACTIVE(CONFIG_SENML_ATTR_SUM) || defined(DOXYGEN)
senml_numeric_t base_sum; /**< Base Sum, set `CONFIG_SENML_ATTR_SUM` to 1 to enable */
#endif
#if IS_ACTIVE(CONFIG_SENML_ATTR_VERSION) || defined(DOXYGEN)
uint64_t base_version; /**< Base Version, set `CONFIG_SENML_ATTR_VERSION` to 1 to enable */
#endif
const char *name; /**< Name of the measurement */
senml_unit_t unit; /**< Unit */
#if IS_ACTIVE(CONFIG_SENML_ATTR_SUM) || defined(DOXYGEN)
senml_numeric_t sum; /**< Sum, set `CONFIG_SENML_ATTR_SUM` to 1 to enable */
#endif
senml_numeric_t time; /**< Time of the measurement (relative or Unix) in seconds */
#if IS_ACTIVE(CONFIG_SENML_ATTR_UPDATE_TIME) || defined(DOXYGEN)
senml_numeric_t update_time; /**< Maximum time before the next sensor value, set `CONFIG_SENML_ATTR_UPDATE_TIME` to 1 to enable */
#endif
} senml_attr_t;
/**
* @brief SenML string value.
*/
typedef struct {
senml_attr_t attr; /**< SenML attributes */
senml_numeric_t value; /**< Value */
} senml_value_t;
/**
* @brief SenML string value.
*/
typedef struct {
senml_attr_t attr; /**< SenML attributes */
const char *value; /**< Value */
size_t len; /**< Value length */
} senml_string_value_t;
/**
* @brief SenML boolean value.
*/
typedef struct {
senml_attr_t attr; /**< SenML attributes */
bool value; /**< Value */
} senml_bool_value_t;
/**
* @brief SenML data value.
*/
typedef struct {
senml_attr_t attr; /**< SenML attributes */
const uint8_t *value; /**< Value */
size_t len; /**< Value length */
} senml_data_value_t;
/**
* @brief Create a floating point numeric value.
*
* @param v Value to encode.
* @return Numeric value containing the given value.
*/
static inline senml_numeric_t senml_float(float v)
{
return (senml_numeric_t){ .type = SENML_TYPE_NUMERIC_FLOAT,
.value = { .f = v } };
}
/**
* @brief Set a floating point numeric value.
*
* @param n Numeric value to set.
* @param v Value to encode.
*/
static inline void senml_set_float(senml_numeric_t *n, float v)
{
n->type = SENML_TYPE_NUMERIC_FLOAT;
n->value.f = v;
}
/**
* @brief Create a double precision floating point numeric value.
*
* @param v Value to encode.
* @return Numeric value containing the given value.
*/
static inline senml_numeric_t senml_double(double v)
{
return (senml_numeric_t){ .type = SENML_TYPE_NUMERIC_DOUBLE,
.value = { .d = v } };
}
/**
* @brief Set a double precision floating point numeric value.
*
* @param n Numeric value to set.
* @param v Value to encode.
*/
static inline void senml_set_double(senml_numeric_t *n, double v)
{
n->type = SENML_TYPE_NUMERIC_DOUBLE;
n->value.d = v;
}
/**
* @brief Create an integer numeric value.
*
* @param v Value to encode.
* @return Numeric value containing the given value.
*/
static inline senml_numeric_t senml_int(int64_t v)
{
return (senml_numeric_t){ .type = SENML_TYPE_NUMERIC_INT,
.value = { .i = v } };
}
/**
* @brief Set an integer numeric value.
*
* @param n Numeric value to set.
* @param v Value to encode.
*/
static inline void senml_set_int(senml_numeric_t *n, int64_t v)
{
n->type = SENML_TYPE_NUMERIC_INT;
n->value.i = v;
}
/**
* @brief Create an unsigned integer numeric value.
*
* @param v Value to encode.
* @return Numeric value containing the given value.
*/
static inline senml_numeric_t senml_uint(uint64_t v)
{
return (senml_numeric_t){ .type = SENML_TYPE_NUMERIC_UINT,
.value = { .u = v } };
}
/**
* @brief Set an unsigned integer numeric value.
*
* @param n Numeric value to set.
* @param v Value to encode.
*/
static inline void set_senml_uint(senml_numeric_t *n, uint64_t v)
{
n->type = SENML_TYPE_NUMERIC_UINT;
n->value.u = v;
}
/**
* @brief Create a decimal fraction numeric value in the form `m*10^e`.
*
* @param m Mantissa (value) to encode.
* @param e Exponent (scale) to encode.
* @return Numeric value containing the given value.
*/
static inline senml_numeric_t senml_decfrac(int32_t m, int32_t e)
{
return (senml_numeric_t){ .type = SENML_TYPE_NUMERIC_DECFRAC,
.value = { .df = { .e = e, .m = m } } };
}
/**
* @brief Set a decimal fraction numeric value in the form `m*10^e`.
*
* @param n Numeric value to set.
* @param m Mantissa (value) to encode.
* @param e Exponent (scale) to encode.
*/
static inline void senml_set_decfrac(senml_numeric_t *n, int32_t m, int32_t e)
{
n->type = SENML_TYPE_NUMERIC_DECFRAC;
n->value.df.e = e;
n->value.df.m = m;
}
/**
* @brief Get an integer representation of a duration in seconds.
*
* @param s Duration in seconds.
* @return Numeric representation of the duration in seconds.
*/
static inline senml_numeric_t senml_duration_s(int64_t s)
{
return senml_int(s);
}
/**
* @brief Set an integer representation of a duration in seconds.
*
* @param n Numeric value to set.
* @param s Duration in seconds.
*/
static inline void senml_set_duration_s(senml_numeric_t *n, int64_t s)
{
senml_set_int(n, s);
}
/**
* @brief Get a @ref senml_decfrac_t representation of a duration in milliseconds.
*
* @param ms Duration in milliseconds.
* @return Numeric representation of the duration.
*/
static inline senml_numeric_t senml_duration_ms(int32_t ms)
{
return senml_decfrac(ms, -3);
}
/**
* @brief Set a @ref senml_decfrac_t representation of a duration in milliseconds.
*
* @param n Numeric value to set.
* @param ms Duration in milliseconds.
* @return Numeric representation of the duration.
*/
static inline void senml_set_duration_ms(senml_numeric_t *n, int32_t ms)
{
senml_set_decfrac(n, ms, -3);
}
/**
* @brief Get a @ref senml_decfrac_t representation of a duration in microseconds.
*
* @param us Duration in microseconds.
* @return Numeric representation of the duration.
*/
static inline senml_numeric_t senml_duration_us(int32_t us)
{
return senml_decfrac(us, -6);
}
/**
* @brief Get a @ref senml_decfrac_t representation of a duration in microseconds.
*
* @param n Numeric value to set.
* @param us Duration in microseconds.
* @return Numeric representation of the duration.
*/
static inline void senml_set_duration_us(senml_numeric_t *n, int32_t us)
{
senml_set_decfrac(n, us, -6);
}
/**
* @brief Get a @ref senml_decfrac_t representation of a duration in nanoseconds.
*
* @param ns Duration in nanoseconds.
* @return Numeric representation of the duration.
*/
static inline senml_numeric_t senml_duration_ns(int32_t ns)
{
return senml_decfrac(ns, -9);
}
/**
* @brief Set a @ref senml_decfrac_t representation of a duration in nanoseconds.
*
* @param n Numeric value to set.
* @param ns Duration in nanoseconds.
*/
static inline void senml_set_duration_ns(senml_numeric_t *n, int32_t ns)
{
senml_set_decfrac(n, ns, -9);
}
/**
* @brief Convert a SenML unit to a string.
*
* See the [SenML units](https://www.iana.org/assignments/senml/senml.xhtml#senml-units) and
* [Secondary units](https://www.iana.org/assignments/senml/senml.xhtml#secondary-units) from IANA.
* Values not defined in @ref senml_unit_t will result in an empty string.
*
* @param unit Unit to convert to string.
*
* @return String representation of the unit.
*/
const char *senml_unit_to_str(senml_unit_t unit);
#ifdef __cplusplus
}
#endif
#endif /* SENML_H */
/** @} */

121
sys/include/senml/cbor.h Normal file
View File

@ -0,0 +1,121 @@
/*
* Copyright (C) 2021 Silke Hofstra
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup sys_senml_cbor SenML CBOR
* @ingroup sys_senml
* @brief Functionality for encoding SenML values as CBOR
*
* The `senml_cbor` module contains functionality for encoding @ref sys_senml
* values to CBOR using @ref pkg_nanocbor.
*
* @{
*
* @file
* @brief Functionality for encoding SenML values as CBOR
*
* @author Silke Hofstra <silke@slxh.eu>
*/
#ifndef SENML_CBOR_H
#define SENML_CBOR_H
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include "senml.h"
#include "nanocbor/nanocbor.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief SenML CBOR labels
*
* This list contains the SenML CBOR labels as assigned by IANA.
*/
typedef enum {
SENML_LABEL_BASE_VERSION = -1,
SENML_LABEL_BASE_NAME = -2,
SENML_LABEL_BASE_TIME = -3,
SENML_LABEL_BASE_UNIT = -4,
SENML_LABEL_BASE_VALUE = -5,
SENML_LABEL_BASE_SUM = -6,
SENML_LABEL_NAME = 0,
SENML_LABEL_UNIT = 1,
SENML_LABEL_VALUE = 2,
SENML_LABEL_STRING_VALUE = 3,
SENML_LABEL_BOOLEAN_VALUE = 4,
SENML_LABEL_SUM = 5,
SENML_LABEL_TIME = 6,
SENML_LABEL_UPDATE_TIME = 7,
SENML_LABEL_DATA_VALUE = 8,
} senml_cbor_label_t;
#if IS_ACTIVE(CONFIG_SENML_ATTR_SUM) || defined(DOXYGEN)
/**
* @brief Encode @ref senml_attr_t containing `sum` as CBOR.
*
* Requires the `sum` attribute to be enabled by setting `CONFIG_SENML_ATTR_SUM` to 1.
*
* @param enc NanoCBOR encoder.
* @param attr Attributes (including `sum`) to encode.
*
* @return Size of the encoded data.
*/
int senml_encode_sum_cbor(nanocbor_encoder_t *enc, const senml_attr_t *attr);
#endif
/**
* @brief Encode @ref senml_bool_value_t as CBOR.
*
* @param enc NanoCBOR encoder.
* @param val value to encode.
*
* @return Size of the encoded data.
*/
int senml_encode_bool_cbor(nanocbor_encoder_t *enc, const senml_bool_value_t *val);
/**
* @brief Encode @ref senml_value_t as CBOR.
*
* @param enc NanoCBOR encoder.
* @param val value to encode.
*
* @return Size of the encoded data.
*/
int senml_encode_value_cbor(nanocbor_encoder_t *enc, const senml_value_t *val);
/**
* @brief Encode @ref senml_string_value_t as CBOR.
*
* @param enc NanoCBOR encoder.
* @param val value to encode.
*
* @return Size of the encoded data.
*/
int senml_encode_string_cbor(nanocbor_encoder_t *enc, const senml_string_value_t *val);
/**
* @brief Encode @ref senml_data_value_t as CBOR.
*
* @param enc NanoCBOR encoder.
* @param val value to encode.
*
* @return Size of the encoded data.
*/
int senml_encode_data_cbor(nanocbor_encoder_t *enc, const senml_data_value_t *val);
#ifdef __cplusplus
}
#endif
#endif /* SENML_CBOR_H */
/** @} */

View File

@ -0,0 +1,98 @@
/*
* Copyright (C) 2021 Silke Hofstra
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup sys_senml_phydat SenML Phydat
* @ingroup sys_senml
* @brief Functionality for converting from @ref sys_phydat to @ref sys_senml
*
* The `senml_phydat` module contains various functions for converting
* @ref sys_phydat values to @ref sys_senml.
*
* @{
*
* @file
* @brief Functionality for converting from @ref sys_phydat to @ref sys_senml
*
* @author Silke Hofstra <silke@slxh.eu>
*/
#ifndef SENML_PHYDAT_H
#define SENML_PHYDAT_H
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include "senml.h"
#include <phydat.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Create a SenML boolean value.
*
* Writes the value of the given @p dim of @p phydat as a boolean.
* @p phydat is assumed to be of @ref UNIT_BOOL.
*
* @note `phydat->scale` must be zero.
*
* @param[out] senml SenML value to store value in.
* @param[in] phydat Phydat to convert.
* @param[in] dim Dimension of @p phydat to convert.
*/
void phydat_to_senml_bool(senml_bool_value_t *senml, const phydat_t *phydat, const uint8_t dim);
/**
* @brief Create a SenML float value.
*
* Writes the value of the given @p dim of @p phydat as a float.
* The unit of @p phydat is converted using the following rules:
*
* - @ref UNIT_TIME is converted to @ref SENML_UNIT_SECOND.
* - @ref UNIT_F is converted to @ref SENML_UNIT_KELVIN.
* - @ref UNIT_G is converted to @ref SENML_UNIT_METER_PER_SQUARE_SECOND.
* - @ref UNIT_BAR is converted to @ref SENML_UNIT_PASCAL.
* - @ref UNIT_GPM3 is converted to @ref SENML_UNIT_KILOGRAM_PER_CUBIC_METER.
* - @ref UNIT_GS is converted to @ref SENML_UNIT_TESLA.
* - Compatible units are set to their SenML equivalent.
* - Incompatible (or unknown) units are set to @ref SENML_UNIT_NONE.
*
* @param[out] senml SenML value to store value in.
* @param[in] phydat Phydat to convert.
* @param[in] dim Dimension of @p phydat to convert.
*/
void phydat_to_senml_float(senml_value_t *senml, const phydat_t *phydat, const uint8_t dim);
/**
* @brief Create a SenML decimal fraction value.
*
* Writes the value of the given @p dim of @p phydat as a decimal value.
* The unit of @p phydat is converted using the following rules:
*
* - @ref UNIT_TIME is converted to @ref SENML_UNIT_SECOND.
* - @ref UNIT_BAR is converted to @ref SENML_UNIT_PASCAL.
* - @ref UNIT_GPM3 is converted to @ref SENML_UNIT_KILOGRAM_PER_CUBIC_METER.
* - @ref UNIT_GS is converted to @ref SENML_UNIT_TESLA.
* - Compatible units are set to their SenML equivalent.
* - Incompatible (or unknown) units are set to @ref SENML_UNIT_NONE.
*
* @param[out] senml SenML value to store value in.
* @param[in] phydat Phydat to convert.
* @param[in] dim Dimension of @p phydat to convert.
*/
void phydat_to_senml_decimal(senml_value_t *senml, const phydat_t *phydat, const uint8_t dim);
#ifdef __cplusplus
}
#endif
#endif /* SENML_PHYDAT_H */
/** @} */

76
sys/include/senml/saul.h Normal file
View File

@ -0,0 +1,76 @@
/*
* Copyright (C) 2021 Silke Hofstra
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup sys_senml_saul SenML SAUL
* @ingroup sys_senml
* @brief Functionality for reading @ref drivers_saul sensors as @ref sys_senml
*
* The `senml_saul` module contains functions for reading sensors using
* @ref drivers_saul and converting them to @ref sys_senml_cbor.
*
* @{
*
* @file
* @brief Functionality for reading @ref drivers_saul sensors as @ref sys_senml
*
* @author Silke Hofstra <silke@slxh.eu>
*/
#ifndef SENML_SAUL_H
#define SENML_SAUL_H
#include <stdint.h>
#include "nanocbor/nanocbor.h"
#include "saul_reg.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Use floats instead of decimal types when encoding SAUL measurements.
*
* If this is set to `1` the @ref phydat_t values from SAUL are converted to
* @ref senml_numeric_t using @ref phydat_to_senml_float.
* Values are converted using @ref phydat_to_senml_decimal otherwise.
*/
#ifndef CONFIG_SENML_SAUL_USE_FLOATS
#define CONFIG_SENML_SAUL_USE_FLOATS 0
#endif
/**
* @brief Encode a single @ref drivers_saul sensor as senml+cbor.
*
* @param enc NanoCBOR encoder.
* @param dev SAUL sensor to encode.
*
* @return Number of dimensions encoded. Less or equal to 0 on error.
*/
int senml_saul_reg_encode_cbor(nanocbor_encoder_t *enc, saul_reg_t *dev);
/**
* @brief Encode all sensors from a @ref drivers_saul registry as senml+cbor.
*
* This functions reads all @ref drivers_saul sensors in a registry and encodes
* the values as SenML/CBOR.
*
* @param buf Buffer to store the CBOR in.
* @param len Length of the buffer.
* @param reg SAUL registry to encode.
*
* @return Size of the encoded data.
*/
size_t senml_saul_encode_cbor(uint8_t *buf, size_t len, saul_reg_t *reg);
#ifdef __cplusplus
}
#endif
#endif /* SENML_SAUL_H */
/** @} */

38
sys/senml/Kconfig Normal file
View File

@ -0,0 +1,38 @@
# Copyright (c) 2021 Silke Hofstra
#
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.
#
config MODULE_SENML
bool "SenML"
depends on TEST_KCONFIG
help
Generic data container for physical data and utility functions.
config MODULE_SENML_SAUL
bool "SenML SAUL integration"
depends on TEST_KCONFIG
select MODULE_SENML_PHYDAT
select MODULE_SENML_CBOR
depends on MODULE_SAUL
depends on MODULE_SAUL_REG
help
Generic data container for physical data and utility functions.
config MODULE_SENML_PHYDAT
bool "SenML Phydat support"
depends on TEST_KCONFIG
select MODULE_SENML
select MODULE_PHYDAT
help
Utilities to convert Phydat valus to SenML
config MODULE_SENML_CBOR
bool "SenML CBOR enconding"
depends on TEST_KCONFIG
select MODULE_SENML
select PACKAGE_NANOCBOR
help
Support for CBOR encoding of SenML values

7
sys/senml/Makefile Normal file
View File

@ -0,0 +1,7 @@
MODULE = senml
SRC = senml.c
SUBMODULES = 1
include $(RIOTBASE)/Makefile.base

16
sys/senml/Makefile.dep Normal file
View File

@ -0,0 +1,16 @@
ifneq (,$(filter senml_saul,$(USEMODULE)))
USEMODULE += senml
USEMODULE += senml_cbor
USEMODULE += senml_phydat
USEMODULE += saul_reg
endif
ifneq (,$(filter senml_cbor,$(USEMODULE)))
USEPKG += nanocbor
USEMODULE += senml
endif
ifneq (,$(filter senml_phydat,$(USEMODULE)))
USEMODULE += senml
USEMODULE += phydat
endif

155
sys/senml/cbor.c Normal file
View File

@ -0,0 +1,155 @@
/*
* Copyright (C) 2021 Silke Hofstra
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
#include "senml.h"
#include "senml/cbor.h"
#include "nanocbor/nanocbor.h"
static int senml_encode_numeric_cbor(nanocbor_encoder_t *enc, const senml_numeric_t *v)
{
switch (v->type) {
case SENML_TYPE_NUMERIC_FLOAT:
return nanocbor_fmt_float(enc, v->value.f);
case SENML_TYPE_NUMERIC_DOUBLE:
return nanocbor_fmt_double(enc, v->value.d);
case SENML_TYPE_NUMERIC_INT:
return nanocbor_fmt_int(enc, v->value.i);
case SENML_TYPE_NUMERIC_UINT:
return nanocbor_fmt_uint(enc, v->value.u);
case SENML_TYPE_NUMERIC_DECFRAC:
return nanocbor_fmt_decimal_frac(enc, v->value.df.e, v->value.df.m);
default:
return 0;
}
}
static int senml_encode_start_cbor(nanocbor_encoder_t *enc, const senml_attr_t *attr,
bool sum_value)
{
int n = nanocbor_fmt_map(enc, !sum_value
+ (attr->base_name != NULL)
+ (attr->base_time.value.u != 0)
+ (attr->base_unit != SENML_UNIT_NONE)
+ (attr->base_value.value.u != 0)
#if IS_ACTIVE(CONFIG_SENML_ATTR_SUM)
+ (attr->base_sum.value.u != 0)
#endif
#if IS_ACTIVE(CONFIG_SENML_ATTR_VERSION)
+ (attr->base_version != 0 && attr->base_version != 10)
#endif
+ (attr->name != NULL)
+ (attr->unit != SENML_UNIT_NONE)
#if IS_ACTIVE(CONFIG_SENML_ATTR_SUM)
+ (sum_value || attr->sum.value.u != 0)
#endif
+ (attr->time.value.u != 0)
#if IS_ACTIVE(CONFIG_SENML_ATTR_UPDATE_TIME)
+ (attr->update_time.value.u != 0)
#endif
);
if (attr->base_name != NULL) {
n += nanocbor_fmt_int(enc, SENML_LABEL_BASE_NAME);
n += nanocbor_put_tstr(enc, attr->base_name);
}
if (attr->base_time.value.u != 0) {
n += nanocbor_fmt_int(enc, SENML_LABEL_BASE_TIME);
n += senml_encode_numeric_cbor(enc, &attr->base_time);
}
if (attr->base_unit != SENML_UNIT_NONE) {
n += nanocbor_fmt_int(enc, SENML_LABEL_BASE_UNIT);
n += nanocbor_put_tstr(enc, senml_unit_to_str(attr->base_unit));
}
if (attr->base_value.value.u != 0) {
n += nanocbor_fmt_int(enc, SENML_LABEL_BASE_VALUE);
n += senml_encode_numeric_cbor(enc, &attr->base_value);
}
#if IS_ACTIVE(CONFIG_SENML_ATTR_SUM)
if (attr->base_sum.value.u != 0) {
n += nanocbor_fmt_int(enc, SENML_LABEL_BASE_SUM);
n += senml_encode_numeric_cbor(enc, &attr->base_sum);
}
#endif
#if IS_ACTIVE(CONFIG_SENML_ATTR_VERSION)
if (attr->base_version != 0 && attr->base_version != 10) {
n += nanocbor_fmt_int(enc, SENML_LABEL_BASE_VERSION);
n += nanocbor_fmt_uint(enc, attr->base_version);
}
#endif
if (attr->name != NULL) {
n += nanocbor_fmt_int(enc, SENML_LABEL_NAME);
n += nanocbor_put_tstr(enc, attr->name);
}
if (attr->unit != SENML_UNIT_NONE) {
n += nanocbor_fmt_int(enc, SENML_LABEL_UNIT);
n += nanocbor_put_tstr(enc, senml_unit_to_str(attr->unit));
}
#if IS_ACTIVE(CONFIG_SENML_ATTR_SUM)
if (sum_value || attr->sum.value.u != 0) {
n += nanocbor_fmt_int(enc, SENML_LABEL_SUM);
n += senml_encode_numeric_cbor(enc, &attr->sum);
}
#endif
if (attr->time.value.u != 0) {
n += nanocbor_fmt_int(enc, SENML_LABEL_TIME);
n += senml_encode_numeric_cbor(enc, &attr->time);
}
#if IS_ACTIVE(CONFIG_SENML_ATTR_UPDATE_TIME)
if (attr->update_time.value.u != 0) {
n += nanocbor_fmt_int(enc, SENML_LABEL_UPDATE_TIME);
n += senml_encode_numeric_cbor(enc, &attr->update_time);
}
#endif
return n;
}
#if IS_ACTIVE(CONFIG_SENML_ATTR_SUM)
int senml_encode_sum_cbor(nanocbor_encoder_t *enc, const senml_attr_t *attr)
{
return senml_encode_start_cbor(enc, attr, true);
}
#endif
int senml_encode_bool_cbor(nanocbor_encoder_t *enc, const senml_bool_value_t *val)
{
return senml_encode_start_cbor(enc, &val->attr, false) +
nanocbor_fmt_int(enc, SENML_LABEL_BOOLEAN_VALUE) +
nanocbor_fmt_bool(enc, val->value);
}
int senml_encode_value_cbor(nanocbor_encoder_t *enc, const senml_value_t *val)
{
return senml_encode_start_cbor(enc, &val->attr, false) +
nanocbor_fmt_int(enc, SENML_LABEL_VALUE) +
senml_encode_numeric_cbor(enc, &val->value);
}
int senml_encode_string_cbor(nanocbor_encoder_t *enc, const senml_string_value_t *val)
{
return senml_encode_start_cbor(enc, &val->attr, false) +
nanocbor_fmt_int(enc, SENML_LABEL_STRING_VALUE) +
nanocbor_put_tstrn(enc, val->value, val->len);
}
int senml_encode_data_cbor(nanocbor_encoder_t *enc, const senml_data_value_t *val)
{
return senml_encode_start_cbor(enc, &val->attr, false) +
nanocbor_fmt_int(enc, SENML_LABEL_DATA_VALUE) +
nanocbor_put_bstr(enc, val->value, val->len);
}

153
sys/senml/phydat.c Normal file
View File

@ -0,0 +1,153 @@
/*
* Copyright (C) 2021 Silke Hofstra
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
#include <stdint.h>
#include "math.h"
#include "senml.h"
#include "senml/phydat.h"
static uint8_t phydat_unit_to_senml_unit(uint8_t unit)
{
switch (unit) {
/* Compatible units */
case UNIT_TEMP_C: return SENML_UNIT_CELSIUS;
case UNIT_TEMP_K: return SENML_UNIT_KELVIN;
case UNIT_LUX: return SENML_UNIT_LUX;
case UNIT_M: return SENML_UNIT_METER;
case UNIT_M2: return SENML_UNIT_SQUARE_METER;
case UNIT_M3: return SENML_UNIT_CUBIC_METER;
case UNIT_GR: return SENML_UNIT_GRAM;
case UNIT_A: return SENML_UNIT_AMPERE;
case UNIT_V: return SENML_UNIT_VOLT;
case UNIT_W: return SENML_UNIT_WATT;
case UNIT_T: return SENML_UNIT_TESLA;
case UNIT_COULOMB: return SENML_UNIT_COULOMB;
case UNIT_F: return SENML_UNIT_FARAD;
case UNIT_OHM: return SENML_UNIT_OHM;
case UNIT_PH: return SENML_UNIT_PH;
case UNIT_PA: return SENML_UNIT_PASCAL;
case UNIT_CD: return SENML_UNIT_CANDELA;
/* Compatible Secondary units */
case UNIT_DBM: return SENML_UNIT_DECIBEL_MILLIWATT;
case UNIT_PERCENT: return SENML_UNIT_PERCENT;
case UNIT_PERMILL: return SENML_UNIT_PERMILLE;
case UNIT_PPM: return SENML_UNIT_PARTS_PER_MILLION;
case UNIT_PPB: return SENML_UNIT_PARTS_PER_BILLION;
/* Incompatible units */
case UNIT_TEMP_F: return SENML_UNIT_NONE; /* use K or Cel instead */
case UNIT_GS: return SENML_UNIT_NONE; /* use T instead */
case UNIT_G: return SENML_UNIT_NONE; /* use m/s2 instead */
case UNIT_BAR: return SENML_UNIT_NONE; /* use Pa or hPa instead */
case UNIT_TIME: return SENML_UNIT_NONE; /* split into second/minute/hour */
case UNIT_DATE: return SENML_UNIT_NONE; /* split into day/month/year */
case UNIT_GPM3: return SENML_UNIT_NONE; /* use kg/m3 instead */
case UNIT_DPS: return SENML_UNIT_NONE; /* no alternative */
case UNIT_CPM3: return SENML_UNIT_NONE; /* no alternative */
default: return SENML_UNIT_NONE;
}
}
void phydat_to_senml_bool(senml_bool_value_t *senml, const phydat_t *phydat, const uint8_t dim)
{
senml->value = phydat->val[dim] == 1;
senml->attr.unit = SENML_UNIT_NONE;
}
void phydat_to_senml_float(senml_value_t *senml, const phydat_t *phydat, const uint8_t dim)
{
float value = (float)(phydat->val[dim]);
if (phydat->scale) {
value *= pow(10, phydat->scale);
}
switch (phydat->unit) {
/* time conversion */
case UNIT_TIME:
senml->attr.unit = (dim == 0)
? SENML_UNIT_SECOND
: (dim == 1)
? SENML_UNIT_MINUTE
: SENML_UNIT_HOUR;
break;
/* simple conversions */
case UNIT_TEMP_F:
/* convert fahrenheit to kelvin */
value = (value + 459.67) * (5. / 9.);
senml->attr.unit = SENML_UNIT_KELVIN;
break;
case UNIT_G:
/* convert gravitational acceleration to acceleration */
value *= 9.80665;
senml->attr.unit = SENML_UNIT_METER_PER_SQUARE_SECOND;
break;
case UNIT_BAR:
value *= 100000;
senml->attr.unit = SENML_UNIT_PASCAL;
break;
case UNIT_GPM3:
value *= 0.001;
senml->attr.unit = SENML_UNIT_KILOGRAM_PER_CUBIC_METER;
break;
case UNIT_GS:
value *= 0.0001;
senml->attr.unit = SENML_UNIT_TESLA;
break;
/* compatible (or not converted) */
default:
senml->attr.unit = phydat_unit_to_senml_unit(phydat->unit);
break;
}
senml->value = senml_float(value);
}
void phydat_to_senml_decimal(senml_value_t *senml, const phydat_t *phydat, const uint8_t dim)
{
int32_t m = phydat->val[dim];
int32_t e = phydat->scale;
switch (phydat->unit) {
/* time conversion */
case UNIT_TIME:
senml->attr.unit = (dim == 0)
? SENML_UNIT_SECOND
: (dim == 1)
? SENML_UNIT_MINUTE
: SENML_UNIT_HOUR;
break;
/* simple conversions */
case UNIT_BAR:
e += 5;
senml->attr.unit = SENML_UNIT_PASCAL;
break;
case UNIT_GPM3:
e -= 3;
senml->attr.unit = SENML_UNIT_KILOGRAM_PER_CUBIC_METER;
break;
case UNIT_GS:
e -= 4;
senml->attr.unit = SENML_UNIT_TESLA;
break;
/* compatible, or not converted */
default:
senml->attr.unit = phydat_unit_to_senml_unit(phydat->unit);
break;
}
senml->value = senml_decfrac(m, e);
}

99
sys/senml/saul.c Normal file
View File

@ -0,0 +1,99 @@
/*
* Copyright (C) 2021 Silke Hofstra
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
#include "nanocbor/nanocbor.h"
#include "saul_reg.h"
#include "senml.h"
#include "senml/cbor.h"
#include "senml/phydat.h"
#include "senml/saul.h"
static inline void senml_encode_phydat_bool(nanocbor_encoder_t *enc,
const saul_reg_t *dev,
const phydat_t *data,
const uint8_t dim)
{
senml_bool_value_t val = { .attr = { .name = dev->name } };
phydat_to_senml_bool(&val, data, dim);
senml_encode_bool_cbor(enc, &val);
}
static inline uint8_t senml_fix_unit(const saul_reg_t *dev, const uint8_t unit)
{
/* Fix the unit for relative humidity. */
if (dev->driver->type == SAUL_SENSE_HUM &&
unit == SENML_UNIT_PERCENT) {
return SENML_UNIT_RELATIVE_HUMIDITY_PERCENT;
}
return unit;
}
static void senml_encode_phydat_float(nanocbor_encoder_t *enc,
const saul_reg_t *dev,
const phydat_t *data, const uint8_t dim)
{
senml_value_t val = { .attr = { .name = dev->name } };
phydat_to_senml_float(&val, data, dim);
val.attr.unit = senml_fix_unit(dev, val.attr.unit);
senml_encode_value_cbor(enc, &val);
}
static void senml_encode_phydat_decimal(nanocbor_encoder_t *enc,
const saul_reg_t *dev,
const phydat_t *data, const uint8_t dim)
{
senml_value_t val = { .attr = { .name = dev->name } };
phydat_to_senml_decimal(&val, data, dim);
val.attr.unit = senml_fix_unit(dev, val.attr.unit);
senml_encode_value_cbor(enc, &val);
}
int senml_saul_reg_encode_cbor(nanocbor_encoder_t *enc, saul_reg_t *dev)
{
phydat_t data;
int dim = saul_reg_read(dev, &data);
if (dim <= 0) {
return dim;
}
for (uint8_t i = 0; i < dim; i++) {
if (data.unit == UNIT_BOOL) {
senml_encode_phydat_bool(enc, dev, &data, i);
}
else if (CONFIG_SENML_SAUL_USE_FLOATS) {
senml_encode_phydat_float(enc, dev, &data, i);
}
else {
senml_encode_phydat_decimal(enc, dev, &data, i);
}
}
return dim;
}
size_t senml_saul_encode_cbor(uint8_t *buf, size_t len, saul_reg_t *dev)
{
nanocbor_encoder_t enc;
nanocbor_encoder_init(&enc, buf, len);
nanocbor_fmt_array_indefinite(&enc);
while (dev) {
if (senml_saul_reg_encode_cbor(&enc, dev) <= 0) {
return 0;
}
dev = dev->next;
}
nanocbor_fmt_end_indefinite(&enc);
return nanocbor_encoded_len(&enc);
}

127
sys/senml/senml.c Normal file
View File

@ -0,0 +1,127 @@
/*
* Copyright (C) 2021 Silke Hofstra
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
#include "senml.h"
const char *senml_unit_to_str(senml_unit_t unit)
{
switch (unit) {
case SENML_UNIT_NONE: return "";
case SENML_UNIT_METER: return "m";
case SENML_UNIT_KILOGRAM: return "kg";
case SENML_UNIT_GRAM: return "g";
case SENML_UNIT_SECOND: return "s";
case SENML_UNIT_AMPERE: return "A";
case SENML_UNIT_KELVIN: return "K";
case SENML_UNIT_CANDELA: return "cd";
case SENML_UNIT_MOLE: return "mol";
case SENML_UNIT_HERTZ: return "Hz";
case SENML_UNIT_RADIAN: return "rad";
case SENML_UNIT_STERADIAN: return "sr";
case SENML_UNIT_NEWTON: return "N";
case SENML_UNIT_PASCAL: return "Pa";
case SENML_UNIT_JOULE: return "J";
case SENML_UNIT_WATT: return "W";
case SENML_UNIT_COULOMB: return "C";
case SENML_UNIT_VOLT: return "V";
case SENML_UNIT_FARAD: return "F";
case SENML_UNIT_OHM: return "Ohm";
case SENML_UNIT_SIEMENS: return "S";
case SENML_UNIT_WEBER: return "Wb";
case SENML_UNIT_TESLA: return "T";
case SENML_UNIT_HENRY: return "H";
case SENML_UNIT_CELSIUS: return "Cel";
case SENML_UNIT_LUMEN: return "lm";
case SENML_UNIT_LUX: return "lx";
case SENML_UNIT_BECQUEREL: return "Bq";
case SENML_UNIT_GRAY: return "Gy";
case SENML_UNIT_SIEVERT: return "Sv";
case SENML_UNIT_KATAL: return "kat";
case SENML_UNIT_SQUARE_METER: return "m2";
case SENML_UNIT_CUBIC_METER: return "m3";
case SENML_UNIT_LITER: return "l";
case SENML_UNIT_METER_PER_SECOND: return "m/s";
case SENML_UNIT_METER_PER_SQUARE_SECOND: return "m/s2";
case SENML_UNIT_CUBIC_METER_PER_SECOND: return "m3/s";
case SENML_UNIT_LITER_PER_SECOND: return "l/s";
case SENML_UNIT_WATT_PER_SQUARE_METER: return "W/m2";
case SENML_UNIT_CANDELA_PER_SQUARE_METER: return "cd/m2";
case SENML_UNIT_BIT: return "bit";
case SENML_UNIT_BIT_PER_SECOND: return "bit/s";
case SENML_UNIT_LATITUDE: return "lat";
case SENML_UNIT_LONGITUDE: return "lon";
case SENML_UNIT_PH: return "pH";
case SENML_UNIT_DECIBEL: return "dB";
case SENML_UNIT_DBW: return "dBW";
case SENML_UNIT_BEL: return "Bspl";
case SENML_UNIT_COUNT: return "count";
case SENML_UNIT_RATIO: return "/";
case SENML_UNIT_RATIO_2: return "%";
case SENML_UNIT_RELATIVE_HUMIDITY_PERCENT: return "%RH";
case SENML_UNIT_REMAINING_BATTERY_PERCENT: return "%EL";
case SENML_UNIT_REMAINING_BATTERY_SECONDS: return "EL";
case SENML_UNIT_RATE: return "1/s";
case SENML_UNIT_RPM: return "1/min";
case SENML_UNIT_BEAT_PER_MINUTE: return "beat/min";
case SENML_UNIT_BEATS: return "beats";
case SENML_UNIT_SIEMENS_PER_METER: return "S/m";
case SENML_UNIT_BYTE: return "B";
case SENML_UNIT_VOLT_AMPERE: return "VA";
case SENML_UNIT_VOLT_AMPERE_SECOND: return "VAs";
case SENML_UNIT_VOLT_AMPERE_REACTIVE: return "var";
case SENML_UNIT_VOLT_AMPERE_REACTIVE_SECOND: return "vars";
case SENML_UNIT_JOULE_PER_METER: return "J/m";
case SENML_UNIT_KILOGRAM_PER_CUBIC_METER: return "kg/m3";
case SENML_UNIT_DEGREE: return "deg";
case SENML_UNIT_NEPHELOMETRIC_TURBIDITY_UNIT: return "NTU";
case SENML_UNIT_MILLISECOND: return "ms";
case SENML_UNIT_MINUTE: return "min";
case SENML_UNIT_HOUR: return "h";
case SENML_UNIT_MEGAHERTZ: return "MHz";
case SENML_UNIT_KILOWATT: return "kW";
case SENML_UNIT_KILOVOLT_AMPERE: return "kVA";
case SENML_UNIT_KILOVAR: return "kvar";
case SENML_UNIT_AMPERE_HOUR: return "Ah";
case SENML_UNIT_WATT_HOUR: return "Wh";
case SENML_UNIT_KILOWATT_HOUR: return "kWh";
case SENML_UNIT_VAR_HOUR: return "varh";
case SENML_UNIT_KILOVAR_HOUR: return "kvarh";
case SENML_UNIT_KILOVOLT_AMPERE_HOUR: return "kVAh";
case SENML_UNIT_WATT_HOUR_PER_KILOMETER: return "Wh/km";
case SENML_UNIT_KIBIBYTE: return "KiB";
case SENML_UNIT_GIGABYTE: return "GB";
case SENML_UNIT_MEGABIT_PER_SECOND: return "MBit/s";
case SENML_UNIT_BYTE_PER_SECOND: return "B/s";
case SENML_UNIT_MEGABYTE_PER_SECOND: return "MB/s";
case SENML_UNIT_MILLIVOLT: return "mV";
case SENML_UNIT_MILLIAMPERE: return "mA";
case SENML_UNIT_DECIBEL_MILLIWATT: return "dBm";
case SENML_UNIT_MICROGRAM_PER_CUBIC_METER: return "ug/m3";
case SENML_UNIT_MILLIMETER_PER_HOUR: return "mm/h";
case SENML_UNIT_METER_PER_HOUR: return "m/h";
case SENML_UNIT_PARTS_PER_MILLION: return "ppm";
case SENML_UNIT_PERCENT: return "/100";
case SENML_UNIT_PERMILLE: return "/1000";
case SENML_UNIT_HECTOPASCAL: return "hPa";
case SENML_UNIT_MILLIMETER: return "mm";
case SENML_UNIT_CENTIMETER: return "cm";
case SENML_UNIT_KILOMETER: return "km";
case SENML_UNIT_KILOMETER_PER_HOUR: return "km/h";
case SENML_UNIT_PARTS_PER_BILLION: return "ppb";
case SENML_UNIT_PARTS_PER_TRILLION: return "ppt";
case SENML_UNIT_VOLT_AMPERE_HOUR: return "VAh";
case SENML_UNIT_MILLIGRAM_PER_LITER: return "mg/l";
case SENML_UNIT_MICROGRAM_PER_LITER: return "ug/l";
case SENML_UNIT_GRAM_PER_LITER: return "g/l";
default: return "";
}
}

16
tests/senml_cbor/Makefile Normal file
View File

@ -0,0 +1,16 @@
include ../Makefile.tests_common
USEMODULE += senml_cbor
USEMODULE += fmt
USEMODULE += embunit
CFLAGS += -DCONFIG_SENML_ATTR_SUM
CFLAGS += -DCONFIG_SENML_ATTR_VERSION
CFLAGS += -DCONFIG_SENML_ATTR_UPDATE_TIME
CFLAGS += -DTHREAD_STACKSIZE_DEFAULT=1536
# The following BOARDs redefine THREAD_STACKSIZE_DEFAULT
BOARD_BLACKLIST += nucleo-l011k4 stk3200
include $(RIOTBASE)/Makefile.include

View File

@ -0,0 +1,10 @@
BOARD_INSUFFICIENT_MEMORY := \
arduino-duemilanove \
arduino-leonardo \
arduino-nano \
arduino-uno \
atmega328p \
atmega328p-xplained-mini \
nucleo-f031k6 \
nucleo-l011k4 \
#

View File

@ -0,0 +1,3 @@
CONFIG_MODULE_SENML_CBOR=y
CONFIG_MODULE_FMT=y
CONFIG_MODULE_EMBUNIT=y

126
tests/senml_cbor/main.c Normal file
View File

@ -0,0 +1,126 @@
/*
* Copyright (C) 2021 Silke Hofstra
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup tests
* @{
*
* @file
* @brief Short SenML CBOR test
*
* @author Silke Hofstra <silke@slxh.eu>
*
* @}
*/
#include <stdio.h>
#include <string.h>
#include "embUnit.h"
#include "senml/cbor.h"
#include "fmt.h"
#define BUF_SIZE (128)
static uint8_t cbor_buf[BUF_SIZE];
static char result[2 * BUF_SIZE];
static char expect[] = "89A5216943424F522074657374221A608404D001616D07187802F953B0A206"
"0102F953B0A2060202183DA2060302183DA2060402C48220190267A2060504"
"F5A20606036752494F54204F53A20607084400010203A201626B6705183D";
void test_senml_encode(void)
{
nanocbor_encoder_t enc;
/* Some common attributes to set on the first element */
senml_attr_t attr = {
.base_name = "CBOR test",
.base_time = senml_duration_s(1619264720),
.update_time = senml_duration_s(120),
.unit = SENML_UNIT_METER,
};
/* A numeric (float) value */
senml_value_t vf = { .attr = attr, .value = senml_float(61.5) };
/* A numeric (double) value */
senml_value_t vd = { .attr = { .time = senml_duration_s(1) },
.value = senml_double(61.5) };
/* A numeric (int) value */
senml_value_t vi = { .attr = { .time = senml_duration_s(2) },
.value = senml_int(61) };
/* A numeric (uint) value */
senml_value_t vu = { .attr = { .time = senml_duration_s(3) },
.value = senml_uint(61) };
/* A numeric (decimal fraction) value */
senml_value_t vdf = { .attr = { .time = senml_duration_s(4) },
.value = senml_decfrac(615, -1) };
/* A boolean value */
senml_bool_value_t vb = { .attr = { .time = senml_duration_s(5) },
.value = true };
/* A string value */
char string[] = "RIOT OS";
senml_string_value_t vs = { .attr = { .time = senml_duration_s(6) },
.value = string, .len = sizeof string - 1 };
/* A data value */
uint8_t data[] = { 0x00, 0x01, 0x02, 0x03 };
senml_data_value_t vdat = { .attr = { .time = senml_duration_s(7) },
.value = data, .len = sizeof data };
/* A numeric (float) sum value */
senml_attr_t sum = {
.sum = senml_int(61),
.unit = SENML_UNIT_KILOGRAM,
};
/* Initialize encoder, and start array */
nanocbor_encoder_init(&enc, cbor_buf, sizeof cbor_buf);
nanocbor_fmt_array(&enc, 9);
/* Encode the values */
senml_encode_value_cbor(&enc, &vf);
senml_encode_value_cbor(&enc, &vd);
senml_encode_value_cbor(&enc, &vi);
senml_encode_value_cbor(&enc, &vu);
senml_encode_value_cbor(&enc, &vdf);
senml_encode_bool_cbor(&enc, &vb);
senml_encode_string_cbor(&enc, &vs);
senml_encode_data_cbor(&enc, &vdat);
senml_encode_sum_cbor(&enc, &sum);
size_t len = nanocbor_encoded_len(&enc);
fmt_bytes_hex(result, cbor_buf, len);
/* Compare hex result */
TEST_ASSERT_EQUAL_INT(2 * len, sizeof expect - 1);
TEST_ASSERT_EQUAL_INT(0, strncmp(expect, result, len));
}
Test *tests_senml(void)
{
EMB_UNIT_TESTFIXTURES(fixtures) {
new_TestFixture(test_senml_encode),
};
EMB_UNIT_TESTCALLER(senml_tests, NULL, NULL, fixtures);
return (Test *)&senml_tests;
}
int main(void)
{
TESTS_START();
TESTS_RUN(tests_senml());
TESTS_END();
return 0;
}

View File

@ -0,0 +1,14 @@
#!/usr/bin/env python3
# Copyright (C) 2017 Freie Universität Berlin
#
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.
import sys
from testrunner import run_check_unittests
if __name__ == "__main__":
sys.exit(run_check_unittests())

View File

@ -0,0 +1,7 @@
include ../Makefile.tests_common
USEMODULE += senml_phydat
USEMODULE += embunit
USEMODULE += printf_float
include $(RIOTBASE)/Makefile.include

View File

@ -0,0 +1,16 @@
BOARD_INSUFFICIENT_MEMORY := \
arduino-duemilanove \
arduino-leonardo \
arduino-nano \
arduino-uno \
atmega328p \
atmega328p-xplained-mini \
nucleo-f031k6 \
nucleo-f042k6 \
nucleo-l011k4 \
nucleo-l031k6 \
samd10-xmini \
stk3200 \
stm32f030f4-demo \
stm32g0316-disco \
#

View File

@ -0,0 +1,3 @@
CONFIG_MODULE_SENML_PHYDAT=y
CONFIG_MODULE_EMBUNIT=y
CONFIG_MODULE_PRINTF_FLOAT=y

174
tests/senml_phydat/main.c Normal file
View File

@ -0,0 +1,174 @@
/*
* Copyright (C) 2021 Silke Hofstra
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup tests
* @{
*
* @file
* @brief SenML Phydat tests
*
* @author Silke Hofstra <silke@slxh.eu>
*
* @}
*/
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "embUnit.h"
#include "senml/phydat.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
#ifndef PRIi64
#define PRIi64 "lli"
#endif
typedef struct {
phydat_t phydat;
senml_value_t senml;
} time_test_t;
typedef struct {
phydat_t phydat;
senml_value_t senml1;
senml_value_t senml2;
uint8_t dim;
} value_test_t;
#define senml_s(s) { .attr = { .unit = SENML_UNIT_SECOND }, \
.value = { SENML_TYPE_NUMERIC_INT, { .i = s } } }
#define senml_f(v, u) { .attr = { .unit = u }, \
.value = { SENML_TYPE_NUMERIC_FLOAT, { .f = v } } }
#define senml_df(m, e, u) { .attr = { .unit = u }, \
.value = { SENML_TYPE_NUMERIC_DECFRAC, { .df = { e, m } } } }
static value_test_t value_tests[] = {
{
.phydat = { { 360, 0, 0 }, UNIT_M, 6 },
.senml1 = senml_f(360e6, SENML_UNIT_METER),
.senml2 = senml_df(360, 6, SENML_UNIT_METER),
.dim = 0
},
{
.phydat = { { 864, 0, 0 }, UNIT_TIME, 2 },
.senml1 = senml_f(86400, SENML_UNIT_SECOND),
.senml2 = senml_df(864, 2, SENML_UNIT_SECOND),
.dim = 0
},
{
.phydat = { { 0, 144, 0 }, UNIT_TIME, 1 },
.senml1 = senml_f(1440, SENML_UNIT_MINUTE),
.senml2 = senml_df(144, 1, SENML_UNIT_MINUTE),
.dim = 1
},
{
.phydat = { { 0, 0, 24 }, UNIT_TIME, 0 },
.senml1 = senml_f(24, SENML_UNIT_HOUR),
.senml2 = senml_df(24, 0, SENML_UNIT_HOUR),
.dim = 2
},
{
.phydat = { { 0, 0, 0 }, UNIT_TEMP_F, 3 },
.senml1 = senml_f(255.37, SENML_UNIT_KELVIN),
.senml2 = senml_df(0, 3, SENML_UNIT_NONE),
},
{
.phydat = { { 314, 0, 0 }, UNIT_G, -2 },
.senml1 = senml_f(30.792881, SENML_UNIT_METER_PER_SQUARE_SECOND),
.senml2 = senml_df(314, -2, SENML_UNIT_NONE),
},
{
.phydat = { { 988, 0, 0 }, UNIT_BAR, -3 },
.senml1 = senml_f(98.8e3, SENML_UNIT_PASCAL),
.senml2 = senml_df(988, 2, SENML_UNIT_PASCAL),
},
{
.phydat = { { 193, 0, 0 }, UNIT_GPM3, 5 },
.senml1 = senml_f(19.3e3, SENML_UNIT_KILOGRAM_PER_CUBIC_METER),
.senml2 = senml_df(193, 2, SENML_UNIT_KILOGRAM_PER_CUBIC_METER),
},
{
.phydat = { { 220, 0, 0 }, UNIT_GS, 3 },
.senml1 = senml_f(22, SENML_UNIT_TESLA),
.senml2 = senml_df(220, -1, SENML_UNIT_TESLA),
}
};
void test_phydat_to_senml_float(void)
{
senml_value_t res;
for (size_t i = 0; i < ARRAY_SIZE(value_tests); i++) {
senml_value_t *exp = &(value_tests[i].senml1);
phydat_to_senml_float(&res, &(value_tests[i].phydat), value_tests[i].dim);
DEBUG("Float: %" PRIi16 "e%" PRIi16 " %s -> %.f %s\n",
value_tests[i].phydat.val[value_tests[i].dim], value_tests[i].phydat.scale,
phydat_unit_to_str(value_tests[i].phydat.unit),
res.value.value.f,
senml_unit_to_str(res.attr.unit));
TEST_ASSERT_EQUAL_STRING(senml_unit_to_str(exp->attr.unit),
senml_unit_to_str(res.attr.unit));
TEST_ASSERT_EQUAL_INT(exp->value.type, res.value.type);
TEST_ASSERT_EQUAL_INT((int)roundf(exp->value.value.f * 100),
(int)roundf(res.value.value.f * 100));
}
}
void test_phydat_to_senml_decimal(void)
{
senml_value_t res;
for (size_t i = 0; i < ARRAY_SIZE(value_tests); i++) {
senml_value_t *exp = &(value_tests[i].senml2);
phydat_to_senml_decimal(&res, &(value_tests[i].phydat), value_tests[i].dim);
DEBUG("Decimal: %" PRIi16 "e%" PRIi16 " %s -> %" PRIi32 "e%" PRIi32 " %s\n",
value_tests[i].phydat.val[value_tests[i].dim], value_tests[i].phydat.scale,
phydat_unit_to_str(value_tests[i].phydat.unit),
res.value.value.df.m, res.value.value.df.e,
senml_unit_to_str(res.attr.unit));
TEST_ASSERT_EQUAL_STRING(senml_unit_to_str(exp->attr.unit),
senml_unit_to_str(res.attr.unit));
TEST_ASSERT_EQUAL_INT(exp->value.type, res.value.type);
TEST_ASSERT_EQUAL_INT(exp->value.value.df.m, res.value.value.df.m);
TEST_ASSERT_EQUAL_INT(exp->value.value.df.e, res.value.value.df.e);
}
}
Test *tests_senml(void)
{
EMB_UNIT_TESTFIXTURES(fixtures) {
/* Don't run this test on CPUs with unpredictable rounding */
#if !defined(__AVR__) && !defined(CPU_MSP430FXYZ)
new_TestFixture(test_phydat_to_senml_float),
#endif
new_TestFixture(test_phydat_to_senml_decimal),
};
EMB_UNIT_TESTCALLER(senml_tests, NULL, NULL, fixtures);
return (Test *)&senml_tests;
}
int main(void)
{
TESTS_START();
TESTS_RUN(tests_senml());
TESTS_END();
return 0;
}

View File

@ -0,0 +1,14 @@
#!/usr/bin/env python3
# Copyright (C) 2017 Freie Universität Berlin
#
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.
import sys
from testrunner import run_check_unittests
if __name__ == "__main__":
sys.exit(run_check_unittests())

View File

@ -0,0 +1,8 @@
include ../Makefile.tests_common
USEMODULE += saul_default
USEMODULE += senml_saul
USEMODULE += fmt
USEMODULE += embunit
include $(RIOTBASE)/Makefile.include

View File

@ -0,0 +1,12 @@
BOARD_INSUFFICIENT_MEMORY := \
arduino-duemilanove \
arduino-leonardo \
arduino-nano \
arduino-uno \
atmega328p \
atmega328p-xplained-mini \
nucleo-f031k6 \
nucleo-l011k4 \
samd10-xmini \
stm32f030f4-demo \
#

61
tests/senml_saul/main.c Normal file
View File

@ -0,0 +1,61 @@
/*
* Copyright (C) 2021 Silke Hofstra
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup tests
* @{
*
* @file
* @brief Short SenML SAUL test
*
* @author Silke Hofstra <silke@slxh.eu>
*
* @}
*/
#include <stdio.h>
#include <string.h>
#include "embUnit.h"
#include "senml/saul.h"
#include "fmt.h"
#define BUF_SIZE (128)
static uint8_t cbor_buf[BUF_SIZE];
static char result[2 * BUF_SIZE];
static char expect[] = "9FFF";
void test_senml_encode(void)
{
size_t len = senml_saul_encode_cbor(cbor_buf, sizeof cbor_buf, saul_reg);
/* Encode as hex */
fmt_bytes_hex(result, cbor_buf, len);
/* Compare hex result */
TEST_ASSERT_EQUAL_INT(2 * len, sizeof expect - 1);
TEST_ASSERT_EQUAL_INT(0, strncmp(expect, result, len));
}
Test *tests_senml(void)
{
EMB_UNIT_TESTFIXTURES(fixtures) {
new_TestFixture(test_senml_encode),
};
EMB_UNIT_TESTCALLER(senml_tests, NULL, NULL, fixtures);
return (Test *)&senml_tests;
}
int main(void)
{
TESTS_START();
TESTS_RUN(tests_senml());
TESTS_END();
return 0;
}

View File

@ -0,0 +1,14 @@
#!/usr/bin/env python3
# Copyright (C) 2017 Freie Universität Berlin
#
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.
import sys
from testrunner import run_check_unittests
if __name__ == "__main__":
sys.exit(run_check_unittests())