The KPN SenML library helps you create and parse senml documents in both json and cbor format. The library can be used for sending sensor data and receiving actuator commands.

Files at this revision

API Documentation at this revision

Comitter:
kpniot
Date:
Sat May 19 17:35:20 2018 +0000
Commit message:
first commit

Changed in this revision

LICENSE.txt Show annotated file Show diff for this revision Revisions of this file
README.md Show annotated file Show diff for this revision Revisions of this file
cbor.cpp Show annotated file Show diff for this revision Revisions of this file
cbor.h Show annotated file Show diff for this revision Revisions of this file
footer.md Show annotated file Show diff for this revision Revisions of this file
index.md Show annotated file Show diff for this revision Revisions of this file
kpn_senml.h Show annotated file Show diff for this revision Revisions of this file
senml_JsonListener.h Show annotated file Show diff for this revision Revisions of this file
senml_JsonStreamingParser.cpp Show annotated file Show diff for this revision Revisions of this file
senml_JsonStreamingParser.h Show annotated file Show diff for this revision Revisions of this file
senml_base.cpp Show annotated file Show diff for this revision Revisions of this file
senml_base.h Show annotated file Show diff for this revision Revisions of this file
senml_base_parser.cpp Show annotated file Show diff for this revision Revisions of this file
senml_base_parser.h Show annotated file Show diff for this revision Revisions of this file
senml_binary_actuator.cpp Show annotated file Show diff for this revision Revisions of this file
senml_binary_actuator.h Show annotated file Show diff for this revision Revisions of this file
senml_binary_record.cpp Show annotated file Show diff for this revision Revisions of this file
senml_binary_record.h Show annotated file Show diff for this revision Revisions of this file
senml_bool_actuator.cpp Show annotated file Show diff for this revision Revisions of this file
senml_bool_actuator.h Show annotated file Show diff for this revision Revisions of this file
senml_bool_record.cpp Show annotated file Show diff for this revision Revisions of this file
senml_bool_record.h Show annotated file Show diff for this revision Revisions of this file
senml_cbor_parser.cpp Show annotated file Show diff for this revision Revisions of this file
senml_cbor_parser.h Show annotated file Show diff for this revision Revisions of this file
senml_double_pack.cpp Show annotated file Show diff for this revision Revisions of this file
senml_double_pack.h Show annotated file Show diff for this revision Revisions of this file
senml_enums.cpp Show annotated file Show diff for this revision Revisions of this file
senml_enums.h Show annotated file Show diff for this revision Revisions of this file
senml_float_actuator.cpp Show annotated file Show diff for this revision Revisions of this file
senml_float_actuator.h Show annotated file Show diff for this revision Revisions of this file
senml_float_record.cpp Show annotated file Show diff for this revision Revisions of this file
senml_float_record.h Show annotated file Show diff for this revision Revisions of this file
senml_helpers.cpp Show annotated file Show diff for this revision Revisions of this file
senml_helpers.h Show annotated file Show diff for this revision Revisions of this file
senml_int_actuator.cpp Show annotated file Show diff for this revision Revisions of this file
senml_int_actuator.h Show annotated file Show diff for this revision Revisions of this file
senml_int_pack.cpp Show annotated file Show diff for this revision Revisions of this file
senml_int_pack.h Show annotated file Show diff for this revision Revisions of this file
senml_int_record.cpp Show annotated file Show diff for this revision Revisions of this file
senml_int_record.h Show annotated file Show diff for this revision Revisions of this file
senml_json_parser.cpp Show annotated file Show diff for this revision Revisions of this file
senml_json_parser.h Show annotated file Show diff for this revision Revisions of this file
senml_logging.cpp Show annotated file Show diff for this revision Revisions of this file
senml_logging.h Show annotated file Show diff for this revision Revisions of this file
senml_pack.cpp Show annotated file Show diff for this revision Revisions of this file
senml_pack.h Show annotated file Show diff for this revision Revisions of this file
senml_pack_t.h Show annotated file Show diff for this revision Revisions of this file
senml_record.cpp Show annotated file Show diff for this revision Revisions of this file
senml_record.h Show annotated file Show diff for this revision Revisions of this file
senml_record_t.h Show annotated file Show diff for this revision Revisions of this file
senml_string_actuator.cpp Show annotated file Show diff for this revision Revisions of this file
senml_string_actuator.h Show annotated file Show diff for this revision Revisions of this file
senml_string_record.cpp Show annotated file Show diff for this revision Revisions of this file
senml_string_record.h Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r a9259748d982 LICENSE.txt
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LICENSE.txt	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,22 @@
+MIT License
+
+Copyright (c) 2018 KPN IoT
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff -r 000000000000 -r a9259748d982 README.md
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README.md	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,17 @@
+# Introduction
+
+The KPN SenML library helps you create and parse [senml documents](https://tools.ietf.org/html/draft-ietf-core-senml-13)
+in both json and cbor format.
+
+# key features
+
+- Object oriented design.
+- built in support for [senml's unit registry](https://tools.ietf.org/html/draft-ietf-core-senml-12#section-12.1)
+- extensible for new data types
+- makes use of (but doesn't restrict to) KPN's predefined list of record names.
+- direct support to read/write in json and cbor format.
+- automatically adjusts record data with respect to base time, base value & base sum.
+
+Please visit our [docs site](https://kpn-iot.github.io/senml-c-library) for more info.
+
+
diff -r 000000000000 -r a9259748d982 cbor.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cbor.cpp	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,383 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * cbor parsing and rendering
+ */
+
+
+#include <cbor.h>
+#include <senml_helpers.h>
+
+
+#ifndef htons
+#define htons(x) ( ((x)<<8 & 0xFF00) | ((x)>>8 & 0x00FF) )
+#endif // !htons
+
+#ifndef  htonl
+#define htonl(x) ( ((x)<<24 & 0xFF000000UL) | \
+                   ((x)<< 8 & 0x00FF0000UL) | \
+                   ((x)>> 8 & 0x0000FF00UL) | \
+                   ((x)>>24 & 0x000000FFUL) )
+#endif // ! htonl
+
+#ifndef  htonll
+#define htonll(x) ((((uint64_t)htonl(x)) << 32) + htonl((x) >> 32))
+#endif // ! htonll
+
+
+#ifndef  ntohl
+#define ntohl(x) ( ((x) & 0xFF000000UL) >> 24 | \
+                   ((x) & 0x00FF0000UL) >> 8 | \
+                   ((x) & 0x0000FF00UL) << 8 | \
+                   ((x) & 0x000000FFUL) << 24 )
+#endif // ! ntohl
+
+
+
+/**
+ * Convert float @p x to host format
+ */
+inline float ntohf(uint32_t x)
+{
+    union u {
+        float f;
+        uint32_t i;
+    } u = { .i = ntohl(x) };
+    return u.f;
+}
+
+/**
+ * Convert double @p x to network format
+ */
+static uint64_t htond(double x)
+{
+    union u {
+        double d;
+        uint64_t i;
+    } u = { .d = x };
+    return htonll(u.i);
+}
+
+/**
+ * Convert double @p x to host format
+ */
+inline double ntohd(uint64_t x)
+{
+    union u {
+        double d;
+        uint64_t i;
+    } u = { .i = htonll(x) };
+    return u.d;
+}
+
+inline float ntohd_avr(uint64_t x)
+{
+    union u {
+        unsigned char dn[8];
+        uint64_t i;
+    } u = { .i = htonll(x) };
+
+
+   union {
+       float f;
+       unsigned char b[4];
+       uint32_t ui;
+   } fn;    
+   int expd = ((u.dn[7] & 127) << 4) + ((u.dn[6] & 240) >> 4);
+   int expf = expd ? (expd - 1024) + 128 : 0;
+   
+   fn.b[3] = (u.dn[7] & 128) + (expf >> 1);
+   fn.b[2] = ((expf & 1) << 7) + ((u.dn[6] & 15) << 3) + ((u.dn[5] & 0xe0) >> 5);
+   fn.b[1] = ((u.dn[5] & 0x1f) << 3) + ((u.dn[4] & 0xe0) >> 5);
+   fn.b[0] = ((u.dn[4] & 0x1f) << 3) + ((u.dn[3] & 0xe0) >> 5);
+  return fn.f;
+}
+
+/**
+ * Source: CBOR RFC reference implementation
+ */
+double decode_float_half(unsigned char *halfp)
+{
+    int half = (halfp[0] << 8) + halfp[1];
+    int exp = (half >> 10) & 0x1f;
+    int mant = half & 0x3ff;
+    double val;
+
+	#ifdef __MBED__
+	if (exp == 0) {
+        val = ldexp((double)mant, -24);
+    }
+    else if (exp != 31) {
+        val = ldexp((double)(mant + 1024), exp - 25);
+    }
+	#else
+    if (exp == 0) {
+        val = ldexp(mant, -24);
+    }
+    else if (exp != 31) {
+        val = ldexp(mant + 1024, exp - 25);
+    }
+	#endif
+    else {
+        val = mant == 0 ? INFINITY : NAN;
+    }
+
+    return (half & 0x8000) ? -val : val;
+}
+
+/**
+ * Return additional info field value for input value @p val
+ *
+ * @return Byte with the additional info bits set
+ */
+static unsigned char uint_additional_info(uint64_t val)
+{
+    if (val < CBOR_UINT8_FOLLOWS) {
+        return val;
+    }
+    else if (val <= 0xff) {
+        return CBOR_UINT8_FOLLOWS;
+    }
+    else if (val <= 0xffff) {
+        return CBOR_UINT16_FOLLOWS;
+    }
+    else if (val <= 0xffffffffL) {
+        return CBOR_UINT32_FOLLOWS;
+    }
+
+    return CBOR_UINT64_FOLLOWS;
+}
+
+/**
+ * Return the number of bytes that would follow the additional info field @p additional_info
+ *
+ * @param additional_info Must be in the range [CBOR_UINT8_FOLLOWS, CBOR_UINT64_FOLLOWS]
+ */
+static unsigned char uint_bytes_follow(unsigned char additional_info)
+{
+    if (additional_info < CBOR_UINT8_FOLLOWS || additional_info > CBOR_UINT64_FOLLOWS) {
+        return 0;
+    }
+
+    const unsigned char BYTES_FOLLOW[] = {1, 2, 4, 8};
+    return BYTES_FOLLOW[additional_info - CBOR_UINT8_FOLLOWS];
+}
+
+static size_t encode_int(unsigned char major_type, uint64_t val)
+{
+    unsigned char additional_info = uint_additional_info(val);
+    unsigned char bytes_follow = uint_bytes_follow(additional_info);
+    unsigned char value = (major_type | additional_info);
+    printText( (const char*)&value, 1);
+
+    for (int i = bytes_follow - 1; i >= 0; --i) {
+        value = (val >> (8 * i)) & 0xff;
+        printText((const char*)&value, 1);
+    }
+
+    return bytes_follow + 1;
+}
+
+size_t decode_int(uint64_t *val)
+{
+
+    *val = 0; /* clear val first */
+
+    unsigned char in = readChar();
+    unsigned char bytes_follow = uint_bytes_follow(in);
+
+    switch (bytes_follow) {
+        case 0:
+            *val = (in & CBOR_INFO_MASK);
+            break;
+
+        case 1:
+            *val = readChar();
+            break;
+
+        case 2:
+            uint16_t data;
+            readChars((unsigned char*)&data, 2);
+            *val = htons(data);
+            break;
+
+        case 4:
+            uint32_t data32;
+            readChars((unsigned char*)&data32, 4);
+            *val = htonl(data32);
+            break;
+
+        default:
+            uint64_t data64;
+            readChars((unsigned char*)&data64, 8);
+            *val = htonll(data64);
+            break;
+    }
+
+    return bytes_follow + 1;
+}
+
+static size_t encode_bytes(unsigned char major_type, const char *data, size_t length)
+{
+    uint_bytes_follow(uint_additional_info(length)) + 1;
+    size_t bytes_start = encode_int(major_type, (uint64_t) length);
+
+    if (!bytes_start) {
+        return 0;
+    }
+
+    printText(data, length);
+    return (bytes_start + length);
+}
+
+size_t cbor_serialize_array(size_t array_length)
+{
+    /* serialize number of array items */
+    return encode_int(CBOR_ARRAY, array_length);
+}
+
+size_t cbor_serialize_map(size_t map_length)
+{
+    /* serialize number of item key-value pairs */
+    return encode_int(CBOR_MAP, map_length);
+}
+
+size_t cbor_serialize_int(int val)
+{
+    if (val >= 0) {
+        /* Major type 0: an unsigned integer */
+        return encode_int(CBOR_UINT, val);
+    }
+    else {
+        /* Major type 1: an negative integer */
+        return encode_int(CBOR_NEGINT, -1 - val);
+    }
+}
+
+
+size_t cbor_serialize_unicode_string(const char *val)
+{
+    return encode_bytes(CBOR_TEXT, val, strlen(val));
+}
+
+size_t cbor_serialize_double(double val)
+{
+    unsigned char value = CBOR_FLOAT64;
+    printText((const char*)&value, 1);
+    uint64_t encoded_val = htond(val);
+    printText( (const char*)&encoded_val, 8);
+    return 9;
+}
+
+size_t cbor_deserialize_float_half(float *val)
+{
+    if (CBOR_TYPE != CBOR_7 || !val) {
+        return 0;
+    }
+
+    unsigned char dataType = readChar();
+    if (dataType == CBOR_FLOAT16) {
+        uint16_t data;
+        readChars((unsigned char*)&data, 2);
+        *val = (float)decode_float_half((unsigned char*)&data);
+        return 3;
+    }
+
+    return 0;
+}
+
+size_t cbor_deserialize_float(float *val)
+{
+    if (CBOR_TYPE != CBOR_7 || !val) {
+        return 0;
+    }
+
+    unsigned char dataType = readChar();
+
+    if (dataType == CBOR_FLOAT32) {
+        uint32_t data;
+        readChars((unsigned char*)&data, 4);
+        *val = ntohf(data);
+        return 5;
+    }
+    return 0;
+}
+
+
+size_t cbor_deserialize_double(double *val)
+{
+    if (CBOR_TYPE != CBOR_7 || !val) {
+        return 0;
+    }
+
+    unsigned char dataType = readChar();
+
+    if (dataType == CBOR_FLOAT64) {
+        uint64_t data;
+        readChars((unsigned char*)&data, 8);
+        if(sizeof(double) == 8)                                 //this is the default on most systems
+        {
+            *val = ntohd(data);
+        }
+        else if(sizeof(double) == 4)                            //8 bit processors such as avr don't support 8 byte floating point values, only 4 bytes, so need special conversion
+        {
+            *val = ntohd_avr(data);
+        }
+        return 9;
+    }
+
+    return 0;
+}
+
+size_t cbor_deserialize_int64_t(int64_t *val)
+{
+    unsigned char type = CBOR_TYPE;
+    if ((type != CBOR_UINT && type != CBOR_NEGINT) || !val) {
+        return 0;
+    }
+
+    uint64_t buf;
+    size_t read_bytes = decode_int(&buf);
+
+    if (type == CBOR_UINT) 
+        *val = buf;                         /* resolve as CBOR_UINT */
+    else 
+        *val = -1 - buf;                    /* resolve as CBOR_NEGINT */
+    return read_bytes;
+}
+
+size_t cbor_deserialize_uint64_t(uint64_t *val)
+{
+    if (CBOR_TYPE != CBOR_UINT || !val) {
+        return 0;
+    }
+    return decode_int(val);
+}
+
+size_t cbor_serialize_bool(bool val)
+{
+    unsigned char value = (val ? CBOR_TRUE : CBOR_FALSE);
+    printText((const char*)&value , 1);
+    return 1;
+}
+
+size_t cbor_serialize_byte_string(const char *val, int length)
+{
+    return encode_bytes(CBOR_BYTES, val, length);
+}
+
+
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 cbor.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cbor.h	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,209 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * cbor parsing and rendering header
+ */
+
+
+#ifndef SENMLCBOR
+#define SENMLCBOR
+
+#include <Stream.h>
+#include <stddef.h>
+
+#define SENML_BVER_LABEL    -1
+#define SENML_CBOR_BN_LABEL -2
+#define SENML_CBOR_BT_LABEL -3
+#define SENML_CBOR_BU_LABEL -4
+#define SENML_CBOR_BV_LABEL -5
+#define SENML_CBOR_BS_LABEL -16
+#define SENML_CBOR_N_LABEL   0
+#define SENML_CBOR_U_LABEL   1
+#define SENML_CBOR_V_LABEL   2
+#define SENML_CBOR_VS_LABEL  3
+#define SENML_CBOR_VB_LABEL  4
+#define SENML_CBOR_S_LABEL   5
+#define SENML_CBOR_T_LABEL   6
+#define SENML_CBOR_UT_LABEL  7
+#define SENML_CBOR_VD_LABEL  8
+
+#define CBOR_TYPE_MASK          0xE0    /* top 3 bits */
+#define CBOR_INFO_MASK          0x1F    /* low 5 bits */
+
+/* Major types (cf. section 2.1) */
+/* Major type 0: Unsigned integers */
+#define CBOR_UINT8_FOLLOWS      24      /* 0x18 */
+#define CBOR_UINT16_FOLLOWS     25      /* 0x19 */
+#define CBOR_UINT32_FOLLOWS     26      /* 0x1a */
+#define CBOR_UINT64_FOLLOWS     27      /* 0x1b */
+
+#define CBOR_BYTE_FOLLOWS       24      /* indicator that the next byte is part of this item */
+
+/* Jump Table for Initial Byte (cf. table 5) */
+#define CBOR_UINT       0x00            /* type 0 */
+#define CBOR_NEGINT     0x20            /* type 1 */
+#define CBOR_BYTES      0x40            /* type 2 */
+#define CBOR_TEXT       0x60            /* type 3 */
+#define CBOR_ARRAY      0x80            /* type 4 */
+#define CBOR_MAP        0xA0            /* type 5 */
+#define CBOR_TAG        0xC0            /* type 6 */
+#define CBOR_7          0xE0            /* type 7 (float and other types) */
+
+#define CBOR_VAR_FOLLOWS        31      /* 0x1f */
+
+/* Major type 6: Semantic tagging */
+#define CBOR_DATETIME_STRING_FOLLOWS        0
+#define CBOR_DATETIME_EPOCH_FOLLOWS         1
+
+/* Major type 7: Float and other types */
+#define CBOR_FALSE      (CBOR_7 | 20)
+#define CBOR_TRUE       (CBOR_7 | 21)
+#define CBOR_NULL       (CBOR_7 | 22)
+#define CBOR_UNDEFINED  (CBOR_7 | 23)
+/* CBOR_BYTE_FOLLOWS == 24 */
+#define CBOR_FLOAT16    (CBOR_7 | 25)
+#define CBOR_FLOAT32    (CBOR_7 | 26)
+#define CBOR_FLOAT64    (CBOR_7 | 27)
+#define CBOR_BREAK      (CBOR_7 | 31)
+
+#define CBOR_TYPE (peekChar() & CBOR_TYPE_MASK)
+
+
+/**
+ * @brief Serialize array of length @p array_length
+ *
+ * Basic usage:
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
+ * cbor_serialize_array(2); // array of length 2 follows
+ * cbor_serialize_int(1)); // write item 1
+ * cbor_serialize_int(2)); // write item 2
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * @note You have to make sure to serialize the correct amount of items.
+ * If you exceed the length @p array_length, items will just be appened as normal
+ *
+ * @param[in]  array_length Length of the array of items which follows
+ *
+ * @return Number of bytes written to stream @p stream
+ */
+size_t cbor_serialize_array(size_t array_length);
+
+
+/**
+ * @brief Serialize map of length @p map_length
+ *
+ * Basic usage:
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
+ * cbor_serialize_map(2); // map of length 2 follows
+ * cbor_serialize_int(1)); // write key 1
+ * cbor_serialize_byte_string("1")); // write value 1
+ * cbor_serialize_int(2)); // write key 2
+ * cbor_serialize_byte_string("2")); // write value 2
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * @param map_length Length of the map of items which follows
+ *
+ * @return Number of bytes written to stream @p stream
+ */
+size_t cbor_serialize_map(size_t map_length);
+
+
+/**
+ * @brief Serializes an integer
+ *
+ * @param[in] val       The integer to serialize
+ *
+ * @return Number of bytes written to stream @p stream
+ */
+size_t cbor_serialize_int(int val);
+
+
+/**
+ * @brief Serializes a unicode string.
+ *
+ *                      string
+ * @param[out] val      The zero-terminated unicode string to serialize.
+ *
+ * @return Number of bytes written to stream @p stream
+ */
+size_t cbor_serialize_unicode_string(const char *val);
+
+
+/**
+ * @brief Serializes a double precision floating point value
+ *
+ * @param[in] val       The double to serialize
+ *
+ * @return Number of bytes written to stream @p stream
+ */
+size_t cbor_serialize_double(double val);
+
+
+/**
+ * @brief Serializes a boolean value
+ *
+ * @param[in] val       The boolean value to serialize
+ *
+ * @return Number of bytes written to stream @p stream
+ */
+size_t cbor_serialize_bool(bool val);
+
+/**
+ * @brief Serializes a signed 64 bit value
+ *
+ * @param[in] val       The 64 bit integer to serialize
+ *
+ * @return Number of bytes written to stream @p stream
+ */
+size_t cbor_serialize_byte_string(const char *val, int length);
+
+
+//read integer
+size_t decode_int(uint64_t *val);
+
+/**
+ * @brief Deserialize signed 64 bit values from stream to @p val
+ *
+ * @param[out] val   Pointer to destination array
+ *
+ * @return Number of bytes read from @p stream
+ */
+size_t cbor_deserialize_int64_t(int64_t *val);
+
+/**
+ * @brief Deserialize unsigned 64 bit values from @p stream to @p val
+
+ * @param[out] val   Pointer to destination array
+ *
+ * @return Number of bytes read from @p stream
+ */
+size_t cbor_deserialize_uint64_t(uint64_t *val);
+
+
+/**
+ *  @brief check that the char at the current position is a break char
+ * 
+ * @return 1 if all ok, 0 if no break char was found.
+ */
+size_t cbor_at_break();
+
+size_t cbor_deserialize_float_half(float *val);
+size_t cbor_deserialize_float(float *val);
+size_t cbor_deserialize_double(double *val);
+
+#endif // SENMLCBOR
+
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 footer.md
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/footer.md	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,1 @@
+copyright © 2018 KPN 
diff -r 000000000000 -r a9259748d982 index.md
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/index.md	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,8 @@
+Welcome to the API documet site for the C++ version of KPN's SenML library.
+
+Check out the [detailed docs](https://kpn-iot.github.io/senml-library/) for an in-depth explanation of all the features in the library.
+
+These are the most important classes in the library which you will probably want to check out first:
+
+- [SenMLPack](./class_sen_m_l_pack.html)
+- [SenMLRecord](./class_sen_m_l_record.html)
diff -r 000000000000 -r a9259748d982 kpn_senml.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kpn_senml.h	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,50 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * include all header
+ */
+
+
+#ifndef KML_SENML
+#define KML_SENML
+
+#include <senml_logging.h>
+#include <senml_enums.h>
+#include <senml_base.h>
+#include <senml_record.h>
+#include <senml_record_t.h>
+#include <senml_pack.h>
+#include <senml_pack_t.h>
+#include <senml_int_pack.h>
+#include <senml_double_pack.h>
+#include <senml_binary_record.h>
+#include <senml_bool_record.h>
+#include <senml_float_record.h>
+#include <senml_int_record.h>
+#include <senml_string_record.h>
+
+#include <senml_float_actuator.h>
+#include <senml_string_actuator.h>
+#include <senml_int_actuator.h>
+#include <senml_bool_actuator.h>
+#include <senml_binary_actuator.h>
+
+#include <cbor.h>
+
+
+#endif // KML_SENML
+
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_JsonListener.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_JsonListener.h	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,72 @@
+/**The MIT License (MIT)
+
+Copyright (c) 2015 by Daniel Eichhorn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+See more at http://blog.squix.ch and https://github.com/squix78/json-streaming-parser
+*/
+
+#ifndef SENMLJSONLISTENER
+#define SENMLJSONLISTENER
+
+#ifdef __MBED__
+    #include "mbed.h"
+    #include <string> 
+    using namespace std;
+    #define String string
+#else
+    #include <Arduino.h>
+#endif
+
+/**
+ * Internal helper class for parsing json data.
+ */
+class JsonListener {
+  private:
+
+  public:
+      
+    //virtual void startDocument() = 0;
+
+    virtual void key(String key) = 0;
+
+    virtual void value(String value) = 0;
+
+    //virtual void endArray() = 0;
+
+    //virtual void endObject() = 0;
+
+    //virtual void endDocument() = 0;
+
+    //virtual void startArray() = 0;
+
+    //virtual void startObject() = 0;
+ 
+};
+
+#endif
+
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_JsonStreamingParser.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_JsonStreamingParser.cpp	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,525 @@
+/**The MIT License (MIT)
+
+Copyright (c) 2015 by Daniel Eichhorn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+See more at http://blog.squix.ch and https://github.com/squix78/json-streaming-parser
+*/
+
+#include "senml_JsonStreamingParser.h"
+
+JsonStreamingParser::JsonStreamingParser(): stackPos(0), bufferPos(0), unicodeEscapeBufferPos(0),
+                                            unicodeBufferPos(0), characterCounter(0), unicodeHighSurrogate(0) {
+    reset();
+}
+
+void JsonStreamingParser::reset() {
+    state = STATE_START_DOCUMENT;
+    bufferPos = 0;
+    unicodeEscapeBufferPos = 0;
+    unicodeBufferPos = 0;
+    characterCounter = 0;
+}
+
+void JsonStreamingParser::setListener(JsonListener* listener) {
+  myListener = listener;
+}
+
+void JsonStreamingParser::parse(char c) {
+    //System.out.print(c);
+    // valid whitespace characters in JSON (from RFC4627 for JSON) include:
+    // space, horizontal tab, line feed or new line, and carriage return.
+    // thanks:
+    // http://stackoverflow.com/questions/16042274/definition-of-whitespace-in-json
+    if ((c == ' ' || c == '\t' || c == '\n' || c == '\r')
+        && !(state == STATE_IN_STRING || state == STATE_UNICODE || state == STATE_START_ESCAPE
+            || state == STATE_IN_NUMBER || state == STATE_START_DOCUMENT)) {
+      return;
+    }
+    switch (state) {
+    case STATE_IN_STRING:
+      if (c == '"') {
+        endString();
+      } else if (c == '\\') {
+        state = STATE_START_ESCAPE;
+      } else if ((c < 0x1f) || (c == 0x7f)) {
+        //throw new RuntimeException("Unescaped control character encountered: " + c + " at position" + characterCounter);
+      } else {
+        buffer[bufferPos] = c;
+        increaseBufferPointer();
+      }
+      break;
+    case STATE_IN_ARRAY:
+      if (c == ']') {
+        endArray();
+      } else {
+        startValue(c);
+      }
+      break;
+    case STATE_IN_OBJECT:
+      if (c == '}') {
+        endObject();
+      } else if (c == '"') {
+        startKey();
+      } else {
+        //throw new RuntimeException("Start of string expected for object key. Instead got: " + c + " at position" + characterCounter);
+      }
+      break;
+    case STATE_END_KEY:
+      if (c != ':') {
+        //throw new RuntimeException("Expected ':' after key. Instead got " + c + " at position" + characterCounter);
+      }
+      state = STATE_AFTER_KEY;
+      break;
+    case STATE_AFTER_KEY:
+      startValue(c);
+      break;
+    case STATE_START_ESCAPE:
+      processEscapeCharacters(c);
+      break;
+    case STATE_UNICODE:
+      processUnicodeCharacter(c);
+      break;
+    case STATE_UNICODE_SURROGATE:
+      unicodeEscapeBuffer[unicodeEscapeBufferPos] = c;
+      unicodeEscapeBufferPos++;
+      if (unicodeEscapeBufferPos == 2) {
+        endUnicodeSurrogateInterstitial();
+      }
+      break;
+    case STATE_AFTER_VALUE: {
+      // not safe for size == 0!!!
+      int within = stack[stackPos - 1];
+      if (within == STACK_OBJECT) {
+        if (c == '}') {
+          endObject();
+        } else if (c == ',') {
+          state = STATE_IN_OBJECT;
+        } else {
+          //throw new RuntimeException("Expected ',' or '}' while parsing object. Got: " + c + ". " + characterCounter);
+        }
+      } else if (within == STACK_ARRAY) {
+        if (c == ']') {
+          endArray();
+        } else if (c == ',') {
+          state = STATE_IN_ARRAY;
+        } else {
+          //throw new RuntimeException("Expected ',' or ']' while parsing array. Got: " + c + ". " + characterCounter);
+
+        }
+      } else {
+        //throw new RuntimeException("Finished a literal, but unclear what state to move to. Last state: " + characterCounter);
+      }
+    }break;
+    case STATE_IN_NUMBER:
+      if (c >= '0' && c <= '9') {
+        buffer[bufferPos] = c;
+        increaseBufferPointer();
+      } else if (c == '.') {
+        if (doesCharArrayContain(buffer, bufferPos, '.')) {
+          //throw new RuntimeException("Cannot have multiple decimal points in a number. " + characterCounter);
+        } else if (doesCharArrayContain(buffer, bufferPos, 'e')) {
+          //throw new RuntimeException("Cannot have a decimal point in an exponent." + characterCounter);
+        }
+        buffer[bufferPos] = c;
+        increaseBufferPointer();
+      } else if (c == 'e' || c == 'E') {
+        if (doesCharArrayContain(buffer, bufferPos, 'e')) {
+          //throw new RuntimeException("Cannot have multiple exponents in a number. " + characterCounter);
+        }
+        buffer[bufferPos] = c;
+        increaseBufferPointer();
+      } else if (c == '+' || c == '-') {
+        char last = buffer[bufferPos - 1];
+        if (!(last == 'e' || last == 'E')) {
+          //throw new RuntimeException("Can only have '+' or '-' after the 'e' or 'E' in a number." + characterCounter);
+        }
+        buffer[bufferPos] = c;
+        increaseBufferPointer();
+      } else {
+        endNumber();
+        // we have consumed one beyond the end of the number
+        parse(c);
+      }
+      break;
+    case STATE_IN_TRUE:
+      buffer[bufferPos] = c;
+      increaseBufferPointer();
+      if (bufferPos == 4) {
+        endTrue();
+      }
+      break;
+    case STATE_IN_FALSE:
+      buffer[bufferPos] = c;
+      increaseBufferPointer();
+      if (bufferPos == 5) {
+        endFalse();
+      }
+      break;
+    case STATE_IN_NULL:
+      buffer[bufferPos] = c;
+      increaseBufferPointer();
+      if (bufferPos == 4) {
+        endNull();
+      }
+      break;
+    case STATE_START_DOCUMENT:
+      //myListener->startDocument();
+      if (c == '[') {
+        startArray();
+      } else if (c == '{') {
+        startObject();
+      } else {
+        // throw new ParsingError($this->_line_number,
+        // $this->_char_number,
+        // "Document must start with object or array.");
+      }
+      break;
+    //case STATE_DONE:
+      // throw new ParsingError($this->_line_number, $this->_char_number,
+      // "Expected end of document.");
+    //default:
+      // throw new ParsingError($this->_line_number, $this->_char_number,
+      // "Internal error. Reached an unknown state: ".$this->_state);
+    }
+    characterCounter++;
+  }
+
+void JsonStreamingParser::increaseBufferPointer() {
+  bufferPos = min(bufferPos + 1, BUFFER_MAX_LENGTH - 1);
+}
+
+void JsonStreamingParser::endString() {
+    int popped = stack[stackPos - 1];
+    stackPos--;
+    if (popped == STACK_KEY) {
+      buffer[bufferPos] = '\0';
+      myListener->key(String(buffer));
+      state = STATE_END_KEY;
+    } else if (popped == STACK_STRING) {
+      buffer[bufferPos] = '\0';
+      myListener->value(String(buffer));
+      state = STATE_AFTER_VALUE;
+    } else {
+      // throw new ParsingError($this->_line_number, $this->_char_number,
+      // "Unexpected end of string.");
+    }
+    bufferPos = 0;
+  }
+void JsonStreamingParser::startValue(char c) {
+    if (c == '[') {
+      startArray();
+    } else if (c == '{') {
+      startObject();
+    } else if (c == '"') {
+      startString();
+    } else if (isDigit(c)) {
+      startNumber(c);
+    } else if (c == 't') {
+      state = STATE_IN_TRUE;
+      buffer[bufferPos] = c;
+      increaseBufferPointer();
+    } else if (c == 'f') {
+      state = STATE_IN_FALSE;
+      buffer[bufferPos] = c;
+      increaseBufferPointer();
+    } else if (c == 'n') {
+      state = STATE_IN_NULL;
+      buffer[bufferPos] = c;
+      increaseBufferPointer();
+    } else {
+      // throw new ParsingError($this->_line_number, $this->_char_number,
+      // "Unexpected character for value: ".$c);
+    }
+  }
+
+bool JsonStreamingParser::isDigit(char c) {
+    // Only concerned with the first character in a number.
+    return (c >= '0' && c <= '9') || c == '-';
+  }
+
+void JsonStreamingParser::endArray() {
+    int popped = stack[stackPos - 1];
+    stackPos--;
+    if (popped != STACK_ARRAY) {
+      // throw new ParsingError($this->_line_number, $this->_char_number,
+      // "Unexpected end of array encountered.");
+    }
+    //myListener->endArray();
+    state = STATE_AFTER_VALUE;
+    if (stackPos == 0) {
+      endDocument();
+    }
+  }
+
+void JsonStreamingParser::startKey() {
+    stack[stackPos] = STACK_KEY;
+    stackPos++;
+    state = STATE_IN_STRING;
+  }
+
+void JsonStreamingParser::endObject() {
+    int popped = stack[stackPos];
+    stackPos--;
+    if (popped != STACK_OBJECT) {
+      // throw new ParsingError($this->_line_number, $this->_char_number,
+      // "Unexpected end of object encountered.");
+    }
+    //myListener->endObject();
+    state = STATE_AFTER_VALUE;
+    if (stackPos == 0) {
+      endDocument();
+    }
+  }
+
+void JsonStreamingParser::processEscapeCharacters(char c) {
+    if (c == '"') {
+      buffer[bufferPos] = '"';
+      increaseBufferPointer();
+    } else if (c == '\\') {
+      buffer[bufferPos] = '\\';
+      increaseBufferPointer();
+    } else if (c == '/') {
+      buffer[bufferPos] = '/';
+      increaseBufferPointer();
+    } else if (c == 'b') {
+      buffer[bufferPos] = 0x08;
+      increaseBufferPointer();
+    } else if (c == 'f') {
+      buffer[bufferPos] = '\f';
+      increaseBufferPointer();
+    } else if (c == 'n') {
+      buffer[bufferPos] = '\n';
+      increaseBufferPointer();
+    } else if (c == 'r') {
+      buffer[bufferPos] = '\r';
+      increaseBufferPointer();
+    } else if (c == 't') {
+      buffer[bufferPos] = '\t';
+      increaseBufferPointer();
+    } else if (c == 'u') {
+      state = STATE_UNICODE;
+    } else {
+      // throw new ParsingError($this->_line_number, $this->_char_number,
+      // "Expected escaped character after backslash. Got: ".$c);
+    }
+    if (state != STATE_UNICODE) {
+      state = STATE_IN_STRING;
+    }
+  }
+
+void JsonStreamingParser::processUnicodeCharacter(char c) {
+    if (!isHexCharacter(c)) {
+      // throw new ParsingError($this->_line_number, $this->_char_number,
+      // "Expected hex character for escaped Unicode character. Unicode parsed: "
+      // . implode($this->_unicode_buffer) . " and got: ".$c);
+    }
+
+    unicodeBuffer[unicodeBufferPos] = c;
+    unicodeBufferPos++;
+
+    if (unicodeBufferPos == 4) {
+      int codepoint = getHexArrayAsDecimal(unicodeBuffer, unicodeBufferPos);
+      endUnicodeCharacter(codepoint);
+      return;
+      /*if (codepoint >= 0xD800 && codepoint < 0xDC00) {
+        unicodeHighSurrogate = codepoint;
+        unicodeBufferPos = 0;
+        state = STATE_UNICODE_SURROGATE;
+      } else if (codepoint >= 0xDC00 && codepoint <= 0xDFFF) {
+        if (unicodeHighSurrogate == -1) {
+          // throw new ParsingError($this->_line_number,
+          // $this->_char_number,
+          // "Missing high surrogate for Unicode low surrogate.");
+        }
+        int combinedCodePoint = ((unicodeHighSurrogate - 0xD800) * 0x400) + (codepoint - 0xDC00) + 0x10000;
+        endUnicodeCharacter(combinedCodePoint);
+      } else if (unicodeHighSurrogate != -1) {
+        // throw new ParsingError($this->_line_number,
+        // $this->_char_number,
+        // "Invalid low surrogate following Unicode high surrogate.");
+        endUnicodeCharacter(codepoint);
+      } else {
+        endUnicodeCharacter(codepoint);
+      }*/
+    }
+  }
+bool JsonStreamingParser::isHexCharacter(char c) {
+    return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
+  }
+
+int JsonStreamingParser::getHexArrayAsDecimal(char hexArray[], int length) {
+    int result = 0;
+    for (int i = 0; i < length; i++) {
+      char current = hexArray[length - i - 1];
+      int value = 0;
+      if (current >= 'a' && current <= 'f') {
+        value = current - 'a' + 10;
+      } else if (current >= 'A' && current <= 'F') {
+        value = current - 'A' + 10;
+      } else if (current >= '0' && current <= '9') {
+        value = current - '0';
+      }
+      result += value * 16^i;
+    }
+    return result;
+  }
+
+bool JsonStreamingParser::doesCharArrayContain(char myArray[], int length, char c) {
+    for (int i = 0; i < length; i++) {
+      if (myArray[i] == c) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+void JsonStreamingParser::endUnicodeSurrogateInterstitial() {
+    char unicodeEscape = unicodeEscapeBuffer[unicodeEscapeBufferPos - 1];
+    if (unicodeEscape != 'u') {
+      // throw new ParsingError($this->_line_number, $this->_char_number,
+      // "Expected '\\u' following a Unicode high surrogate. Got: " .
+      // $unicode_escape);
+    }
+    unicodeBufferPos = 0;
+    unicodeEscapeBufferPos = 0;
+    state = STATE_UNICODE;
+  }
+
+void JsonStreamingParser::endNumber() {
+    buffer[bufferPos] = '\0';
+    String value = String(buffer);
+    //float result = 0.0;
+    //if (doesCharArrayContain(buffer, bufferPos, '.')) {
+    //  result = value.toFloat();
+    //} else {
+      // needed special treatment in php, maybe not in Java and c
+    //  result = value.toFloat();
+    //}
+    myListener->value(value.c_str());
+    bufferPos = 0;
+    state = STATE_AFTER_VALUE;
+  }
+
+int JsonStreamingParser::convertDecimalBufferToInt(char myArray[], int length) {
+    int result = 0;
+    for (int i = 0; i < length; i++) {
+      char current = myArray[length - i - 1];
+      result += (current - '0') * 10;
+    }
+    return result;
+  }
+
+void JsonStreamingParser::endDocument() {
+    //myListener->endDocument();
+    state = STATE_DONE;
+  }
+
+void JsonStreamingParser::endTrue() {
+    buffer[bufferPos] = '\0';
+    String value = String(buffer);
+    if (value == "true") {
+      
+    } else {
+      // throw new ParsingError($this->_line_number, $this->_char_number,
+      // "Expected 'true'. Got: ".$true);
+    }
+    bufferPos = 0;
+    state = STATE_AFTER_VALUE;
+  }
+
+void JsonStreamingParser::endFalse() {
+    buffer[bufferPos] = '\0';
+    String value = String(buffer);
+    if (value == "false") {
+      myListener->value("false");
+    } else {
+      // throw new ParsingError($this->_line_number, $this->_char_number,
+      // "Expected 'true'. Got: ".$true);
+    }
+    bufferPos = 0;
+    state = STATE_AFTER_VALUE;
+  }
+
+void JsonStreamingParser::endNull() {
+    buffer[bufferPos] = '\0';
+    String value = String(buffer);
+    if (value == "null") {
+      myListener->value("null");
+    } else {
+      // throw new ParsingError($this->_line_number, $this->_char_number,
+      // "Expected 'true'. Got: ".$true);
+    }
+    bufferPos = 0;
+    state = STATE_AFTER_VALUE;
+  }
+
+void JsonStreamingParser::startArray() {
+    //myListener->startArray();
+    state = STATE_IN_ARRAY;
+    stack[stackPos] = STACK_ARRAY;
+    stackPos++;
+  }
+
+void JsonStreamingParser::startObject() {
+    //myListener->startObject();
+    state = STATE_IN_OBJECT;
+    stack[stackPos] = STACK_OBJECT;
+    stackPos++;
+  }
+
+void JsonStreamingParser::startString() {
+    stack[stackPos] = STACK_STRING;
+    stackPos++;
+    state = STATE_IN_STRING;
+  }
+
+void JsonStreamingParser::startNumber(char c) {
+    state = STATE_IN_NUMBER;
+    buffer[bufferPos] = c;
+    increaseBufferPointer();
+  }
+
+void JsonStreamingParser::endUnicodeCharacter(int codepoint) {
+    buffer[bufferPos] = convertCodepointToCharacter(codepoint);
+    increaseBufferPointer();
+    unicodeBufferPos = 0;
+    unicodeHighSurrogate = -1;
+    state = STATE_IN_STRING;
+  }
+
+char JsonStreamingParser::convertCodepointToCharacter(int num) {
+    if (num <= 0x7F)
+      return (char) (num);
+    // if(num<=0x7FF) return (char)((num>>6)+192) + (char)((num&63)+128);
+    // if(num<=0xFFFF) return
+    // chr((num>>12)+224).chr(((num>>6)&63)+128).chr((num&63)+128);
+    // if(num<=0x1FFFFF) return
+    // chr((num>>18)+240).chr(((num>>12)&63)+128).chr(((num>>6)&63)+128).chr((num&63)+128);
+    return ' ';
+  }
+
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_JsonStreamingParser.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_JsonStreamingParser.h	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,153 @@
+/**The MIT License (MIT)
+
+Copyright (c) 2015 by Daniel Eichhorn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+See more at http://blog.squix.ch and https://github.com/squix78/json-streaming-parser
+*/
+
+#ifndef SENMLJSONSTREAMINGPARSER
+#define SENMLJSONSTREAMINGPARSER
+
+#ifdef __MBED__
+    #include "mbed.h"
+    #include <string> 
+    using namespace std;
+    #define String string
+#else
+    #include <Arduino.h>
+#endif
+#include <senml_JsonListener.h>
+
+#define STATE_START_DOCUMENT     0
+#define STATE_DONE               -1
+#define STATE_IN_ARRAY           1
+#define STATE_IN_OBJECT          2
+#define STATE_END_KEY            3
+#define STATE_AFTER_KEY          4
+#define STATE_IN_STRING          5
+#define STATE_START_ESCAPE       6
+#define STATE_UNICODE            7
+#define STATE_IN_NUMBER          8
+#define STATE_IN_TRUE            9
+#define STATE_IN_FALSE           10
+#define STATE_IN_NULL            11
+#define STATE_AFTER_VALUE        12
+#define STATE_UNICODE_SURROGATE  13
+
+#define STACK_OBJECT             0
+#define STACK_ARRAY              1
+#define STACK_KEY                2
+#define STACK_STRING             3
+
+#define BUFFER_MAX_LENGTH  512
+
+/**
+ * Internal helper class for parsing json data.
+ */
+class JsonStreamingParser {
+    public:
+        JsonStreamingParser();
+        void parse(char c);
+        void setListener(JsonListener* listener);
+        void reset();
+
+
+    private:
+
+        int state;
+        int stack[20];
+        int stackPos;
+        JsonListener* myListener;
+
+        //bool doEmitWhitespace = false;
+        // fixed length buffer array to prepare for c code
+        char buffer[BUFFER_MAX_LENGTH];
+        int bufferPos;
+
+        char unicodeEscapeBuffer[10];
+        int unicodeEscapeBufferPos;
+
+        char unicodeBuffer[10];
+        int unicodeBufferPos;
+
+        int characterCounter;
+
+        int unicodeHighSurrogate;
+
+        void increaseBufferPointer();
+
+        void endString();
+
+        void endArray();
+
+        void startValue(char c);
+
+        void startKey();
+
+        void processEscapeCharacters(char c);
+
+        bool isDigit(char c);
+
+        bool isHexCharacter(char c);
+
+        char convertCodepointToCharacter(int num);
+
+        void endUnicodeCharacter(int codepoint);
+
+        void startNumber(char c);
+
+        void startString();
+
+        void startObject();
+
+        void startArray();
+
+        void endNull();
+
+        void endFalse();
+
+        void endTrue();
+
+        void endDocument();
+
+        int convertDecimalBufferToInt(char myArray[], int length);
+
+        void endNumber();
+
+        void endUnicodeSurrogateInterstitial();
+
+        bool doesCharArrayContain(char myArray[], int length, char c);
+
+        int getHexArrayAsDecimal(char hexArray[], int length);
+
+        void processUnicodeCharacter(char c);
+
+        void endObject();
+};
+
+#endif
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_base.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_base.cpp	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,72 @@
+
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * base class for all senml objects
+ */
+
+
+#include <senml_base.h>
+#include <senml_pack.h>
+
+
+SenMLBase::SenMLBase(): _prev(NULL), _next(NULL)
+{
+}
+
+SenMLBase::~SenMLBase()
+{
+    if(this->_prev)
+        this->_prev->setNext(this->_next);
+    if(this->_next)
+        this->_next->setPrev(this->_prev);
+    
+}
+
+void SenMLBase::setNext(SenMLBase* value)
+{
+    if(value == NULL){                                              //if next becomes null and there is a root, then this object became the last in the list, so let the root know.
+        SenMLPack* root = (SenMLPack*)this->getRoot();
+        if(root)
+            root->setLast(this);
+    }
+    this->_next = value;
+}
+
+ void SenMLBase::setPrev(SenMLBase* value)
+ {
+     this->_prev = value;
+ }
+
+SenMLBase* SenMLBase::getPrev()
+{
+    return this->_prev;
+}
+
+SenMLBase* SenMLBase::getRoot()
+{
+    SenMLBase* prev = this->_prev;
+    while(prev){
+        SenMLBase* newPrev = prev->getPrev();
+        if(newPrev == NULL)
+            return prev;
+        else
+            prev = newPrev;
+    }
+    return this;                                    //if there was no first prev, it means we are root.
+}
+
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_base.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_base.h	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,127 @@
+
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * base class for all senml objects header
+ */
+
+#ifndef SENMLBASE
+#define SENMLBASE
+
+#ifdef __MBED__
+    #include "mbed.h"
+    #include "sstream"
+#else
+    #include <stream.h>
+#endif
+#include <senml_enums.h>
+#include <math.h>
+
+/**
+ * the base class for all objects that can be used in the senml data tree.
+ */
+class SenMLBase
+{
+friend class SenMLPack; 
+friend class SenMLRecord; 
+friend class SenMLJsonListener; 
+friend class SenMLBaseParser;
+public:
+    SenMLBase();
+    ~SenMLBase();
+
+    /** get the next item in the list.
+     * @returns: a pointer to the next SenMLBase object in the list or NULL when at the end of the list.
+     */
+    SenMLBase* getNext(){ return this->_next; };
+
+    /**
+     * Get the root object of this list. Usually, this is a SenMLPack object.
+     * The root object is defined as the first item in the list. 
+     * @returns: a pointer to the first SenMLBase object in the list or NULL when there is none.
+     */
+    SenMLBase* getRoot();
+
+    //This function is called by the root SenMLPack object to indicate that the object
+    //should adjust it's time info relative to the new base time (if applicable)
+    //doesn't do anything by default
+    //params: prev: previous base time
+    //        time: new base time
+    virtual void adjustToBaseTime(double prev, double time) {};
+
+    /**
+     * renders all the fields to json, without the starting and ending brackets.
+     * Inheriters can extend this function if they want to add extra fields to the json output
+     * note: this is public so that custom implementations for the record object can use other objects 
+     * internally and render to json using this function (ex: coordinatesRecord using 3 floatRecrods for lat, lon & alt.
+     * @returns: None
+    */
+    virtual void fieldsToJson() = 0;
+
+    /**
+     * renders all the fields to cbor format. renders all the fields of the object without the length info 
+     * at the beginning
+     * note: this is public so that custom implementations for the record object can use other objects 
+     * internally and render to json using this function (ex: coordinatesRecord using 3 floatRecrods for 
+     * lat, lon & alt.
+     * @returns: The number of bytes that were written.
+    */
+    virtual int fieldsToCbor() = 0;
+
+protected:
+
+    /*
+    renders all the fields to json, with the starting and ending brackets.
+    Inheriters can extend this function if they want to add extra fields to the json output
+    note: tihs is public so that custom implementations for the record object can use other objects internally and render to json using this function (ex: coordinatesRecord using 3 floatRecrods for lat, lon & alt.
+    */
+    virtual void contentToJson() = 0;
+
+
+    //assign the element in the list that this object points to.
+    void setNext(SenMLBase* value);
+    
+    //assign the previous element in the list that thisobject points to.
+    void setPrev(SenMLBase* value);
+    //assign the previous element in the list that thisobject points to.
+    SenMLBase* getPrev();
+
+    //derived classes can use this function to see if the root object (getRoot) is a SenMLPack
+    //class or not.
+    virtual bool isPack() { return false; }
+
+    //renders the content of the pack object without [], but still with {} for objects
+    virtual int contentToCbor() = 0;    
+
+    //calculates the nr of items that this object will put in the json array in senml representation
+    //this is used for rendering cbor which needs to declare the nr of elements in an array.
+    //packs can have multiple elements in the array, but also custom implementations for records that wrap muliple 
+    //records.
+    virtual int getArrayLength() { return 1; };
+
+    //calculates the nr of fields that this object will produce.
+    virtual int getFieldLength() = 0;
+
+
+private:
+    SenMLBase* _next;                               //reference to the next element in the list.
+    SenMLBase* _prev;                               //reference to the previous element, needed for deletion of record
+};
+
+
+
+#endif // SENMLBASE
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_base_parser.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_base_parser.cpp	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,64 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * base class for all parsers
+ */
+
+
+#include <senml_base_parser.h>
+#include <senml_logging.h>
+
+void SenMLBaseParser::setCurrentPack(String& name)
+{
+    this->curPack = NULL;                                                   //reset before we start so we indicate that nothing was ound
+    this->curPackName = name;                                               //need a ref to the name in case we can't find the pack object
+
+    if(name == this->root->getBaseName()){                                  //check the root first, most common probably
+        this->curPack = this->root;
+        return;
+    }
+    SenMLBase* found = this->root->getFirst();
+    while(found){
+        if(found->isPack() && name == ((SenMLPack*)found)->getBaseName()){
+            this->curPack = (SenMLPack*)found;
+            return;
+        }
+        found = found->getNext();
+    }
+}
+
+void SenMLBaseParser::checkBaseUnit(String& name)
+{
+    if(!(this->curPack && name == senml_units_names[this->curPack->getBaseUnit()]))
+        log_debug("bu different");
+}
+
+void SenMLBaseParser::setCurrentRecord(String& name)
+{
+    this->curRec = NULL;
+    this->curRecName = name;
+    if(this->curPack){
+        SenMLBase* rec = this->curPack->getFirst();
+        while(rec){
+            if(rec->isPack() == false &&  name == ((SenMLRecord*)rec)->getName()){
+                this->curRec = (SenMLRecord*)rec;
+                return;
+            }
+            rec = rec->getNext();
+        }
+    }
+}
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_base_parser.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_base_parser.h	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,61 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * base class for all parsers header
+ */
+
+
+
+#ifndef SENMLBASEPARSER
+#define SENMLBASEPARSER
+
+#include <senml_pack.h>
+#include <senml_record.h>
+
+/**
+ * base class for json and cbor parsers. Provides functionality to connect to 
+ * and search in senml packs and recrods. 
+*/
+class SenMLBaseParser {
+
+  public:
+    SenMLBaseParser(SenMLPack* root): root(root), curPack(root) {};
+
+
+  private:
+    
+
+  protected:
+    SenMLPack* root;                    //the root document for which we are parsing. Used to search the up all the actuators.
+    SenMLRecord* curRec;
+    SenMLPack* curPack;                 //used while searching, to store the current pack to use. init to root pack by default. Make it protected, so child parsers can easily access it without too much fuzz
+    String curRecName;                  //keeps a ref of the record name, for raising events for unknown records
+    String curPackName;                 //name of hte current pack to send the actuator to.
+
+    //looks up the pack object that has to contain the next field. This is in order
+    //to support multiple packs (gateway functionality)
+    void setCurrentPack(String& name);
+
+
+    //check if base units match, if not, show error message
+    void checkBaseUnit(String& name);
+
+    //look up a record with the spedified name in the current pack 
+    void setCurrentRecord(String& name);
+};
+
+#endif // SENMLBASEPARSER
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_binary_actuator.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_binary_actuator.cpp	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,80 @@
+
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * support for binary actuators
+ */
+
+#include <senml_binary_actuator.h>
+#include <senml_logging.h>
+
+#ifdef ESP32
+    extern "C" {
+    #include "libb64/cdecode.h"
+    }
+    int base64_dec_len(char * input, int inputLen) {
+        int i = 0;
+        int numEq = 0;
+        for(i = inputLen - 1; input[i] == '='; i--) {
+            numEq++;
+        }
+
+        return ((6 * inputLen) / 8) - numEq;
+    }
+#elif __MBED__
+    #include <base64.h>
+    int base64_dec_len(char * input, int inputLen) {
+        int i = 0;
+        int numEq = 0;
+        for(i = inputLen - 1; input[i] == '='; i--) {
+            numEq++;
+        }
+
+        return ((6 * inputLen) / 8) - numEq;
+    }
+#else
+    #include <Base64.h>
+#endif
+
+void SenMLBinaryActuator::actuate(const void* value, int dataLength, SenMLDataType dataType)
+{
+    if(dataType == SENML_TYPE_DATA){
+        int decodedLen = base64_dec_len((char*)value, dataLength);
+        char decoded[decodedLen];
+        #ifdef ESP32
+            base64_decode_chars((const char*)value, dataLength, decoded);
+        #elif __MBED__
+            // todo: check result of function
+            size_t olen;
+            mbedtls_base64_decode((unsigned char*)decoded, decodedLen, &olen, (const unsigned char*)value, dataLength);
+        #else
+            base64_decode(decoded, (char*)value, dataLength); 
+        #endif 
+
+        this->set((unsigned char*)decoded, decodedLen);
+        if(this->callback)
+            this->callback((unsigned char*)decoded, decodedLen);
+    }
+    else if(dataType == CBOR_TYPE_DATA){
+        this->set((unsigned char*)value, dataLength);
+        if(this->callback)
+            this->callback((unsigned char*)value, dataLength);
+    }
+    else
+        log_debug("invalid type");
+}
+
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_binary_actuator.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_binary_actuator.h	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,57 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * support for binary actuators header
+ */
+
+#ifndef SENMLBINARYACTUATOR
+#define SENMLBINARYACTUATOR
+
+#include <senml_binary_record.h>
+
+#define BINARY_ACTUATOR_SIGNATURE void (*callback)(const unsigned char*, int)
+
+/**
+ * A SenMLRecord that stores binary data and supports actuation.
+ */ 
+class SenMLBinaryActuator: public SenMLBinaryRecord
+{
+    friend class SenMLCborParser;
+public:
+    SenMLBinaryActuator(const char* name, BINARY_ACTUATOR_SIGNATURE): SenMLBinaryRecord(name, SENML_UNIT_NONE), callback(callback) {};
+    SenMLBinaryActuator(const char* name, SenMLUnit unit, BINARY_ACTUATOR_SIGNATURE): SenMLBinaryRecord(name, unit), callback(callback) {};
+    ~SenMLBinaryActuator(){};
+
+protected:
+
+    //called while parsing a senml message, when the parser found the value for an SenMLJsonListener
+    virtual void actuate(const void* value, int dataLength, SenMLDataType dataType);
+
+    //called while parsing a senml message, when the parser found the value for an SenMLJsonListener
+    //the actual value has already been converted to it's appropriate type
+    inline void actuate(const char* value, int length)
+    {
+        this->set((unsigned char*)value, length);
+        if(this->callback)
+            this->callback((unsigned char*)value, length);
+    };
+
+private:
+    BINARY_ACTUATOR_SIGNATURE;
+};
+
+#endif // SENMLBINARYACTUATOR
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_binary_record.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_binary_record.cpp	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,56 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * support for binary sensors
+ */
+
+#include <senml_binary_record.h>
+#include <senml_helpers.h>
+#include <cbor.h>
+
+SenMLBinaryRecord::SenMLBinaryRecord(const char* name): SenMLRecord(name)
+{
+}
+
+SenMLBinaryRecord::SenMLBinaryRecord(const char* name, SenMLUnit unit): SenMLRecord(name, unit)
+{
+}
+
+bool SenMLBinaryRecord::set(unsigned char* value, unsigned int length, double time) 
+{
+    this->_value = (unsigned char*)malloc(length);
+    memcpy(this->_value, value, length);
+    this->_length = length;
+    return this->setTime(time);
+}
+
+
+void SenMLBinaryRecord::fieldsToJson()
+{
+    SenMLRecord::fieldsToJson();
+    printText(",\"vd\":\"", 7);
+    printBinaryAsBase64(this->_value, this->_length);
+    printText("\"", 1);
+}
+
+int SenMLBinaryRecord::fieldsToCbor()
+{
+    int res = SenMLRecord::fieldsToCbor();
+    res += cbor_serialize_int(SENML_CBOR_VD_LABEL);
+    res += cbor_serialize_byte_string((const char*)this->_value, this->_length);
+    return res;
+}
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_binary_record.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_binary_record.h	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,96 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * support for binary sensors header
+ */
+
+#ifndef SENMLBINARYRECORD
+#define SENMLBINARYRECORD
+
+#include <senml_record.h>
+
+/**
+ * A SenMLRecord that stores binary data.
+ * This type of object can only be used for sensor data. If actuation is needed, use SenMLBinaryActuator
+ * instead.
+ */ 
+class SenMLBinaryRecord: public SenMLRecord
+{
+public:
+    SenMLBinaryRecord(const char* name);
+    SenMLBinaryRecord(const char* name, SenMLUnit unit);
+    ~SenMLBinaryRecord(){ if(this->_value) free(this->_value); };
+
+	
+	#ifdef __MBED__
+	
+	/** 
+	* set the value and length.
+    * warning: value is not copied over. a direct reference to the buffer is stored, so if this
+    * memory is changed/freed, then the data stored in the object will also be changed.
+    */
+	bool set(unsigned char* value, unsigned int length)
+    {
+        return this->set(value, length, NAN);
+    }
+    
+    /** 
+	* set the value and length.
+    * warning: value is not copied over. a direct reference to the buffer is stored, so if this
+    * memory is changed/freed, then the data stored in the object will also be changed.
+    */
+    bool set(unsigned char* value, unsigned int length, double time);
+    
+	#else
+    /** 
+	* set the value and length.
+    * warning: value is not copied over. a direct reference to the buffer is stored, so if this
+    * memory is changed/freed, then the data stored in the object will also be changed.
+    */
+    bool set(unsigned char* value, unsigned int length, double time = NAN);
+
+	#endif
+	
+    /**
+     * renders all the fields to json, without the starting and ending brackets.
+     * Inheriters can extend this function if they want to add extra fields to the json output
+     * note: this is public so that custom implementations for the record object can use other objects 
+     * internally and render to json using this function (ex: coordinatesRecord using 3 floatRecrods for lat, lon & alt.
+     * @returns: None
+    */
+    void fieldsToJson();
+
+    /**
+     * renders all the fields to cbor format. renders all the fields of the object without the length info 
+     * at the beginning
+     * note: this is public so that custom implementations for the record object can use other objects 
+     * internally and render to json using this function (ex: coordinatesRecord using 3 floatRecrods for 
+     * lat, lon & alt.
+     * @returns: The number of bytes that were written.
+    */
+    virtual int fieldsToCbor();
+
+protected:
+
+    
+private:
+    unsigned char* _value;                  //raw data buffer (not null terminated)
+    unsigned int _length;                   //size of the _data packet
+
+};
+
+#endif // SENMLBINARYRECORD
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_bool_actuator.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_bool_actuator.cpp	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,35 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * support for bool actuators
+ */
+
+#include <senml_bool_actuator.h>
+#include <senml_logging.h>
+
+void SenMLBoolActuator::actuate(const void* value, int dataLength, SenMLDataType dataType)
+{
+    if(dataType == SENML_TYPE_BOOL || dataType == CBOR_TYPE_BOOL){
+        this->set(*((bool*)value));
+        if(this->callback){
+            this->callback(*((bool*)value));
+        }
+    }
+    else{
+        log_debug("invalid type");
+    }
+}
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_bool_actuator.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_bool_actuator.h	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,48 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * support for bool actuator header
+ */
+
+#ifndef SENMLBOOLACTUATOR
+#define SENMLBOOLACTUATOR
+
+#include <senml_bool_record.h>
+
+#define BOOL_ACTUATOR_SIGNATURE void (*callback)(bool)
+
+/**
+ * A SenMLRecord that stores boolean data and supports actuation.
+ */ 
+class SenMLBoolActuator: public SenMLBoolRecord
+{
+public:
+    SenMLBoolActuator(const char* name, BOOL_ACTUATOR_SIGNATURE): SenMLBoolRecord(name, SENML_UNIT_NONE, false), callback(callback) {};
+    SenMLBoolActuator(const char* name, SenMLUnit unit, BOOL_ACTUATOR_SIGNATURE): SenMLBoolRecord(name, unit, false), callback(callback) {};
+    SenMLBoolActuator(const char* name, SenMLUnit unit, bool value, BOOL_ACTUATOR_SIGNATURE):  SenMLBoolRecord(name, unit, value), callback(callback) {};
+    ~SenMLBoolActuator(){};
+
+protected:
+
+    //called while parsing a senml message, when the parser found the value for an SenMLJsonListener
+    virtual void actuate(const void* value, int dataLength, SenMLDataType dataType);
+
+private:
+    BOOL_ACTUATOR_SIGNATURE;
+};
+
+#endif // SENMLBOOLACTUATOR
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_bool_record.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_bool_record.cpp	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,50 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * support for bool sensor 
+ */
+
+
+#include <senml_bool_record.h>
+#include <cbor.h>
+#include <senml_helpers.h>
+
+SenMLBoolRecord::SenMLBoolRecord(const char* name): SenMLRecordTemplate(name)
+{
+}
+
+SenMLBoolRecord::SenMLBoolRecord(const char* name, SenMLUnit unit): SenMLRecordTemplate(name, unit)
+{
+}
+
+void SenMLBoolRecord::fieldsToJson()
+{
+    SenMLRecord::fieldsToJson();
+    printText(",\"vb\":", 6);
+    if(this->get())
+        printText("true", 4);
+    else
+        printText("false", 5);
+}
+
+int SenMLBoolRecord::fieldsToCbor()
+{
+    int res = SenMLRecord::fieldsToCbor();
+    res += cbor_serialize_int(SENML_CBOR_VB_LABEL);
+    res += cbor_serialize_bool(this->get());
+    return res;
+}
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_bool_record.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_bool_record.h	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,63 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * support for bool sensor header
+ */
+
+#ifndef SENMLBOOLRECORD
+#define SENMLBOOLRECORD
+
+#include <senml_record_t.h>
+
+/**
+ * A SenMLRecord that stores boolean data.
+ * This type of object can only be used for sensor data. If actuation is needed, use SenMLBoolActuator
+ * instead.
+ */ 
+class SenMLBoolRecord: public SenMLRecordTemplate<bool>
+{
+public:
+    SenMLBoolRecord(const char* name);
+    SenMLBoolRecord(const char* name, SenMLUnit unit);
+    SenMLBoolRecord(const char* name, SenMLUnit unit, bool value):  SenMLRecordTemplate(name, unit, value){};
+    ~SenMLBoolRecord(){};
+
+    /**
+     * renders all the fields to json, without the starting and ending brackets.
+     * Inheriters can extend this function if they want to add extra fields to the json output
+     * note: this is public so that custom implementations for the record object can use other objects 
+     * internally and render to json using this function (ex: coordinatesRecord using 3 floatRecrods for lat, lon & alt.
+     * @returns: None
+    */
+    virtual void fieldsToJson();
+
+    /**
+     * renders all the fields to cbor format. renders all the fields of the object without the length info 
+     * at the beginning
+     * note: this is public so that custom implementations for the record object can use other objects 
+     * internally and render to json using this function (ex: coordinatesRecord using 3 floatRecrods for 
+     * lat, lon & alt.
+     * @returns: The number of bytes that were written.
+    */
+    virtual int fieldsToCbor();
+
+protected:
+    
+private:
+};
+
+#endif // SENMLBOOLRECORD
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_cbor_parser.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_cbor_parser.cpp	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,203 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * parse cbor
+ */
+
+
+#include <senml_cbor_parser.h>
+#include <senml_helpers.h>
+#include <senml_binary_actuator.h>
+
+void SenMLCborParser::parse(Stream* source)
+{
+    this->ctx.data.stream = source;
+    this->ctx.dataAsBlob = false;
+    this->internalParse();
+}
+
+
+void SenMLCborParser::parse(char* source, int length)
+{
+    this->ctx.data.blob.data = source;
+    this->ctx.data.blob.curPos = 0;
+    this->ctx.data.blob.length = length;
+    this->ctx.dataAsBlob = true;
+    this->internalParse();
+}
+
+void SenMLCborParser::internalParse()
+{
+    unsigned int read_bytes = this->processArray();         //the root element of senml input is always an array. We do this cause the readable() function on mbed doesn't work as expected, so we can't read until there is no more data, we need to read until we have valid input.
+
+    if (read_bytes == 0) {
+        log_debug("invalid input");
+    }
+    flush();                                                    //make certain that the input streams are empty when done. This also resets any internally stored data. If we don't do this, we can only handle 1 bad input stream, ten it breaks.
+}
+
+
+void SenMLCborParser::processDouble(double value){
+    switch (this->curLabel)
+    {
+        //case SENML_CBOR_S_LABEL:
+        case SENML_CBOR_BV_LABEL: this->ctx.baseValue.baseDouble = value; break;
+        case SENML_CBOR_V_LABEL:
+            double calculated = this->ctx.baseValue.baseDouble + value;
+            this->setValue(&calculated, sizeof(double), CBOR_TYPE_DOUBLE); 
+            break;
+    }
+}
+
+unsigned int SenMLCborParser::parseNext()
+{
+    switch (CBOR_TYPE) {
+        case CBOR_UINT:     return this->processUnsignedInt();
+        case CBOR_NEGINT:   return this->processInt();
+        case CBOR_BYTES:    return this->processBytes(CBOR_TYPE_DATA);
+        case CBOR_TEXT:     return this->processBytes(CBOR_TYPE_STRING); 
+        case CBOR_ARRAY:    return this->processArray();
+        case CBOR_MAP:      return this->processMap();
+        case CBOR_7: {
+            bool boolRes;
+            float floatVal;
+            double doubleVal;
+            size_t read_bytes;
+            switch ((int)peekChar()) {
+                case CBOR_FALSE:   
+                    readChar();                                         //need to remove the char from the stream.
+                    boolRes = false;
+                    this->setValue((void*)&boolRes, sizeof(boolRes), CBOR_TYPE_BOOL); 
+                    return 1;
+                case CBOR_TRUE:     
+                    readChar();                                         //need to remove the char from the stream.
+                    boolRes = true;
+                    this->setValue((void*)&boolRes, sizeof(boolRes), CBOR_TYPE_BOOL); 
+                    return 1;
+                case CBOR_FLOAT16:  
+                    read_bytes = cbor_deserialize_float_half(&floatVal);
+                    this->processDouble(floatVal); 
+                    return read_bytes;
+                case CBOR_FLOAT32:
+                    read_bytes = cbor_deserialize_float(&floatVal);
+                    this->processDouble(floatVal); 
+                    return read_bytes;
+                case CBOR_FLOAT64:
+                    read_bytes = cbor_deserialize_double(&doubleVal);
+                    this->processDouble(doubleVal); 
+                    return read_bytes;
+            }
+        }
+    }
+    return 0;                                                                   //if we get here, something went wrong
+}
+
+void SenMLCborParser::setValue(void* value, int length, SenMLDataType type)
+{
+    if(this->curRec){
+        this->curRec->actuate(value, length, type);
+    }
+    else {
+        SenMLPack* pack = this->curPack;
+        if(!pack)
+            pack = this->root;
+        if(pack)
+            pack->actuate(this->curPackName.c_str(), this->curRecName.c_str(), value, length, type);
+    }
+}
+
+void SenMLCborParser::setBinaryValue(const char* value, int length)
+{
+    if(this->curRec){
+        ((SenMLBinaryActuator*)this->curRec)->actuate(value, length);
+    }
+    else {
+        SenMLPack* pack = this->curPack;
+        if(!pack)
+            pack = this->root;
+        if(pack)
+            pack->actuate(this->curPackName.c_str(), this->curRecName.c_str(), value, length, CBOR_TYPE_DATA);
+    }
+}
+
+
+unsigned int SenMLCborParser::processBytes(SenMLDataType type)
+{
+    uint64_t bytes_length;                              //needs to be this big for decode_int
+    size_t bytes_read = decode_int(&bytes_length);
+    
+    if(bytes_read == 0) return 0;
+    char buffer[bytes_length + 1];                      //need a null 
+    for(int i = 0; i < (int)bytes_length; i++)
+        buffer[i] = readChar();
+    buffer[bytes_length] = 0;                           //close it, just to be save. not always needed, but it is for strings
+
+    if(type == CBOR_TYPE_DATA){                         //we are expecting binary data, so it has to be for a binary data value.
+        if(this->curLabel == SENML_CBOR_VD_LABEL)
+            this->setBinaryValue(buffer, bytes_length); 
+        else
+            log_debug("invalid input");
+    }else{                                              //its text
+        String value;
+        switch (this->curLabel)
+        {
+            case SENML_CBOR_BN_LABEL: 
+                value = buffer;
+                this->setCurrentPack(value); 
+                break;
+            case SENML_CBOR_BU_LABEL: 
+                value = buffer;
+                this->checkBaseUnit(value); 
+                break;
+            case SENML_CBOR_N_LABEL:
+                value = buffer;
+                this->setCurrentRecord(value); 
+                break;
+            case SENML_CBOR_VS_LABEL:
+                this->setValue((void*)buffer, bytes_length, CBOR_TYPE_STRING);
+                    break;
+        }
+    }
+    return bytes_read + bytes_length;
+}
+
+unsigned int SenMLCborParser::processArray()
+{
+    const bool is_indefinite = (peekChar() == (CBOR_ARRAY | CBOR_VAR_FOLLOWS));
+    uint64_t array_length = 0;
+    size_t read_bytes;
+
+    if (is_indefinite){
+        log_debug("not supported");
+    }
+    else
+        read_bytes = decode_int(&array_length);
+
+    size_t i = 0;
+
+    while (i < array_length) {
+        size_t inner_read_bytes = this->parseNext();
+        if (inner_read_bytes == 0) {
+            log_debug("invalid input");
+            break;
+        }
+        read_bytes += inner_read_bytes;
+        ++i;
+    }
+
+    return read_bytes;
+}
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_cbor_parser.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_cbor_parser.h	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,147 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * parse cbor header
+ */
+
+#ifndef SENMLCBORPARSER
+#define SENMLCBORPARSER
+
+#include <senml_base_parser.h>
+#include <senml_enums.h>
+#include <senml_logging.h>
+#include <senml_helpers.h>
+#include <cbor.h>
+
+enum SenMLCborDataType {};
+
+#define SENML_CBOR_KEY 1
+#define SENML_CBOR_VALUE 2
+
+
+/**
+ * Inernal helper class for parsing cbor data.
+ */
+class SenMLCborParser: public SenMLBaseParser {
+
+  public:
+    SenMLCborParser(SenMLPack* root, SenMLStreamMethod format): SenMLBaseParser(root), state(0) 
+    {
+        this->ctx.format = format;
+        this->ctx.baseValue.baseUint = 0;               //init to 0, so we get correct results for first element as well.
+        this->ctx.baseSum.baseUint = 0;
+        _streamCtx = &this->ctx;                                     //set the global variable so that we don't have to pass it along on the stack all the time (saves mem & codesize)
+    };
+
+    //convert the cbor raw data into senml and actuate the records in the root pack.
+    void parse(Stream* source);
+
+
+    void parse(char* source, int length);
+
+
+  private:
+    unsigned char state;                                            //keeps track of the current parse state
+    int curLabel;                                         //the cbor number that represents the current senml label (unit, value, boolvalue, basename,..). The next item to read has to be the value for the label
+    StreamContext ctx;
+
+    unsigned int parseNext();
+
+    void setValue(void* value, int length, SenMLDataType type);
+    void setBinaryValue(const char* value, int length);
+    void processDouble(double value);
+
+    void internalParse();
+
+    unsigned int processBytes(SenMLDataType type);
+
+    unsigned int processArray();
+
+    inline unsigned int processMap()
+    {
+        const bool is_indefinite = (peekChar() == (CBOR_MAP | CBOR_VAR_FOLLOWS));
+        uint64_t map_length = 0;
+        size_t read_bytes;
+
+        if (is_indefinite){
+            log_debug("not supported");
+        }
+        else 
+            read_bytes = decode_int(&map_length);
+
+        unsigned char curState = this->state;
+        size_t i = 0;
+        while (i < map_length) {
+            size_t key_read_bytes, value_read_bytes;
+            this->state = SENML_CBOR_KEY;
+            key_read_bytes = this->parseNext(); /* key */
+            this->state = SENML_CBOR_VALUE;
+            value_read_bytes = this->parseNext(); /* value */
+            if (key_read_bytes == 0 || value_read_bytes == 0) {
+                log_debug("invalid input");
+                break;
+            }
+            read_bytes += key_read_bytes + value_read_bytes;
+            ++i;
+        }
+        this->state = curState;                                     //reset to the original state. was changed inside loop
+        this->ctx.baseValue.baseUint = 0;                                        //if there was a base value, reset it for the next run.
+        return read_bytes;
+    };
+
+    inline unsigned int processUnsignedInt()
+    {
+        uint64_t val; 
+        size_t read_bytes = cbor_deserialize_uint64_t(&val); 
+        if(this->state == SENML_CBOR_VALUE){
+            switch (this->curLabel)
+            {
+                case SENML_CBOR_BV_LABEL: this->ctx.baseValue.baseUint = val; break;
+                case SENML_CBOR_V_LABEL: 
+                    uint64_t calculated = this->ctx.baseValue.baseUint + val;
+                    this->setValue((void*)&calculated, sizeof(uint64_t), CBOR_TYPE_UINT); 
+                    break;
+            }
+        }
+        else if(this->state == SENML_CBOR_KEY)             //store the value type (basename, baseunit, value, stringvalue,...)
+            this->curLabel = (int)val;
+        return read_bytes; 
+    };
+
+    inline unsigned int processInt()
+    {
+        int64_t val; 
+        size_t read_bytes = cbor_deserialize_int64_t(&val); 
+        if(this->state == SENML_CBOR_VALUE){
+            switch (this->curLabel)
+            {
+                case SENML_CBOR_BV_LABEL: this->ctx.baseValue.baseInt = val; break;
+                case SENML_CBOR_V_LABEL: 
+                uint64_t calculated = this->ctx.baseValue.baseInt + val;
+                    this->setValue((void*)&calculated, sizeof(int64_t), CBOR_TYPE_INT); 
+                    break;
+            }
+        }
+        else if(this->state == SENML_CBOR_KEY)             //store the value type (basename, baseunit, value, stringvalue,...)
+            this->curLabel = val;
+        return read_bytes; 
+    };
+
+    
+};
+
+#endif // SENMLCBORPARSER
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_double_pack.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_double_pack.cpp	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,78 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * pack (document) with double base values
+ */
+
+
+#include <senml_double_pack.h> 
+#include <senml_helpers.h>
+#include <cbor.h> 
+
+
+void SenMLDoublePack::setupStreamCtx(Stream *dest, SenMLStreamMethod format)
+{
+    SenMLPack::setupStreamCtx(dest, format);
+    _streamCtx->baseValue.baseDouble = this->getBaseValue();
+    _streamCtx->baseSum.baseDouble = this->getBaseValue();
+    _streamCtx->baseDataType = CBOR_TYPE_DOUBLE;
+}
+
+void SenMLDoublePack::setupStreamCtx(char *dest, int length, SenMLStreamMethod format)
+{
+    SenMLPack::setupStreamCtx(dest, length, format);
+    _streamCtx->baseValue.baseDouble = this->getBaseValue();
+    _streamCtx->baseSum.baseDouble = this->getBaseValue();
+    _streamCtx->baseDataType = CBOR_TYPE_DOUBLE;
+}
+
+void SenMLDoublePack::fieldsToJson() 
+{
+    double val;
+    SenMLPack::fieldsToJson();
+    val = this->getBaseValue();
+    if(val != 0){
+        printText(",\"bv\":", 6);
+        printDouble(val, SENML_MAX_DOUBLE_PRECISION);
+    }
+
+    val = this->getBaseSum();
+    if(val != 0){
+        printText(",\"bs\":", 6);
+        printDouble(val, SENML_MAX_DOUBLE_PRECISION);
+    }
+
+}
+
+int SenMLDoublePack::fieldsToCbor() 
+{
+    int val;
+    int res = SenMLPack::fieldsToCbor();
+    
+    val = this->getBaseValue();
+    if(val){
+        res += cbor_serialize_int(SENML_CBOR_VB_LABEL);
+        res += cbor_serialize_double(val);
+    }
+
+    val = this->getBaseSum();
+    if(val){
+        res += cbor_serialize_int(SENML_CBOR_BS_LABEL);
+        res += cbor_serialize_double(val);
+    }
+    return res;
+}
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_double_pack.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_double_pack.h	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,75 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * pack (document) with double base values headers
+ */
+
+#ifndef SENMLDOUBLEPACK
+#define SENMLDOUBLEPACK
+
+#include <senml_pack_t.h>
+
+/**
+ * An implimentation of the SenMLPack template that stores double base value and base-sum.
+ */
+class SenMLDoublePack: public SenMLPackTemplate<double>
+{
+    public:
+
+        SenMLDoublePack(const char* baseName): SenMLPackTemplate(baseName, SENML_UNIT_NONE, NAN) {};
+        SenMLDoublePack(const char* baseName, SenMLUnit baseUnit): SenMLPackTemplate(baseName, baseUnit, NAN) {};
+        SenMLDoublePack(const char* baseName, SenMLUnit baseUnit, double baseTime): SenMLPackTemplate(baseName, baseUnit, baseTime) {};
+
+        SenMLDoublePack(PACK_ACTUATOR_SIGNATURE): SenMLPackTemplate("", SENML_UNIT_NONE, NAN, callback) {};
+        SenMLDoublePack(const char* baseName, PACK_ACTUATOR_SIGNATURE): SenMLPackTemplate(baseName, SENML_UNIT_NONE, NAN, callback) {};
+        SenMLDoublePack(const char* baseName, SenMLUnit baseUnit, PACK_ACTUATOR_SIGNATURE): SenMLPackTemplate(baseName, baseUnit, NAN, callback) {};
+        SenMLDoublePack(const char* baseName, SenMLUnit baseUnit, double baseTime, PACK_ACTUATOR_SIGNATURE): SenMLPackTemplate(baseName, baseUnit, baseTime, callback){};
+
+
+
+        ~SenMLDoublePack(){};
+
+        /**
+         * renders all the fields to json, without the starting and ending brackets.
+         * Inheriters can extend this function if they want to add extra fields to the json output
+         * note: this is public so that custom implementations for the record object can use other objects 
+         * internally and render to json using this function (ex: coordinatesRecord using 3 floatRecrods for lat, lon & alt.
+         * @returns: None
+        */
+        virtual void fieldsToJson();
+
+        /**
+         * renders all the fields to cbor format. renders all the fields of the object without the length info 
+         * at the beginning
+         * note: this is public so that custom implementations for the record object can use other objects 
+         * internally and render to json using this function (ex: coordinatesRecord using 3 floatRecrods for 
+         * lat, lon & alt.
+         * @returns: The number of bytes that were written.
+        */
+        virtual int fieldsToCbor();
+
+    protected:
+
+        virtual void setupStreamCtx(Stream *dest, SenMLStreamMethod format);
+        virtual void setupStreamCtx(char *dest, int length, SenMLStreamMethod format);
+
+        
+
+    private:
+};
+
+#endif // SENMLDOUBLEPACK
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_enums.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_enums.cpp	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,78 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * enum strings
+ */
+
+const char* senml_units_names[] = {"", 
+                     "m", 
+					 "kg", 
+					 "g", 
+					 "s", 
+					 "A", 
+					 "K", 
+					 "cd", 
+					 "mol", 
+					 "Hz", 
+					 "rad", 
+					 "sr", 
+					 "N", 
+					 "Pa", 
+					 "J", 
+					 "W", 
+					 "C", 
+					 "V",
+					 "F",
+					 "Ohm",
+					 "S",
+					 "Wb",
+					 "T",
+					 "H",
+					 "Cel",
+					 "lm",
+					 "lx",
+					 "Bq",
+					 "Gy",
+					 "Sv",
+					 "kat",
+					 "m2",
+					 "m3",
+					 "l",
+					 "m/s",
+					 "m/s2",
+					 "m3/s",
+					 "l/s",
+					 "W/m2",
+					 "cd/m2",
+					 "bit",
+					 "bit/s",
+					 "lat",
+					 "lon",
+					 "pH",
+					 "db",
+					 "dBW",
+					 "Bspl",
+					 "count",
+					 "//",
+					 "%RH",
+					 "%EL",
+					 "EL",
+					 "1/s",
+					 "1/min",
+					 "beat/min",
+					 "beats",
+					 "S/m"};
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_enums.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_enums.h	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,137 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * unit and record names
+ */
+
+#ifndef SENMLENUMS
+#define SENMLENUMS
+
+
+/**
+ * Lists all the data types that a value can have in senml.
+ * This is primarely used for actuator callbacks on SenMLPack objects when no appropirate SenMLRecord could be found for the acutator command.
+ */
+enum SenMLDataType { //json data types:
+                    SENML_TYPE_NR, SENML_TYPE_STRING, SENML_TYPE_BOOL, SENML_TYPE_DATA,
+                    //cbor data types, which have already converted the data value
+                    CBOR_TYPE_UINT, CBOR_TYPE_INT, CBOR_TYPE_STRING, CBOR_TYPE_FLOAT, CBOR_TYPE_DOUBLE, CBOR_TYPE_BOOL, CBOR_TYPE_DATA
+                   };
+
+extern const char* senml_units_names[];
+
+/**
+ * Determines how data should be read/rendered to the stream: in the original format
+ * or converted to hex values. The latter is used for serial communication with a modem such as a lora modem.
+ */
+enum SenMLStreamMethod {SENML_HEX, SENML_RAW};
+
+/**
+ * Lists all the units of measurement that are supported by SenML.
+ */
+enum SenMLUnit {SENML_UNIT_NONE, 
+                SENML_UNIT_METER,
+                SENML_UNIT_KILOGRAM,
+                SENML_UNIT_GRAM,
+                SENML_UNIT_SECOND,
+                SENML_UNIT_AMPERE,
+                SENML_UNIT_KELVIN,                             
+                SENML_UNIT_CANDELA,                            
+                SENML_UNIT_MOLE,                               
+                SENML_UNIT_HERTZ,                              
+                SENML_UNIT_RADIAN,                             
+                SENML_UNIT_STERADIAN,                          
+                SENML_UNIT_NEWTON,                             
+                SENML_UNIT_PASCAL,                             
+                SENML_UNIT_JOULE,                              
+                SENML_UNIT_WATT,                               
+                SENML_UNIT_COULOMB,                            
+                SENML_UNIT_VOLT,                             
+                SENML_UNIT_FARAD,                              
+                SENML_UNIT_OHM,                            
+                SENML_UNIT_SIEMENS,
+                SENML_UNIT_WEBER,                            
+                SENML_UNIT_TESLA,                             
+                SENML_UNIT_HENRY,
+                SENML_UNIT_DEGREES_CELSIUS,
+                SENML_UNIT_LUMEN,                              
+                SENML_UNIT_LUX ,                               
+                SENML_UNIT_BECQUEREL,                          
+                SENML_UNIT_GRAY,                               
+                SENML_UNIT_SIEVERT,                            
+                SENML_UNIT_KATAL,                      
+                SENML_UNIT_SQUARE_METER,
+                SENML_UNIT_CUBIC_METER,
+                SENML_UNIT_LITER ,
+                SENML_UNIT_VELOCITY,
+                SENML_UNIT_ACCELERATION,
+                SENML_UNIT_CUBIC_METER_PER_SECOND,
+                SENML_UNIT_LITER_PER_SECOND,
+                SENML_UNIT_WATT_PER_SQUARE_METER,
+                SENML_UNIT_CANDELA_PER_SQUARE_METER,
+                SENML_UNIT_BIT ,
+                SENML_UNIT_BIT_PER_SECOND,
+                SENML_UNIT_DEGREES_LATITUDE,
+                SENML_UNIT_DEGREES_LONGITUDE,
+                SENML_UNIT_PH ,
+                SENML_UNIT_DECIBEL ,
+                SENML_UNIT_DECIBEL_RELATIVE_TO_1_W,
+                SENML_UNIT_BEL ,
+                SENML_UNIT_COUNTER,
+                SENML_UNIT_RATIO ,
+                SENML_UNIT_RELATIVE_HUMIDITY,
+                SENML_UNIT_PERCENTAGE_REMAINING_BATTERY_LEVEL,
+                SENML_UNIT_SECONDS_REMAINING_BATTERY_LEVEL,
+                SENML_UNIT_EVENT_RATE_PER_SECOND,
+                SENML_UNIT_EVENT_RATE_PER_MINUTE,
+                SENML_UNIT_BPM,
+                SENML_UNIT_BEATS,
+                SENML_UNIT_SIEMENS_PER_METER
+                };
+
+
+#define KPN_SENML_PRESSURE "pressure"
+#define KPN_SENML_ANGLE "angle"
+#define KPN_SENML_LENGHT "lenght"
+#define KPN_SENML_BREADTH "breadth"
+#define KPN_SENML_HEIGHT "height"
+#define KPN_SENML_WEIGHT "weight"
+#define KPN_SENML_THICKNESS "thickness"
+#define KPN_SENML_DISTANCE "distance"
+#define KPN_SENML_AREA "area"
+#define KPN_SENML_VOLUME "volume"
+#define KPN_SENML_VELOCITY "velocity"
+#define KPN_SENML_ELECTRICCURRENT "electricCurrent"
+#define KPN_SENML_ELECTRICPOTENTIAL "electricPotential"
+#define KPN_SENML_ELECTRICRESISTANCE "electricResistance"
+#define KPN_SENML_TEMPERATURE "temperature"
+#define KPN_SENML_ILLUMINANCE "illuminance"
+#define KPN_SENML_ALTITUDE "altitude"
+#define KPN_SENML_ACCELERATIONX "accelerationX"
+#define KPN_SENML_ACCELERATIONY "accelerationY"
+#define KPN_SENML_ACCELERATIONZ "accelerationZ"
+#define KPN_SENML_HEADING "heading"
+#define KPN_SENML_LONGITUDE "longitude"
+#define KPN_SENML_LATTITUDE "lattitude"
+#define KPN_SENML_CARBONMONOXIDE "carbonMonoxide"
+#define KPN_SENML_CARBONDIOXIDE "carbonDioxide"
+#define KPN_SENML_SOUND "sound"
+#define KPN_SENML_FREQUENCY "frequency"
+#define KPN_SENML_BATTERYLEVEL "batteryLevel"
+#define KPN_SENML_HUMIDITY "humidity"
+
+#endif // !SENMLENUMS
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_float_actuator.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_float_actuator.cpp	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,38 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * support for float actuators
+ */
+
+#include <senml_float_actuator.h>
+#include <senml_logging.h>
+
+void SenMLFloatActuator::actuate(const void* value, int dataLength, SenMLDataType dataType)
+{
+    if(dataType == SENML_TYPE_NR || dataType == CBOR_TYPE_DOUBLE){
+        this->set((float)*((double*)value));
+        if(this->callback)
+            this->callback((float)*((double*)value));
+    }
+    else if(dataType == CBOR_TYPE_FLOAT){
+        this->set(*((float*)value));
+        if(this->callback)
+            this->callback(*((float*)value));
+    }
+    else
+        log_debug("invalid type");
+}
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_float_actuator.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_float_actuator.h	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,48 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * support for float actuators headers
+ */
+
+#ifndef SENMLFLOATACTUATOR
+#define SENMLFLOATACTUATOR
+
+#include <senml_float_record.h>
+
+#define FLOAT_ACTUATOR_SIGNATURE void (*callback)(float)
+
+/**
+ * A SenMLRecord that stores float data and supports actuation.
+ */ 
+class SenMLFloatActuator: public SenMLFloatRecord
+{
+public:
+    SenMLFloatActuator(const char* name, FLOAT_ACTUATOR_SIGNATURE): SenMLFloatRecord(name, SENML_UNIT_NONE, 0.0), callback(callback) {};
+    SenMLFloatActuator(const char* name, SenMLUnit unit, FLOAT_ACTUATOR_SIGNATURE): SenMLFloatRecord(name, unit, 0.0), callback(callback) {};
+    SenMLFloatActuator(const char* name, SenMLUnit unit, float value, FLOAT_ACTUATOR_SIGNATURE):  SenMLFloatRecord(name, unit, value), callback(callback) {};
+    ~SenMLFloatActuator(){};
+
+protected:
+
+    //called while parsing a senml message, when the parser found the value for an SenMLJsonListener
+    virtual void actuate(const void* value, int dataLength, SenMLDataType dataType);
+
+private:
+    FLOAT_ACTUATOR_SIGNATURE;
+};
+
+#endif // SENMLFLOATACTUATOR
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_float_record.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_float_record.cpp	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,73 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * support for float sensors
+ */
+
+
+#include <senml_float_record.h>
+#include <senml_helpers.h>
+#include <cbor.h>
+#include <senml_double_pack.h> 
+#include <senml_int_pack.h> 
+
+SenMLFloatRecord::SenMLFloatRecord(const char* name): SenMLRecordTemplate(name)
+{
+}
+
+SenMLFloatRecord::SenMLFloatRecord(const char* name, SenMLUnit unit): SenMLRecordTemplate(name, unit)
+{
+}
+
+float SenMLFloatRecord::getAdjustedValue()
+{
+    float adjustedValue = this->get();
+    if(_streamCtx->baseDataType == CBOR_TYPE_DOUBLE){
+        if(this->asSum())
+            adjustedValue -= ((SenMLDoublePack*)this->getRoot())->getBaseSum();
+        else
+            adjustedValue -= ((SenMLDoublePack*)this->getRoot())->getBaseValue();
+    }
+    else if( _streamCtx->baseDataType == CBOR_TYPE_INT){
+        if(this->asSum())
+            adjustedValue -= ((SenMLIntPack*)this->getRoot())->getBaseSum();
+        else
+            adjustedValue -= ((SenMLIntPack*)this->getRoot())->getBaseValue();
+    }
+    return adjustedValue;
+}
+
+void SenMLFloatRecord::fieldsToJson()
+{
+    SenMLRecord::fieldsToJson();
+    if(this->asSum())
+        printText(",\"s\":", 5);
+    else
+        printText(",\"v\":", 5);
+    printDouble(this->getAdjustedValue(), SENML_MAX_DOUBLE_PRECISION);
+}
+
+int SenMLFloatRecord::fieldsToCbor()
+{
+    int res = SenMLRecord::fieldsToCbor();
+    if(this->asSum())
+        res += cbor_serialize_int(SENML_CBOR_S_LABEL);
+    else
+        res += cbor_serialize_int(SENML_CBOR_V_LABEL);
+    res += cbor_serialize_double(this->getAdjustedValue());
+    return res;
+}
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_float_record.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_float_record.h	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,64 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * support for float sensors header
+ */
+
+#ifndef SENMLFLOATRECORD
+#define SENMLFLOATRECORD
+
+#include <senml_record_t.h>
+
+/**
+ * A SenMLRecord that stores float data.
+ * This type of object can only be used for sensor data. If actuation is needed, use SenMLFloatActuator
+ * instead.
+ */ 
+class SenMLFloatRecord: public SenMLRecordTemplate<float>
+{
+public:
+    SenMLFloatRecord(const char* name);
+    SenMLFloatRecord(const char* name, SenMLUnit unit);
+    SenMLFloatRecord(const char* name, SenMLUnit unit, float value):  SenMLRecordTemplate(name, unit, value){};
+    ~SenMLFloatRecord(){};
+
+    /**
+     * renders all the fields to json, without the starting and ending brackets.
+     * Inheriters can extend this function if they want to add extra fields to the json output
+     * note: this is public so that custom implementations for the record object can use other objects 
+     * internally and render to json using this function (ex: coordinatesRecord using 3 floatRecrods for lat, lon & alt.
+     * @returns: None
+    */
+    virtual void fieldsToJson();
+
+    /**
+     * renders all the fields to cbor format. renders all the fields of the object without the length info 
+     * at the beginning
+     * note: this is public so that custom implementations for the record object can use other objects 
+     * internally and render to json using this function (ex: coordinatesRecord using 3 floatRecrods for 
+     * lat, lon & alt.
+     * @returns: The number of bytes that were written.
+    */
+    virtual int fieldsToCbor();
+protected:
+    
+
+private:
+    float getAdjustedValue();
+};
+
+#endif // SENMLFLOATRECORD
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_helpers.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_helpers.cpp	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,196 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * felper functions
+ */
+
+#include <senml_helpers.h>
+
+#ifdef ESP32
+    #include <base64.h>
+    #include <arduino.h>                        //needed for sprintf
+#elif __MBED__
+    #include <base64.h>
+    int base64_enc_len(int plainLen) {
+	    int n = plainLen;
+	    return (n + 2 - ((n + 2) % 3)) / 3 * 4;
+    }
+#else
+    #include <Base64.h>
+#endif
+
+
+
+//global reference to the stream and stream configuration. This should save us memory
+//by not having to pass these values continuously on the stack.
+StreamContext* _streamCtx = NULL;
+
+void printDouble(double f, unsigned int digits) 
+{
+    #ifdef __MBED__
+        char s[30];
+        sprintf(s, "%f", f);
+        printText(s, strlen(s));
+    #else
+        String temp(f, digits);
+        const char* s = temp.c_str();
+        int length = temp.length();
+
+        for(int i = length -1; i >0; i--){                              //remove unwanted trailing 0
+            if(s[i] != '0'){
+                length = i;
+                if(s[i] == '.')                                         //if we end on something like x.  then add a last 0, so that it becomes x.0
+                    length++;
+                break;
+            }
+        }
+        printText(s, length + 1);
+    #endif
+} 
+
+
+
+void printBinaryAsBase64(const unsigned char* data, unsigned int length)
+{
+    #ifdef ESP32
+        String encoded = base64::encode((uint8_t*)data, length);
+        printText(encoded.c_str(), encoded.length());
+    #else
+        int encodedLen = base64_enc_len(length);
+        char encoded[encodedLen];
+        
+        #ifdef __MBED__
+            // todo: check result of function
+            size_t olen;
+            mbedtls_base64_encode((unsigned char*)encoded, encodedLen, &olen, data, length);
+        #else
+            // note input is consumed in this step: it will be empty afterwards
+            base64_encode(encoded, (char*)data, length); 
+        #endif
+        printText(encoded, encodedLen);
+    #endif
+}
+
+void printUnit(SenMLUnit unit)
+{
+    if(unit != SENML_UNIT_NONE)
+        printText(senml_units_names[unit], strlen(senml_units_names[unit]));
+}
+
+void printText(const char* value, int len)
+{
+    char hexTable[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+    if(_streamCtx->dataAsBlob){
+        if(_streamCtx->format == SENML_RAW){
+            for(int i = 0; i < len; i++){
+                if(_streamCtx->data.blob.curPos >= _streamCtx->data.blob.length) return;            //if we reached the end of the buffer, stop rendering otherwise we overwrite some other mem which is not good.
+                _streamCtx->data.blob.data[_streamCtx->data.blob.curPos++] = value[i];
+            }
+        }
+        else{
+            for(int i = 0; i < len; i++){
+                if(_streamCtx->data.blob.curPos >= _streamCtx->data.blob.length) return;            //if we reached the end of the buffer, stop rendering otherwise we overwrite some other mem which is not good.
+                _streamCtx->data.blob.data[_streamCtx->data.blob.curPos++] = hexTable[value[i] / 16];
+                _streamCtx->data.blob.data[_streamCtx->data.blob.curPos++] = hexTable[value[i] % 16];
+            }
+        }
+    }
+    else{
+        if(_streamCtx->format == SENML_RAW){
+            #ifdef __MBED__
+                for(int i = 0; i < len; i++)
+                    _streamCtx->data.stream->putc(value[i]);
+            #else
+                _streamCtx->data.stream->write(value, len);
+            #endif
+        }
+        else if (_streamCtx->format == SENML_HEX){
+            for(int i = 0; i< len; i++){
+                #ifdef __MBED__
+                    _streamCtx->data.stream->putc(hexTable[value[i] / 16]);
+                    _streamCtx->data.stream->putc(hexTable[value[i] % 16]);
+                #else
+                    _streamCtx->data.stream->print(hexTable[value[i] / 16]);
+                    _streamCtx->data.stream->print(hexTable[value[i] % 16]);
+                #endif
+            }
+        }
+    }
+}
+
+static bool peeked = false;                     //if we peek the stream in HEX format, we actually need to read 2 bytes, so the peek doesn't work, but we need to read the actual value.
+static int peekVal = 0;                        //these values are removed by the compiler if no cbor is used.
+
+int readChar(){
+    if(peeked == true){
+        peeked = false;
+        return peekVal;
+    }
+    int res;
+    if(_streamCtx->dataAsBlob){
+		if(_streamCtx->data.blob.curPos < _streamCtx->data.blob.length)         //peekchar has to return -1 if there is no more data, so check for this.
+			res = _streamCtx->data.blob.data[_streamCtx->data.blob.curPos++];
+		else
+			return -1;
+        if(_streamCtx->format == SENML_HEX){
+            int resB = _streamCtx->data.blob.data[_streamCtx->data.blob.curPos++];
+            res = (res > '9')? (res &~ 0x20) - 'A' + 10: (res - '0');
+	        resB = (resB > '9')? (resB &~ 0x20) - 'A' + 10: (resB - '0');
+            res = (res * 16) + resB;
+        }
+	} 
+	else {                                                                      //data is comming from the stream, this already returns -1 if there is no data.
+        if(_streamCtx->format == SENML_RAW){
+            #ifdef __MBED__
+                res = _streamCtx->data.stream->getc();
+            #else
+                res = _streamCtx->data.stream->available() ? _streamCtx->data.stream->read() : -1;                          //arduino stream, check if something is available, if not, we can't read anymore.
+            #endif
+        }
+        else{
+            int resB;
+            #ifdef __MBED__
+                res = _streamCtx->data.stream->getc();
+                resB = _streamCtx->data.stream->getc();
+            #else
+              
+                if(_streamCtx->data.stream->available())
+                    res = _streamCtx->data.stream->read();
+                else
+                    return -1;
+                if(_streamCtx->data.stream->available())
+                    resB = _streamCtx->data.stream->read();
+                else
+                    return -1;
+            #endif
+            res = (res > '9')? (res &~ 0x20) - 'A' + 10: (res - '0');
+            resB = (resB > '9')? (resB &~ 0x20) - 'A' + 10: (resB - '0');
+            res = (res * 16) + resB;
+        }
+	}
+    return res;
+};
+
+int peekChar(){
+    if(peeked == true)                                                              //if already peeked, return the currently buffered value.
+        return peekVal;
+    peekVal = readChar();
+    if(peekVal != -1)                                                               //if no more data, don't try to buffer it, some new data might arrive later on
+        peeked = true;
+    return peekVal;
+};
+
+void flush(){
+    peeked = false;
+}
+
+
+
diff -r 000000000000 -r a9259748d982 senml_helpers.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_helpers.h	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,167 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * felper functions header
+ */
+
+#ifndef SENMLHELPERS
+#define SENMLHELPERS
+
+#ifdef __MBED__
+    #include "mbed.h"
+    #include "sstream"
+#else
+    #include <stream.h>
+#endif
+#include <senml_enums.h>
+#include <math.h> 
+
+#ifndef SENML_MAX_DOUBLE_PRECISION
+#define SENML_MAX_DOUBLE_PRECISION 4
+#endif // !SENML_MAX_DOUBLE_PRECISION
+
+/** 
+ * Internal helper struct that stores the base value to be used during rendering
+ */
+typedef union BaseData_t{
+    uint64_t baseUint;
+    int64_t baseInt;
+    double baseDouble;
+} BaseData;
+
+/**
+ * Internal helper struct that stores information needed during the rendering process
+ */
+typedef struct SenmlMemoryData_t{
+    char* data;                             //pointer to the buffer that constitutes the input/output buffer
+    int curPos;                             //current position in the buffer.
+    int length;                             //length of the memory buffer.
+} SenmlMemoryData;                          //a record used when the input for the parser or output for the renderer is a data blob stored in memory
+
+/**
+ *  Internal helper struct that stores information needed during the rendering process
+ */
+typedef union SenmlData_t{
+    Stream* stream;
+    SenmlMemoryData blob;
+} SenmlData;                                //choose between input/outpu from a stream object (ex: direct input from uart) or from buffered data.
+
+/**
+ * Internal helper struct that stores information needed during the rendering process
+ */
+typedef struct StreamContext_t{
+    bool dataAsBlob;                        //when true, data is from a memory blob, otherwise it comes from/goes to a stream object (ex: direct from/to uart)
+    SenmlData data;                         //the data source to be parsed or destination to render to. can be from a stream or from data buffered in memory
+    SenMLStreamMethod format;
+    BaseData baseValue;                     //filled in when records need to adjust while rendering data.
+    BaseData baseSum;
+    SenMLDataType baseDataType;             //so we know which data type to use for baseValue and baseSum
+} StreamContext;
+
+/**
+ * Internal data field used for the rendering process.
+ */
+extern StreamContext* _streamCtx;
+
+/** 
+ * Helper function for the generation process
+ * write a double as string to the stream (_streamCtx).
+ * @param f the value to print 
+ * @param digits the nr of digits that should be printed after the comma.
+ */
+void printDouble(double f, unsigned int digits) ;
+
+/**
+ * Helper function for the generation process
+ * convert the data array to base64 string and write to stream (_streamCtx)
+ * @param data pointer to the binary data blob that needs to be rendered as base64 to the stream.
+ * @param length the length of the data blob
+ */
+void printBinaryAsBase64(const unsigned char* data, unsigned int length);
+
+/**
+ * Helper function for the generation process
+ * convert the unit to string and write to stream (_streamCtx).
+ * @param unit the value to write as text to the stream.
+ */
+void printUnit(SenMLUnit unit);
+
+/**
+ * Helper function for the generation process
+ * Writes the specified text to the stream (_streamCtx).
+ * @param value the value to write
+ * @param len the length of the value.
+ */
+void printText(const char*value, int len);
+
+
+
+/**
+ * Helper function for the parsing process
+ * read a character from the current data stream (_streamCtx) 
+ * takes into account that the stream might contain hex values
+ */
+int readChar();
+
+/**
+ * Helper function for the parsing process
+ * peek a character from the current data stream.
+ * takes into account that the stream might contain hex values.
+ */
+int peekChar();
+
+/**
+ * Helper function for the parsing process
+ * flush and reset all input for the current data stream.
+ */
+void flush();
+
+/**
+ * Helper function for the parsing process
+ * Reads the specified nr of characters from the stream (_streamCtx)
+ * takes into account that the stream might contain hex values
+ * @param buffer the buffer to store the values in.
+ * @param len the nr of characters to read from the stream into the buffer. 
+ *        If the input contains hex values, the actual nr of characters read from the
+ *        stream is double this value.
+ */
+inline void readChars(unsigned char* buffer, int len)
+{
+    for(int i = 0; i < len; i++){
+        buffer[i] = readChar();
+    }
+}
+
+/**
+ * Helper function for the parsing process
+ * Checks if there is data available on the stream.
+* Warning: THIS CAN BE PROBLEMATIC ON MBED SYSTEMS>
+ */
+inline bool charAvailable(){
+    if(_streamCtx->dataAsBlob){
+        return _streamCtx->data.blob.curPos < _streamCtx->data.blob.length;
+    }
+    else{
+        #ifdef __MBED__
+            return _streamCtx->data.stream->readable() != 0;      
+        #else  
+            return _streamCtx->data.stream->available() != 0; 
+        #endif
+    }
+}
+
+#endif // SENMLHELPERS
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_int_actuator.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_int_actuator.cpp	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,45 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * support for int actuators
+ */
+
+#include <senml_int_actuator.h>
+#include <senml_logging.h>
+
+void SenMLIntActuator::actuate(const void* value, int dataLength, SenMLDataType dataType)
+{
+    if(dataType == SENML_TYPE_NR){
+        this->set((int)*((double*)value));
+        if(this->callback)
+            this->callback((int)*((double*)value));
+    }
+    else if(dataType == CBOR_TYPE_INT){
+        int64_t val = *((int64_t*)value);
+        this->set((int)val);
+        if(this->callback)
+            this->callback((int)val);
+    }
+    else if(dataType == CBOR_TYPE_UINT){
+        uint64_t val = *((uint64_t*)value);
+        this->set((int)val);
+        if(this->callback)
+            this->callback((int)val);
+    }
+    else
+        log_debug("invalid type");
+}
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_int_actuator.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_int_actuator.h	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,50 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * support for int actuators headers
+ */
+
+#ifndef SENMLINTACTUATOR
+#define SENMLINTACTUATOR
+
+#include <senml_int_record.h>
+
+#define INT_ACTUATOR_SIGNATURE void (*callback)(int)
+
+/**
+ * A SenMLRecord that stores integer data and supports actuation.
+ */ 
+class SenMLIntActuator: public SenMLIntRecord
+{
+public:
+    SenMLIntActuator(const char* name, INT_ACTUATOR_SIGNATURE): SenMLIntRecord(name, SENML_UNIT_NONE, 0), callback(callback) {};
+    SenMLIntActuator(const char* name, SenMLUnit unit, INT_ACTUATOR_SIGNATURE): SenMLIntRecord(name, unit, 0), callback(callback) {};
+    SenMLIntActuator(const char* name, SenMLUnit unit, int value, INT_ACTUATOR_SIGNATURE):  SenMLIntRecord(name, unit, value), callback(callback) {};
+    ~SenMLIntActuator(){};
+
+protected:
+
+    /**
+     * called while parsing a senml message, when the parser found the value for an SenMLJsonListener
+     */
+    virtual void actuate(const void* value, int dataLength, SenMLDataType dataType);
+
+private:
+    INT_ACTUATOR_SIGNATURE;
+};
+
+#endif // SENMLINTACTUATOR
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_int_pack.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_int_pack.cpp	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,81 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * pack (document) with int base values
+ */
+
+
+#include <senml_int_pack.h> 
+#include <senml_helpers.h>
+#include <cbor.h>
+ 
+
+void SenMLIntPack::setupStreamCtx(Stream *dest, SenMLStreamMethod format)
+{
+    SenMLPack::setupStreamCtx(dest, format);
+    _streamCtx->baseValue.baseInt = this->getBaseValue();
+    _streamCtx->baseSum.baseInt = this->getBaseValue();
+    _streamCtx->baseDataType = CBOR_TYPE_INT;
+}
+
+void SenMLIntPack::setupStreamCtx(char *dest, int length, SenMLStreamMethod format)
+{
+    SenMLPack::setupStreamCtx(dest, length, format);
+    _streamCtx->baseValue.baseInt = this->getBaseValue();
+    _streamCtx->baseSum.baseInt = this->getBaseValue();
+    _streamCtx->baseDataType = CBOR_TYPE_INT;
+}
+
+void SenMLIntPack::fieldsToJson() 
+{
+    int val;
+    String strVal;
+    SenMLPack::fieldsToJson();
+    val = this->getBaseValue();
+    if(val != 0){
+        printText(",\"bv\":", 6);
+        strVal = val;
+        printText(strVal.c_str(), strVal.length());
+    }
+
+    val = this->getBaseSum();
+    if(val != 0){
+        printText(",\"bs\":", 6);
+        strVal = val;
+        printText(strVal.c_str(), strVal.length());
+    }
+
+}
+
+int SenMLIntPack::fieldsToCbor() 
+{
+    int val;
+    int res = SenMLPack::fieldsToCbor();
+    
+    val = this->getBaseValue();
+    if(val){
+        res += cbor_serialize_int(SENML_CBOR_VB_LABEL);
+        res += cbor_serialize_int(val);
+    }
+
+    val = this->getBaseSum();
+    if(val){
+        res += cbor_serialize_int(SENML_CBOR_BS_LABEL);
+        res += cbor_serialize_int(val);
+    }
+    return res;
+}
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_int_pack.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_int_pack.h	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,76 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * pack (document) with int base values header
+ */
+
+#ifndef SENMLINTPACK
+#define SENMLINTPACK
+
+#include <senml_pack_t.h>
+
+/**
+ * An implimentation of the SenMLPack template that stores integer base value and base-sum.
+ */
+class SenMLIntPack: public SenMLPackTemplate<int>
+{
+    public:
+
+        SenMLIntPack(const char* baseName): SenMLPackTemplate(baseName, SENML_UNIT_NONE, NAN) {};
+        SenMLIntPack(const char* baseName, SenMLUnit baseUnit): SenMLPackTemplate(baseName, baseUnit, NAN) {};
+        SenMLIntPack(const char* baseName, SenMLUnit baseUnit, double baseTime): SenMLPackTemplate(baseName, baseUnit, baseTime) {};
+
+        SenMLIntPack(PACK_ACTUATOR_SIGNATURE): SenMLPackTemplate("", SENML_UNIT_NONE, NAN, callback) {};
+        SenMLIntPack(const char* baseName, PACK_ACTUATOR_SIGNATURE): SenMLPackTemplate(baseName, SENML_UNIT_NONE, NAN, callback) {};
+        SenMLIntPack(const char* baseName, SenMLUnit baseUnit, PACK_ACTUATOR_SIGNATURE): SenMLPackTemplate(baseName, baseUnit, NAN, callback) {};
+        SenMLIntPack(const char* baseName, SenMLUnit baseUnit, double baseTime, PACK_ACTUATOR_SIGNATURE): SenMLPackTemplate(baseName, baseUnit, baseTime, callback){};
+
+
+
+        ~SenMLIntPack(){};
+
+        /**
+         * renders all the fields to json, without the starting and ending brackets.
+         * Inheriters can extend this function if they want to add extra fields to the json output
+         * note: this is public so that custom implementations for the record object can use other objects 
+         * internally and render to json using this function (ex: coordinatesRecord using 3 floatRecrods for lat, lon & alt.
+         * @returns: None
+        */
+        virtual void fieldsToJson();
+
+        /**
+         * renders all the fields to cbor format. renders all the fields of the object without the length info 
+         * at the beginning
+         * note: this is public so that custom implementations for the record object can use other objects 
+         * internally and render to json using this function (ex: coordinatesRecord using 3 floatRecrods for 
+         * lat, lon & alt.
+         * @returns: The number of bytes that were written.
+        */
+        virtual int fieldsToCbor();
+
+    protected:
+
+        virtual void setupStreamCtx(Stream *dest, SenMLStreamMethod format);
+        virtual void setupStreamCtx(char *dest, int length, SenMLStreamMethod format);
+
+        
+
+    private:
+        int getAdjustedValue();
+};
+
+#endif // SENMLINTPACK
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_int_record.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_int_record.cpp	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,98 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * support for int sensors
+ */
+
+#include <senml_int_record.h>
+#include <cbor.h>
+#include <senml_helpers.h>
+#include <senml_int_pack.h> 
+#include <senml_double_pack.h> 
+
+SenMLIntRecord::SenMLIntRecord(const char* name): SenMLRecordTemplate(name)
+{
+}
+
+SenMLIntRecord::SenMLIntRecord(const char* name, SenMLUnit unit): SenMLRecordTemplate(name, unit)
+{
+}
+
+int SenMLIntRecord::getAdjustedValue()
+{
+    int adjustedValue = this->get();
+    if(_streamCtx->baseDataType == CBOR_TYPE_INT){
+        if(this->asSum())
+            adjustedValue -= ((SenMLIntPack*)this->getRoot())->getBaseSum();
+        else
+            adjustedValue -= ((SenMLIntPack*)this->getRoot())->getBaseValue();
+    }
+    return adjustedValue;
+}
+
+double SenMLIntRecord::getAdjustedValueD()
+{
+    double adjustedValue = this->get();
+    if(_streamCtx->baseDataType == CBOR_TYPE_DOUBLE){
+        if(this->asSum())
+            adjustedValue -= ((SenMLDoublePack*)this->getRoot())->getBaseSum();
+        else
+            adjustedValue -= ((SenMLDoublePack*)this->getRoot())->getBaseValue();
+    }
+    return adjustedValue;
+}
+
+void SenMLIntRecord::fieldsToJson()
+{
+    SenMLRecord::fieldsToJson();
+    if(this->asSum())
+        printText(",\"s\":", 5);
+    else
+        printText(",\"v\":", 5);
+
+    //if the parent pack has a base value or base sum of type double, then we need to render a double value.
+    if(_streamCtx->baseDataType == CBOR_TYPE_INT || _streamCtx->baseDataType == CBOR_TYPE_DATA){
+        #ifdef __MBED__
+            char buf[10];
+            sprintf(buf, "%d", this->getAdjustedValue());
+            String val = buf;
+        #else
+            String val(this->getAdjustedValue());
+        #endif
+        printText(val.c_str(), val.length());
+    }
+    else{
+        printDouble(this->getAdjustedValueD(), SENML_MAX_DOUBLE_PRECISION);
+    }
+}
+
+
+int SenMLIntRecord::fieldsToCbor()
+{
+    int res = SenMLRecord::fieldsToCbor();
+    if(this->asSum())
+        res += cbor_serialize_int(SENML_CBOR_S_LABEL);
+    else
+        res += cbor_serialize_int(SENML_CBOR_V_LABEL);
+
+    //if the parent pack has a base value or base sum of type double, then we need to render a double value.
+    if(_streamCtx->baseDataType == CBOR_TYPE_INT || _streamCtx->baseDataType == CBOR_TYPE_DATA)        
+        res += cbor_serialize_int(this->getAdjustedValue());
+    else
+        res += cbor_serialize_double(this->getAdjustedValueD());
+    return res;
+}
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_int_record.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_int_record.h	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,67 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * support for int sensors header
+ */
+
+#ifndef SENMLINTRECORD
+#define SENMLINTRECORD
+
+#include <senml_record_t.h>
+
+/**
+ * A SenMLRecord that stores integer data.
+ * This type of object can only be used for sensor data. If actuation is needed, use SenMLIntActuator
+ * instead.
+ */ 
+class SenMLIntRecord: public SenMLRecordTemplate<int>
+{
+public:
+    SenMLIntRecord(const char* name);
+    SenMLIntRecord(const char* name, SenMLUnit unit);
+    SenMLIntRecord(const char* name, SenMLUnit unit, int value):  SenMLRecordTemplate(name, unit, value){};
+    ~SenMLIntRecord(){};
+
+    /**
+     * renders all the fields to json, without the starting and ending brackets.
+     * Inheriters can extend this function if they want to add extra fields to the json output
+     * note: this is public so that custom implementations for the record object can use other objects 
+     * internally and render to json using this function (ex: coordinatesRecord using 3 floatRecrods for lat, lon & alt.
+     * @returns: None
+    */
+    virtual void fieldsToJson();
+
+    /**
+     * renders all the fields to cbor format. renders all the fields of the object without the length info 
+     * at the beginning
+     * note: this is public so that custom implementations for the record object can use other objects 
+     * internally and render to json using this function (ex: coordinatesRecord using 3 floatRecrods for 
+     * lat, lon & alt.
+     * @returns: The number of bytes that were written.
+    */
+    virtual int fieldsToCbor();
+
+protected:
+
+    
+
+private:
+    int getAdjustedValue();
+    double getAdjustedValueD();
+};
+
+#endif // SENMLINTRECORD
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_json_parser.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_json_parser.cpp	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,140 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * parse json
+ */
+
+#include "senml_json_parser.h"
+#include "senml_helpers.h"
+#include "senml_JsonListener.h"
+
+#define NONE 0
+#define BN_VALUE 1
+#define BU_VALUE 2
+#define N_VALUE 3
+#define V_VALUE 4
+#define VB_VALUE 5
+#define VD_VALUE 6
+#define VS_VALUE 7
+#define BV_VALUE 8
+
+
+#ifdef __MBED__
+    #define KEY1 key[1]    
+#else
+    #define KEY1 key.charAt(1)   
+#endif
+
+
+void SenMLJsonListener::key(String key) {
+    //Serial.println("key: " + key);
+    this->expected = NONE;
+    if(key.length() > 0){                  //using faseter char comare instead of string compare
+        #ifdef __MBED__
+            char first = key[0];    
+        #else
+            char first = key.charAt(0);    
+        #endif
+        switch (first)
+        {
+            case 'b':
+                if(key.length() == 2){
+                    switch (KEY1)
+                    {
+                        case 'n': this->expected = BN_VALUE; break;
+                        case 'u': this->expected = BU_VALUE; break;
+                        case 'v': this->expected = BV_VALUE; break;
+                    }
+                }
+                break;
+            case 'n':
+                this->expected = N_VALUE; break;
+            case 'v':
+                if(key.length() == 2){
+                    switch (KEY1)
+                    {
+                        case 'b': this->expected = VB_VALUE; break;
+                        case 'd': this->expected = VD_VALUE; break;
+                        case 's': this->expected = VS_VALUE; break;
+                    }
+                }
+                else if(key.length() == 1)
+                    this->expected = V_VALUE;
+                break;
+        }
+    }
+  
+}
+
+void SenMLJsonListener::value(String value) {
+    double dblVal;
+    bool boolVal;
+    switch (this->expected)
+    {
+        case BV_VALUE: this->baseValue = atof(value.c_str());
+        case BN_VALUE: this->setCurrentPack(value); break;
+        case BU_VALUE: this->checkBaseUnit(value); break;
+        case N_VALUE: this->setCurrentRecord(value); break;
+        case V_VALUE: 
+            dblVal = atof(value.c_str()) + this->baseValue;
+            this->setValue(&dblVal, sizeof(double), SENML_TYPE_NR); 
+            break;
+        case VB_VALUE: 
+            boolVal = strcmp(value.c_str(), "true") == 0;
+            this->setValue(&boolVal, sizeof(bool), SENML_TYPE_BOOL); 
+            break;
+        case VD_VALUE: this->setValue(value.c_str(), value.length(), SENML_TYPE_DATA); break;
+        case VS_VALUE: this->setValue(value.c_str(), value.length(), SENML_TYPE_STRING); break;
+  }
+}
+
+void SenMLJsonListener::setValue(const void* value, int length, SenMLDataType dataType)
+{
+    if(this->curRec){
+        this->curRec->actuate(value, length, dataType);
+    }
+    else {
+        SenMLPack* pack = this->curPack;
+        if(!pack)
+            pack = this->root;
+        if(pack)
+            pack->actuate(this->curPackName.c_str(), this->curRecName.c_str(), value, length, dataType);
+    }
+}
+
+/*
+
+void SenMLJsonListener::startDocument() {
+    Serial.println("start document");
+}
+
+void SenMLJsonListener::endArray() {
+  Serial.println("end array. ");
+}
+
+void SenMLJsonListener::endDocument() {
+  Serial.println("end document. ");
+}
+
+void SenMLJsonListener::startArray() {
+   Serial.println("start array. ");
+}
+
+void SenMLJsonListener::startObject() {
+   Serial.println("start object. ");
+}
+*/
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_json_parser.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_json_parser.h	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,56 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * parse json header
+ */
+
+#ifndef SENMLSenMLJsonListener
+#define SENMLSenMLJsonListener
+
+#include "senml_JsonListener.h"
+#include <senml_base_parser.h>
+
+/**
+ * Internal helper class for parsing json data.
+ */
+class SenMLJsonListener: public JsonListener, SenMLBaseParser {
+
+  public:
+    SenMLJsonListener(SenMLPack* root): JsonListener(), SenMLBaseParser(root) {};
+
+
+    virtual void key(String key);
+    virtual void value(String value);
+    //virtual void endObject(){};
+
+    //the following need to be implemented cause they are abstract in the base class.
+    //virtual void startDocument(){};
+    //virtual void endArray(){};
+    //virtual void endDocument(){};
+    //virtual void startArray(){};
+    //virtual void startObject(){};
+
+
+
+  private:
+    int expected;               //name of key who's value is expected next
+    double baseValue;           //if the current pack defined a base value, add it to all values that we find
+
+    void setValue(const void* value, int length, SenMLDataType dataType);
+};
+
+#endif // SENMLSenMLJsonListener
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_logging.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_logging.cpp	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,27 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * logging
+ */
+
+#include <senml_logging.h>
+
+Stream* _senml_logger = NULL;
+
+void senMLSetLogger(Stream* logger){
+    _senml_logger = logger;
+};
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_logging.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_logging.h	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,50 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * logging
+ */
+
+#ifndef SENMLLOGGING
+#define SENMLLOGGING
+
+#ifdef __MBED__
+    #include "mbed.h"
+    #include "sstream"
+#else
+    #include <stream.h>
+#endif
+
+//contains a referenc to the global logger object assigned to the module
+extern Stream* _senml_logger;
+
+/**
+ * Assign a stream to the library that can be used to write log information to.
+ * @param logger a stream object that will be used for logging. 
+ * @returns none
+ */
+void senMLSetLogger(Stream* logger);
+
+#ifndef log_debug
+    #ifdef __MBED__
+        #define log_debug(...)  if(_senml_logger) {_senml_logger->printf(__VA_ARGS__); _senml_logger->printf("\n\r"); }
+    #else    
+        #define log_debug(...)  if(_senml_logger) _senml_logger->println(__VA_ARGS__)
+    #endif
+#endif
+
+
+#endif // SENMLLOGGING
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_pack.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_pack.cpp	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,339 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * pack (document) without base values
+ */
+
+#include <senml_pack.h>
+#include <senml_base.h>
+#include <senml_helpers.h>
+#include <senml_json_parser.h>
+#include <senml_cbor_parser.h>
+#include <senml_JsonStreamingParser.h>
+#include <math.h>
+#include <cbor.h>
+#include <senml_logging.h>
+
+
+
+//todo: inline these:
+void SenMLPack::setBaseName(const char* name)
+{
+    this->_bn = name;
+}
+
+const char* SenMLPack::getBaseName()
+{
+    return this->_bn.c_str();
+}
+
+void SenMLPack::setBaseUnit(SenMLUnit unit)
+{
+    this->_bu = unit;
+}
+
+
+void SenMLPack::setBaseTime(double time)
+{
+    double prev = this->_bt;
+    this->_bt = time;                               //set before asking children -> could be better to do it afterwards?
+    SenMLBase *item = this->_start;
+    while(item){
+        item->adjustToBaseTime(prev, time);
+        item = item->getNext();
+    }
+}
+
+
+void SenMLPack::setLast(SenMLBase* value)
+{
+    if(value == this)                           //if we become the last item in the list, then the list is empty.
+        this->_end = NULL;
+    else
+        this->_end = value;
+}
+
+
+bool SenMLPack::add(SenMLBase* item)
+{
+    if(item->getNext() != NULL){
+        log_debug("already in list");
+        return false;
+    }
+
+    SenMLBase* last = this->_end;
+    if(last){
+        last->setNext(item);
+        item->setPrev(last);
+    }
+    else{
+        this->_start = item;
+        item->setPrev(this);
+    }
+    this->_end = item;
+    return true;
+}
+
+bool SenMLPack::clear()
+{
+    SenMLBase *item = this->_start;
+    while(item){
+        if(item->isPack())                                          //if it's a pack element, it also needs to clear out it's children.
+            ((SenMLPack*)item)->clear();
+        item->setPrev(NULL);
+        SenMLBase *next = item->getNext();
+        item->setNext(NULL);
+        item = next;
+    }
+    this->setNext(NULL);
+    this->setPrev(NULL);
+    this->_end = NULL;
+    this->_start = NULL;
+    return true;
+}
+
+void SenMLPack::fromJson(Stream *source, SenMLStreamMethod format)
+{
+    JsonStreamingParser parser;
+    SenMLJsonListener listener(this);
+    
+    parser.setListener(&listener);
+    char data;
+    if(format == SENML_RAW) {
+        #ifdef __MBED__
+            data = source->getc();
+        #else
+            data = source->read();
+        #endif
+    }
+    else{
+        data = readHexChar(source);
+    }
+        
+    while(data != -1){
+        parser.parse(data); 
+        if(format == SENML_RAW){
+            #ifdef __MBED__
+                data = source->getc();
+            #else
+                data = source->read();
+            #endif
+        }   
+        else
+            data = readHexChar(source);
+    }
+    // when we get here, all the data is stored in the document and callbacks have been called.
+}
+
+void SenMLPack::fromJson(const char *source)
+{
+    JsonStreamingParser parser;
+    SenMLJsonListener listener(this);
+    
+    parser.setListener(&listener);
+    for(int i = 0; source[i] != 0; i++){
+        parser.parse(source[i]); 
+    }
+    // when we get here, all the data is stored in the document and callbacks have been called.
+}
+
+void SenMLPack::fromCbor(Stream* source, SenMLStreamMethod format)
+{
+    SenMLCborParser parser(this, format);
+    parser.parse(source);
+}
+
+void SenMLPack::fromCbor(char* source, int length, SenMLStreamMethod format)
+{
+    SenMLCborParser parser(this, format);
+    parser.parse(source, length);
+}
+
+
+void SenMLPack::toJson(Stream *dest, SenMLStreamMethod format)
+{
+    StreamContext renderTo;                                              //set up the global record that configures the rendering. This saves us some bytes on the stack and in code by not having to pass along the values as function arguments.
+    _streamCtx = &renderTo;
+    this->setupStreamCtx(dest, format);
+    this->internalToJson();
+}
+
+void SenMLPack::toJson(char *dest, int length, SenMLStreamMethod format)
+{
+    StreamContext renderTo;                                              //set up the global record that configures the rendering. This saves us some bytes on the stack and in code by not having to pass along the values as function arguments.
+    _streamCtx = &renderTo;
+    this->setupStreamCtx(dest, length, format);
+    this->internalToJson();
+}
+
+//render the content of the current object to json data (string)
+void SenMLPack::internalToJson()
+{
+    printText("[", 1);
+    this->contentToJson();
+    printText("]", 1);
+}
+
+
+void SenMLPack::fieldsToJson()
+{
+    int bnLength = this->_bn.length();
+    if(bnLength > 0){
+        printText("\"bn\":\"", 6);
+        printText(this->_bn.c_str(), bnLength);
+        printText("\"", 1);
+    }
+    if(this->_bu){
+        printText(",\"bu\":\"", 7);
+        printUnit(this->_bu);
+        printText("\"", 1);
+    }
+    if(!isnan(this->_bt)){
+        printText(",\"bt\":", 6);
+        printDouble(this->_bt, SENML_MAX_DOUBLE_PRECISION);
+    }
+}
+
+void SenMLPack::contentToJson()
+{
+    printText("{", 1);
+    this->fieldsToJson();
+    SenMLBase *next = this->_start;
+    if(next && next->isPack() == false){                        //we can only inline the first record. If the first item is a Pack (child device), then don't inline it.
+        printText(",", 1);
+        next->fieldsToJson();
+        next = next->getNext();
+    }
+    printText("}", 1);
+    while(next){
+        printText(",", 1);
+        next->contentToJson();
+        next = next->getNext();
+    }
+}
+
+
+
+void SenMLPack::setupStreamCtx(char *dest, int length, SenMLStreamMethod format)
+{
+    _streamCtx->data.blob.data = dest;
+    _streamCtx->data.blob.length = length;
+    _streamCtx->data.blob.curPos = 0;
+    _streamCtx->dataAsBlob = true;
+    _streamCtx->format = format;
+    _streamCtx->baseValue.baseUint = 0;                                    //by default, there is no base value or sum
+    _streamCtx->baseSum.baseUint = 0;
+    _streamCtx->baseDataType = CBOR_TYPE_DATA;                             //data never adjusts for basevalue, so this is safe.
+}
+
+void SenMLPack::setupStreamCtx(Stream *dest, SenMLStreamMethod format)
+{
+    _streamCtx->data.stream = dest;
+    _streamCtx->format = format;
+    _streamCtx->dataAsBlob = false;
+    _streamCtx->baseValue.baseUint = 0;                                    //by default, there is no base value or sum
+    _streamCtx->baseSum.baseUint = 0;
+    _streamCtx->baseDataType = CBOR_TYPE_DATA;                             //data never adjusts for basevalue, so this is safe.
+}
+
+int SenMLPack::toCbor(Stream *dest, SenMLStreamMethod format)
+{
+    StreamContext renderTo;                                              //set up the global record that configures the rendering. This saves us some bytes on the stack and in code by not having to pass along the values as function arguments.
+    _streamCtx = &renderTo;
+    this->setupStreamCtx(dest, format);
+    int res = cbor_serialize_array(this->getArrayLength());
+    res += this->contentToCbor();
+    return res;
+}
+
+int SenMLPack::toCbor(char *dest, int length, SenMLStreamMethod format)
+{
+    StreamContext renderTo;                                              //set up the global record that configures the rendering. This saves us some bytes on the stack and in code by not having to pass along the values as function arguments.
+    _streamCtx = &renderTo;
+    this->setupStreamCtx(dest, length, format);
+    int res = cbor_serialize_array(this->getArrayLength());
+    res += this->contentToCbor();
+    return res;
+}
+
+int SenMLPack::contentToCbor()
+{
+    int length = cbor_serialize_map(this->getFieldLength());
+
+    int res = this->fieldsToCbor();
+    SenMLBase *next = this->_start;
+    if(next && next->isPack() == false){                        //we can only inline the first record. If the first item is a Pack (child device), then don't inline it.
+        res += next->fieldsToCbor();
+        next = next->getNext();
+    }
+
+    while(next){
+        if(next->isPack() == false)
+            res += next->contentToCbor();
+        else
+            res += ((SenMLPack*)next)->contentToCbor();
+        next = next->getNext();
+    }
+    return res;
+}
+
+int SenMLPack::getArrayLength()
+{
+    int result = 0;                             //init to 0 cause if there is a record, the first will become part of the first element in the array, if we were to init to 1, we would have 1 record too many.
+    SenMLBase *next = this->_start;
+    while(next){                                //we can only inline the first record. If the first item is a Pack (child device), then don't inline it.
+        result += next->getArrayLength();       //custom record implementations may wrap multiple records.
+        next = next->getNext();
+    }
+    if(result == 0)                             //if there are no items in this pack, then we still render 1 array element, that of the pack itself.
+        result = 1;
+    return result;
+}
+
+
+int SenMLPack::getFieldLength()
+{
+    int result = 0;                             
+    if(this->_bn.length() > 0 ) result++;
+    if(this->_bu) result++;
+    if(!isnan(this->_bt)) result++;
+
+    SenMLBase *next = this->_start;
+    if(next && next->isPack() == false){        //we can only inline the first record. If the first item is a Pack (child device), then don't inline it.
+        result += next->getFieldLength();
+        next = next->getNext();
+    }
+    return result;
+}
+
+int SenMLPack::fieldsToCbor()
+{
+    int res = 0 ;
+    if(this->_bn.length() > 0 ){
+        res += cbor_serialize_int(SENML_CBOR_BN_LABEL);
+        res += cbor_serialize_unicode_string(this->_bn.c_str());
+    }
+    if(this->_bu){
+        res += cbor_serialize_int(SENML_CBOR_BU_LABEL);
+        res += cbor_serialize_unicode_string(senml_units_names[this->_bu]);
+    }
+    if(!isnan(this->_bt)){
+        res += cbor_serialize_int(SENML_CBOR_BT_LABEL);
+        res += cbor_serialize_double(this->_bt);
+    }
+    return res;
+}
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_pack.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_pack.h	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,477 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * pack (document) without base values headers
+ */
+
+#ifndef SENMLPACK
+#define SENMLPACK
+
+#ifdef __MBED__
+    #include "mbed.h"
+    #include <string> 
+    using namespace std;
+    #define String string
+#endif
+
+#include <senml_base.h>
+
+
+#define PACK_ACTUATOR_SIGNATURE void (*callback)(const char*, const char*, const void*, int, SenMLDataType)
+
+/** 
+ * SenMLPack represents a single senml document that can be sent or received. 
+ * 
+ * A senmlPack object has SenMLRecords and/or other SenMLPack objects as children. These
+ * represent the data that the object contains.
+ * A SenMLRecord represents a single value, while child SenMLPacks represent data of other 
+ * devices. When a SenMLPack contains other SenMLPack objects, the root object represents a
+ * gateway.
+ * 
+ * A SenMLPack object is able to render and parse the data to/from a json string or to/from binary 
+ * CBOR data. Both rendering and parsing can be done either directly from a stream (like an UART 
+ * connected to a modem), or from a memory buffer. 
+ * Rendering to and parsing from a stream is useful for devices that have extreme low memory available. 
+ * Almost no buffers are used in this case. Error handling is limited in this case though.
+ * The parser and generator are able to render in the native format (strings for json, binary data for
+ * cbor) and as a hex string. This is especially useful when directly working on a stream: some modems 
+ * (ex lora) accept instructions with data in HEX format.
+ * 
+ * example:
+ * 
+ * @code
+ * #include <kpn_senml.h>
+ * 
+ * SenMLPack doc("device_name");
+ * 
+ * void setup(){
+ *     Serial.begin(57600);
+ *     senMLSetLogger(&Serial);
+ *     delay(1000);
+ *     Serial.println("start");
+ * }
+
+ * void loop(){
+ *     int val = 10;                                   //just give it some value
+ *     SenMLFloatRecord rec("temp", SENML_UNIT_DEGREES_CELSIUS, val);
+ *     doc.add(&rec);                      
+ *     doc.toJson(&Serial);                            //as text
+ *     Serial.println();
+ *     doc.toJson(&Serial, SENML_HEX);                 //in hex format (often used in communication with lora modems)
+ *     Serial.println();
+ *     delay(1000);
+ * }
+
+ * @endcode
+ * 
+ */ 
+class SenMLPack: public SenMLBase
+{
+    friend class SenMLJsonListener; 
+    friend class SenMLCborParser;
+    friend class SenMLBase;
+    public:
+
+        /**
+         * create a SenMLPack object.
+         */
+        SenMLPack():  _bu(SENML_UNIT_NONE), 
+                      _bt(NAN),
+                      _end(NULL) {};
+
+        /**
+         * create a SenMLPack object.
+         * @param baseName the string that will be prepended to all records in this pack. 
+         *                 Is used to represent the name of the device.
+         */
+        SenMLPack(const char* baseName):  _bn(baseName),                                                    //mbed compiler doesnt support delegating constructors
+                                          _bu(SENML_UNIT_NONE), 
+                                          _bt(NAN),
+                                          _end(NULL) {};
+
+        /**
+         * create a SenMLPack object.
+         * @param baseName the string that will be prepended to all records in this pack. 
+         *                 Is used to represent the name of the device.
+         * @param baseUnit the unit name that will be used by default if the record doesnt
+         *                 not define one.
+         */                                          
+        SenMLPack(const char* baseName, SenMLUnit baseUnit):  _bn(baseName),                               
+                                                              _bu(baseUnit), 
+                                                              _bt(NAN),
+                                                              _end(NULL) {};
+
+        /**
+         * create a SenMLPack object.
+         * @param baseName the string that will be prepended to all records in this pack. 
+         *                 Is used to represent the name of the device.
+         * @param baseUnit the unit name that will be used by default if the record doesnt
+         *                 not define one.
+         * @param baseTime the time that will be added to each record. When specified, each
+         *                 record that does not specify a time, will receive this time. When
+         *                 the record does have a time, the baseTime of the pack is added to it,
+         *                 so the time of the record becomes relative to that of the pack.
+         */                                                              
+        SenMLPack(const char* baseName, SenMLUnit baseUnit, double baseTime): _bn(baseName),                
+                                                                              _bu(baseUnit), 
+                                                                              _bt(baseTime),
+                                                                              _end(NULL) {};
+
+        /**
+         * create a SenMLPack object.
+         * @param callback a function that will be called while parsing incomming data, when no
+         *                 record can be found that matches any of the defined ones in the object.
+         *                 The parameters of the callback must be: 
+         *                    const char* packName the name of the pack that the record belongs to.
+         *                                         The data is for a child SenMLPack when this
+         *                                         field is different then the name of the root pack.
+         *                    const char* recordName the name of the record 
+         *                    const void* value a pointer to the memory blob that contains the actual value.
+         *                    int size the size of the memory blobl
+         *                    SenMLDataType dataType: defines how to interprete the memory blob (ex: pointer to integer,..)                
+         */
+        SenMLPack(PACK_ACTUATOR_SIGNATURE): _bn(""),                                  
+                                            _bu(SENML_UNIT_NONE), 
+                                            _bt(NAN),
+                                            _end(NULL),
+                                            callback(callback) {};
+
+        /**
+         * create a SenMLPack object.
+         * @param baseName the string that will be prepended to all records in this pack. 
+         *                 Is used to represent the name of the device.
+         * @param callback a function that will be called while parsing incomming data, when no
+         *                 record can be found that matches any of the defined ones in the object.
+         *                 The parameters of the callback must be: 
+         *                    const char* packName the name of the pack that the record belongs to.
+         *                                         The data is for a child SenMLPack when this
+         *                                         field is different then the name of the root pack.
+         *                    const char* recordName the name of the record 
+         *                    const void* value a pointer to the memory blob that contains the actual value.
+         *                    int size the size of the memory blobl
+         *                    SenMLDataType dataType: defines how to interprete the memory blob (ex: pointer to integer,..)                
+         */                                            
+        SenMLPack(const char* baseName, PACK_ACTUATOR_SIGNATURE): _bn(baseName), 
+                                                                  _bu(SENML_UNIT_NONE), 
+                                                                  _bt(NAN),
+                                                                  _end(NULL),
+                                                                  callback(callback) {};
+
+        /**
+         * create a SenMLPack object.
+         * @param baseName the string that will be prepended to all records in this pack. 
+         *                 Is used to represent the name of the device.
+         * @param baseUnit the unit name that will be used by default if the record doesnt
+         *                 not define one.
+         * @param callback a function that will be called while parsing incomming data, when no
+         *                 record can be found that matches any of the defined ones in the object.
+         *                 The parameters of the callback must be: 
+         *                    const char* packName the name of the pack that the record belongs to.
+         *                                         The data is for a child SenMLPack when this
+         *                                         field is different then the name of the root pack.
+         *                    const char* recordName the name of the record 
+         *                    const void* value a pointer to the memory blob that contains the actual value.
+         *                    int size the size of the memory blobl
+         *                    SenMLDataType dataType: defines how to interprete the memory blob (ex: pointer to integer,..)                
+         */                                                                     
+        SenMLPack(const char* baseName, SenMLUnit baseUnit, PACK_ACTUATOR_SIGNATURE): _bn(baseName), 
+                                                                                      _bu(baseUnit), 
+                                                                                      _bt(NAN),
+                                                                                      _end(NULL),
+                                                                                      callback(callback) {};
+
+        /**
+         * create a SenMLPack object.
+         * @param baseName the string that will be prepended to all records in this pack. 
+         *                 Is used to represent the name of the device.
+         * @param baseUnit the unit name that will be used by default if the record doesnt
+         *                 not define one.
+         * @param baseTime the time that will be added to each record. When specified, each
+         *                 record that does not specify a time, will receive this time. When
+         *                 the record does have a time, the baseTime of the pack is added to it,
+         *                 so the time of the record becomes relative to that of the pack.
+         * @param callback a function that will be called while parsing incomming data, when no
+         *                 record can be found that matches any of the defined ones in the object.
+         *                 The parameters of the callback must be: 
+         *                    const char* packName the name of the pack that the record belongs to.
+         *                                         The data is for a child SenMLPack when this
+         *                                         field is different then the name of the root pack.
+         *                    const char* recordName the name of the record 
+         *                    const void* value a pointer to the memory blob that contains the actual value.
+         *                    int size the size of the memory blobl
+         *                    SenMLDataType dataType: defines how to interprete the memory blob (ex: pointer to integer,..)   
+         */                                                                                       
+        SenMLPack(const char* baseName, SenMLUnit baseUnit, double baseTime, PACK_ACTUATOR_SIGNATURE): _bn(baseName), 
+                                                                                                       _bu(baseUnit), 
+                                                                                                       _bt(baseTime),
+                                                                                                       _end(NULL),
+                                                                                                       callback(callback) {};
+
+        /**
+         * destroys the SenMLPack object.
+         */
+        ~SenMLPack(){};
+
+        /**
+         * render the content of the current object to json data (string).
+         * This function is ideal for devices with low memory usage but offers less control over the rendering process.
+         * @param dest the destination stream to where the data will be rendered without buffering it in memory
+         * @param format determins how the data will be rendered. See SenMLStreamMethod for possible methods.
+         * @returns none
+         */
+        void toJson(Stream* dest, SenMLStreamMethod format=SENML_RAW);
+
+        /**
+         * render the content of the current object to json data (string).
+         * This function renders the data to a memory buffer. If the buffer is full before the entire object is 
+         * rendered, an error will be written to the debug stream.
+         * @param dest a memory buffer to which the data will be rendred.
+         * @param length the length of the memory buffer.
+         * @param format determins how the data will be rendered. See SenMLStreamMethod for possible methods.
+         * @returns none
+         */
+        void toJson(char *dest, int length, SenMLStreamMethod format=SENML_RAW);
+
+        /**
+         * render the content of the current object to cbor data (binary).
+         * This function is ideal for devices with low memory usage but offers less control over the rendering process.
+         * @param dest the destination stream to where the data will be rendered without buffering it in memory
+         * @param format determins how the data will be rendered. See SenMLStreamMethod for possible methods.
+         * @returns nr of bytes that were rendered
+         */
+        int toCbor(Stream* dest, SenMLStreamMethod format=SENML_RAW);
+
+        /**
+         * render the content of the current object to cbor data (binary).
+         * This function renders the data to a memory buffer. If the buffer is full before the entire object is 
+         * rendered, an error will be written to the debug stream.
+         * @param dest a memory buffer to which the data will be rendred.
+         * @param length the length of the memory buffer.
+         * @param format determins how the data will be rendered. See SenMLStreamMethod for possible methods.
+         * @returns nr of bytes that were rendered
+         */
+        int toCbor(char *dest, int length, SenMLStreamMethod format=SENML_RAW);
+
+        /**
+         * read and parse a senml json string from the specified source and, for each registered actuator, call the
+         * appropriate event on the actuator itself, for others, the callback function PACK_ACTUATOR_SIGNATURE 
+         * will be called, if present.
+         * This method is ideal for devices with very littel ram memory. It will block on most devices if there is 
+         * no more input to be read from the stream and the end of the json structure is not yet reached.
+         * Note: on mbed systems, the blocking nature is not garanteed. Instead, if no more data is available before
+         * the end is reached, parsing will fail.
+         * @param source the source stream to read the data from.
+         * @param format determins how the data will be read (ex: as normal text or in HEX format). 
+         *               See SenMLStreamMethod for possible methods.
+         * @returns none
+         */
+        void fromJson(Stream* source, SenMLStreamMethod format=SENML_RAW);
+
+        /**
+         * parse a senml json string from the specified source and, for each registered actuator, call the
+         * appropriate event on the actuator itself, for others, the callback function PACK_ACTUATOR_SIGNATURE 
+         * will be called, if present.
+         * This method takes a string stored in memory as input. The json must be fully defined. It is up to the
+         * caller to transform it to a regular text string, if needed (ex: lora devices might send it in hex format).
+         * @param source the source string to use as input. This must be null terminated.
+         * @returns none
+         */
+        void fromJson(const char* source);
+
+        /**
+         * read and parse senml cbor from the specified source and, for each registered actuator, call the
+         * appropriate event on the actuator itself, for others, the callback function PACK_ACTUATOR_SIGNATURE 
+         * will be called, if present.
+         * This method is ideal for devices with very littel ram memory. It will block on most decices if there is no 
+         * more input to be read from the stream and the end of the cbor structure is not yet reached.
+         * Note: on mbed systems, the blocking nature is not garanteed. Instead, if no more data is available before
+         * the end is reached, parsing will fail.
+         * @param source the source stream to read the data from.
+         * @param format determins how the data will be read (ex: as normal binary or in HEX format). 
+         *               See SenMLStreamMethod for possible methods.
+         * @returns none
+         */
+        void fromCbor(Stream* source, SenMLStreamMethod format=SENML_RAW);
+        
+        /**
+         * parse senml cbor from the specified memory and, for each registered actuator, call the
+         * appropriate event on the actuator itself, for others, the callback function PACK_ACTUATOR_SIGNATURE 
+         * will be called, if present.
+         * This method takes a memory blob as input. The data must be fully defined. 
+         * @param source the source data to use as input.
+         * @param length the length of the source data.
+         * @param format determins how the data will be read (ex: as normal binary or in HEX format). 
+         *               See SenMLStreamMethod for possible methods.
+         * @returns none
+         */
+        void fromCbor(char* source, int length, SenMLStreamMethod format);
+
+        /**
+         * assign a basename to the SenMLPack object. This represents the name of the device.
+         * see the spec on [base fields](https://tools.ietf.org/html/draft-ietf-core-senml-13#section-4.1) for more info.
+         * Every SenMLPack object must have a basename. This field will always be rendered in the output, even if the
+         * string is empty.
+         * @param name an immutable string that will be used to represent the name of the device. An internal
+         * copy of the value will be made.
+         * @returns none
+         */ 
+        void setBaseName(const char* name);
+
+        /**
+         * Get the base name. see the spec on [base fields](https://tools.ietf.org/html/draft-ietf-core-senml-13#section-4.1) for more info.
+         * @returns the name of the device as an immutable string.
+         */
+        const char* getBaseName();
+
+        /**
+         * Set the base unit that will be used as the default unit for all records that don't define their own unit. 
+         * see the spec on [base fields](https://tools.ietf.org/html/draft-ietf-core-senml-13#section-4.1) for more info.
+         * Set to SENML_UNIT_NONE for ommiting the base unit from the output (default).
+         * @param unit the unit to use as default. See SenMLUnit for all supported unit names.
+         * @returns none
+         */
+        void setBaseUnit(SenMLUnit unit);
+
+        /**
+         * Get the base unit. see the spec on [base fields](https://tools.ietf.org/html/draft-ietf-core-senml-13#section-4.1) for more info.
+         * @returns a SenMLUnit enum value that is used as the default unit for records that don't define a unit of their own.
+         */
+        inline SenMLUnit getBaseUnit() { return this->_bu; };
+
+
+        /**
+         * Set the base time. see the spec on [base fields](https://tools.ietf.org/html/draft-ietf-core-senml-13#section-4.1) for more info.
+         * @param time the value to use as base time. set bt to NaN if the field should not be included in the output.
+         * @returns none
+         */ 
+        void setBaseTime(double time);
+
+        /**
+         * Get the base time. see the spec on [base fields](https://tools.ietf.org/html/draft-ietf-core-senml-13#section-4.1) for more info.
+         * @returns a double value that is used as the default unit for records that don't define a unit of their own. 
+         *          if no base time is set, NaN will be returned.
+         */
+        inline double getBaseTime() { return this->_bt; };
+
+        /**
+         * Adds the specified SenML object to the document. The item will be appended to the end of the linked list. 
+         * The item being added, can be a regular SenMLRecord or another SenMLPack object if you want to send data 
+         * for multiple devices in 1 SenML message.
+         * Check the result of the function to see if the operation was successful or not. Possible reasons for failure:
+         * - if the item being added is already part of a document.
+         * @param item a pointer to a SenMlRecord or SenMLPack that needs to be added to the document.
+         * @returns true upon success, otherwise false.
+         */        
+        bool add(SenMLBase* item);
+
+        /**
+         * Clear out the document and remove all the children. Children aren't destroyed, this is up to the developer.
+         * @returns true (at the moment, the function does not yet return false as it doesn't detect any errors)
+         */
+        bool clear();
+        
+        /**
+         * get the first recrod of in this pack element. 
+         * @returns null when this object is empty (has no children), otherwise, the first item (SenMLRecord or SenMLPack)
+         * of the list.
+         */
+        inline SenMLBase* getFirst() { return this->_start; };
+
+
+        /**
+         * renders all the fields to json, without the starting and ending brackets.
+         * Inheriters can extend this function if they want to add extra fields to the json output
+         * note: this is public so that custom implementations for the record object can use other objects 
+         * internally and render to json using this function (ex: coordinatesRecord using 3 floatRecrods for lat, lon & alt.
+         * @returns: None
+        */
+        virtual void fieldsToJson();
+
+        /**
+         * renders all the fields to cbor format. renders all the fields of the object without the length info 
+         * at the beginning
+         * note: this is public so that custom implementations for the record object can use other objects 
+         * internally and render to json using this function (ex: coordinatesRecord using 3 floatRecrods for 
+         * lat, lon & alt.
+         * @returns: The number of bytes that were written.
+        */
+        virtual int fieldsToCbor();
+
+    protected:
+
+
+        //derived classes can use this function to see if the root object (getRoot) is a SenMLPack
+        //class or not.
+        virtual bool isPack() { return true; }
+
+        //when the user did not register a specific record actuator, this will be called.
+        inline void actuate(const char* pack, const char* record, const void* value, int valueLength, SenMLDataType dataType)
+        {
+            if(this->callback)
+                this->callback(pack, record, value, valueLength, dataType);
+        };
+
+        //store a ref to the last item in the list for quick link operations
+        void setLast(SenMLBase* value);
+
+        //renders the content of the pack object without []
+        virtual int contentToCbor();    
+
+
+        //calculates the nr of items that there will be in the json array in senml representation
+        //this is used for rendering cbor which needs to declare the nr of elements in an array.
+        virtual int getArrayLength();
+
+        virtual void setupStreamCtx(char *dest, int length, SenMLStreamMethod format);
+
+        virtual void setupStreamCtx(Stream *dest, SenMLStreamMethod format);
+
+    private:
+        String _bn;
+        SenMLUnit _bu;
+        double _bt;
+        SenMLBase *_end;                                //keeps track of the end of the list
+        SenMLBase *_start;                              //keeps track of the start of the list
+        PACK_ACTUATOR_SIGNATURE;                        //for registering actuator callbacks.
+        
+        
+        //renders the content of the pack object without []
+        virtual void contentToJson();              
+
+        //calculates the nr of json fields that this object uses in a senml structure
+        virtual int getFieldLength();
+
+
+        void internalToJson();
+
+        inline char readHexChar(Stream *source){
+            #ifdef __MBED__
+                unsigned char first = source->getc();
+                unsigned char second = source->getc();
+            #else
+                unsigned char first = source->read();
+                unsigned char second = source->read();
+            #endif
+            first = (first < '9') ? first - '0' : first - '7';
+            second = (second < '9') ? second - '0' : second - '7';
+            return (16 * first) + second;
+        };
+};
+
+
+#endif // SENMLPACK
+
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_pack_t.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_pack_t.h	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,84 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * pack (document) base class for packs that have base values
+ */
+
+#ifndef SENMLPACKTEMPLATE
+#define SENMLPACKTEMPLATE
+
+#include <senml_pack.h>
+
+/**
+ * A template class that can be used to create new SenMLPack types that store a base-value and/or base-sum 
+ * with a basic data type (no structs or classes).
+ * When you create a new class, you should always implement the following functions in order
+ * for the new class to operate correctly: fieldsToJson() and fieldsToCbor(). These functions are responsible
+ * for rendering both base-value and base-sum. This class does not implement any rendering.
+ * See previous implementations such as SenMLIntPack for inspiration.
+ */ 
+template <class T>
+class SenMLPackTemplate: public SenMLPack
+{
+    public:
+
+        SenMLPackTemplate(const char* baseName): SenMLPack(baseName, SENML_UNIT_NONE, NAN) {};
+        SenMLPackTemplate(const char* baseName, SenMLUnit baseUnit): SenMLPack(baseName, baseUnit, NAN) {};
+        SenMLPackTemplate(const char* baseName, SenMLUnit baseUnit, double baseTime): SenMLPack(baseName, baseUnit, baseTime) {};
+
+        SenMLPackTemplate(PACK_ACTUATOR_SIGNATURE): SenMLPack("", SENML_UNIT_NONE, NAN, callback) {};
+        SenMLPackTemplate(const char* baseName, PACK_ACTUATOR_SIGNATURE): SenMLPack(baseName, SENML_UNIT_NONE, NAN, callback) {};
+        SenMLPackTemplate(const char* baseName, SenMLUnit baseUnit, PACK_ACTUATOR_SIGNATURE): SenMLPack(baseName, baseUnit, NAN, callback) {};
+        SenMLPackTemplate(const char* baseName, SenMLUnit baseUnit, double baseTime, PACK_ACTUATOR_SIGNATURE): SenMLPack(baseName, baseUnit, baseTime, callback){};
+
+
+
+        ~SenMLPackTemplate(){};
+
+        /**
+         * Get the base-sum assigned to this pack object.
+         * @returns: the base-sum.
+         */
+        T getBaseSum() {return _sum; } ;
+
+        /**
+         * Store the base-sum in the pack object.
+         * @returns: true (returns a value to support possible future extentions)
+         */
+        bool setBaseSum(T value) {_sum = value; return true;};
+
+        /**
+         * Get the base-value assigned to this pack object.
+         * @returns: the base-value.
+         */
+        T getBaseValue() {return _value; } ;
+
+        /**
+         * Store the base-value in the pack object.
+         * @returns: true (returns a value to support possible future extentions)
+         */
+        bool setBaseValue(T value) {_value = value; return true;};
+
+protected:
+
+
+private:
+    T _sum;
+    T _value;
+};
+
+#endif // SENMLPACKTEMPLATE
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_record.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_record.cpp	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,175 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * record base class 
+ */
+
+#include <senml_record.h>
+#include <senml_helpers.h>
+#include <senml_pack.h>
+#include <cbor.h>
+#include <senml_logging.h>
+
+SenMLRecord::SenMLRecord(const char* name): _name(name), _unit(SENML_UNIT_NONE), _time(NAN), _updateTime(0)
+{
+}
+
+SenMLRecord::SenMLRecord(const char* name, SenMLUnit unit): _name(name), _unit(unit), _time(NAN), _updateTime(0)
+{
+}
+
+bool SenMLRecord::setTime(double value, bool absolute)
+{
+    SenMLBase* root = this->getRoot();
+    if(absolute){
+        if(root){
+            if(root->isPack()){
+                double baseTime = ((SenMLPack*)root)->getBaseTime();
+                if(!isnan(baseTime))
+                    value -= baseTime;
+            }
+            else{
+                return false;
+            }
+        }
+    }
+    else if(root == NULL){
+        return false;
+    }
+    this->_time = value;
+    return true;
+}
+
+bool SenMLRecord::setUpdateTime(double value, bool absolute)
+{
+    SenMLBase* root = this->getRoot();
+    if(absolute){
+        if(root){
+            if(root->isPack()){
+                double baseTime = ((SenMLPack*)root)->getBaseTime();
+                if(!isnan(baseTime))
+                    value -= baseTime;
+            }
+            else{
+                return false;
+            }
+        }
+    }
+    else if(root == NULL){
+        return false;
+    }
+    this->_updateTime = value;
+    return true;
+}
+
+void SenMLRecord::contentToJson()
+{
+    printText("{", 1);
+    this->fieldsToJson();
+    printText("}", 1);
+}
+
+void SenMLRecord::adjustToBaseTime(double prev, double time)
+{
+    if(!isnan(this->_time)){
+        if(!isnan(prev))
+        this->_time += prev;
+        if(!isnan(time))
+            this->_time -= time;
+    }
+    if(!isnan(this->_updateTime)){
+        if(!isnan(prev))
+        this->_updateTime += prev;
+        if(!isnan(time))
+            this->_updateTime -= time;
+    }
+}
+
+void SenMLRecord::fieldsToJson()
+{
+    int bnLength = this->_name.length();
+    if(bnLength){
+        printText("\"n\":\"", 5);
+        printText(this->_name.c_str(), bnLength);
+        printText("\"", 1);
+    }
+    if(!isnan(this->_time)){
+        printText(",\"t\":", 5);
+        printDouble(this->_time, SENML_MAX_DOUBLE_PRECISION);
+    }
+    if(this->_unit != SENML_UNIT_NONE){
+        printText(",\"u\":\"", 6);
+        printUnit(this->_unit);
+        printText("\"", 1);
+    }
+    if(this->_updateTime != 0){
+        printText(",\"ut\":", 5);
+        #ifdef __MBED__
+            char buf[10];
+            sprintf(buf, "%d", this->_updateTime);
+            String val = buf;
+        #else
+            String val(this->_updateTime);
+        #endif
+        printText(val.c_str(), val.length());
+    }
+}
+
+void SenMLRecord::actuate(const void* value, int dataLength, SenMLDataType dataType)
+{
+    log_debug(this->getName());
+    log_debug("no actuator");
+}
+
+int SenMLRecord::contentToCbor()
+{
+    int res = cbor_serialize_map(this->getFieldLength());
+    res += this->fieldsToCbor();
+    return res;
+}
+
+int SenMLRecord::getFieldLength()
+{
+    int result = 1;                                 //always have 1 item for the value
+    if(this->_name.length() > 0) result++;
+    if(!isnan(this->_time)) result++;
+    if(this->_unit != SENML_UNIT_NONE) result++;
+    if(this->_updateTime != 0) result ++;
+    return result;
+}
+
+int SenMLRecord::fieldsToCbor()
+{
+    int res = 0;
+    if(this->_name.length() > 0){
+        res += cbor_serialize_int(SENML_CBOR_N_LABEL);
+        res += cbor_serialize_unicode_string(this->_name.c_str());
+    }
+    if(!isnan(this->_time)){
+        res += cbor_serialize_int(SENML_CBOR_T_LABEL);
+        res += cbor_serialize_double(this->_time);
+    }
+    if(this->_unit != SENML_UNIT_NONE){
+        res += cbor_serialize_int(SENML_CBOR_U_LABEL);
+        res += cbor_serialize_unicode_string(senml_units_names[this->_unit]);
+    }
+    if(this->_updateTime != 0){
+        res += cbor_serialize_int(SENML_CBOR_UT_LABEL);
+        res += cbor_serialize_int(this->_updateTime);
+    }
+    return res;
+}
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_record.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_record.h	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,215 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * record base class 
+ */
+
+#ifndef SENMLTIMED
+#define SENMLTIMED
+
+#include <senml_base.h> 
+#include <senml_enums.h>
+
+#ifdef __MBED__
+    #include "mbed.h"
+    #include <string> 
+    using namespace std;
+    #define String string
+#endif
+
+/**
+ * SenMLRecord represents a single measurement. Besides the value, it also stores the name, 
+ * optional unit, timestamp and update time.
+ * 
+ * This is the base class for all record types, you can not work directly with this class.
+ * Instead, use one of it's derivatives such as SenMLBinaryRecord, SenMLBoolRecord, SenMLFloatRecord,
+ * SenMLIntRecord or SenMLStringRecord. Alternatively, you can create your own records by inheriting from
+ * this class, or SenMLRecordTemplate, a convenience template which allows you to create records for 
+ * different data types.
+ * 
+ * Although a record object can be used by itself, usually record objects are added to SenMLPack objects.
+ */ 
+class SenMLRecord: public SenMLBase
+{
+    friend class SenMLCborParser;
+    friend class SenMLJsonListener;
+    public:
+    
+        /**
+         * create a SenMLRecord object.
+         */
+        SenMLRecord(): _unit(SENML_UNIT_NONE), _time(NAN), _updateTime(0){};
+
+        /**
+         * create a SenMLRecord object.
+         * @param name the identifier for this record. This is a free-form string, but a set of
+         *             predefined names, supported by the KPN network can be found in senml_enums.h
+         */
+        SenMLRecord(const char* name);
+
+        /**
+         * create a SenMLRecord object.
+         * @param name the string that will be prepended to all records in this pack. 
+         *                 Is used to represent the name of the device.
+         * @param unit the unit that should be included in the output. See the SenMLUnit enum for 
+         *             supported unit types.
+         */
+        SenMLRecord(const char* name, SenMLUnit unit);
+    
+
+        /**
+         * returns the time assigned to this record. NaN represents 'no time assigned'.
+         * See [base fields](https://tools.ietf.org/html/draft-ietf-core-senml-13#section-4.1) for more info on the time value.
+         * @returns the timestamp assigned to the record, expressed in unix epoch, relative to the
+         *          parent SenMLPack object's base time (if any).
+         */ 
+        double getTime() {return this->_time;};
+
+        /**
+         * set the time, expressed as a unix epoch time, absolute or relative to the base time of the 
+         * parent SenMLPack object..
+         * when absolute is true (default behaviour), the time value will be made relative to the 
+         * base time of the pack object, if it has a base time.
+         * See [base fields](https://tools.ietf.org/html/draft-ietf-core-senml-13#section-4.1) for more info on the time value.
+         * Possible reasons for failure:
+         *  - if there is a root object, but it is not a SenMLPack
+         *  - if absolute is false, but there is no parent SenMLPack object.
+         * @param value the unix epoch time value to assign to the record
+         * @param absolute When true (default), the value will be interpreted as an absolute time stamp. If there 
+         *                 is a parent SenMLPack, the value will be made relative to the pack's base time, if there is any.
+         *                 When false, no conversion is done, but a parent SenMLPack has to be present.
+         * @returns true upon success, otherwise false. 
+         */ 
+        bool setTime(double value, bool absolute = true);
+
+
+        /**
+         * Get the name of the record.
+         * @returns the name of the record as an immutable string. Don't delete this pointer, it refers to the 
+         *          internal buffer of the object and is managed by the record.
+         */        
+        inline const char* getName(){ return this->_name.c_str();}
+
+        /**
+         * Assign an identifier to the record. This is a free-form string, but a set of
+         * predefined names, supported by the KPN network can be found in senml_enums.h
+         * @param name a string that represents the identifier for the record. A copy of the value
+         *             is made.
+         * @returns none
+         */
+        inline void setName(const char* name){ this->_name = name; }
+
+
+        /**
+         * Get the expected timestamp at which this record will be updated again. NaN represents 'no time assigned'.
+         * @returns a unix epoch time, relative to the parent SenMLPack object's base time (if any).
+         */   
+        inline int getUpdateTime(){ return this->_updateTime;}
+
+        /**
+         * Assign a timestamp to the record at which it is expected to update the value.
+         * when absolute is true (default behaviour), the time value will be made relative to the 
+         * base time of the pack object, if it has a base time.
+         * See [base fields](https://tools.ietf.org/html/draft-ietf-core-senml-13#section-4.1) for more info on the time value.
+         * Possible reasons for failure:
+         *  - if there is a root object, but it is not a SenMLPack
+         *  - if absolute is false, but there is no parent SenMLPack object.
+         * @param value the unix epoch update time value to assign to the record
+         * @param absolute When true (default), the value will be interpreted as an absolute time stamp. If there 
+         *                 is a parent SenMLPack, the value will be made relative to the pack's base time, if there is any.
+         *                 When false, no conversion is done, but a parent SenMLPack has to be present.
+         * @returns true upon success, otherwise false. 
+         */
+        bool setUpdateTime(double value, bool absolute=true);
+
+
+        /**
+         * Get the enum that identifies the unit name assigned to the record.
+         * The value SENML_UNIT_NONE means that no unit will be generated in the output.
+         * Senml defines a fixed set of supported [unit names](https://tools.ietf.org/html/draft-ietf-core-senml-13#section-12.1).
+         * @returns a SenMLUnit enum value representing the unit name.
+         */ 
+        SenMLUnit getUnit(){ return this->_unit;}
+
+        /**
+         * Assigns a unit value to the record. 
+         * The value SENML_UNIT_NONE means that no unit will be generated in the output.
+         * Senml defines a fixed set of supported [unit names](https://tools.ietf.org/html/draft-ietf-core-senml-13#section-12.1).
+         * @param value a SenMLUnit enum value representing the unit name.
+         * @returns none
+         */
+        void setUnit(SenMLUnit value){ this->_unit = value; } 
+
+
+        /**
+         * renders all the fields to json, without the starting and ending brackets.
+         * Inheriters can extend this function if they want to add extra fields to the json output
+         * note: this is public so that custom implementations for the record object can use other objects 
+         * internally and render to json using this function (ex: coordinatesRecord using 3 floatRecrods for lat, lon & alt.
+         * @returns: None
+        */
+        virtual void fieldsToJson();
+
+        /**
+         * renders all the fields to cbor format. renders all the fields of the object without the length info 
+         * at the beginning
+         * note: this is public so that custom implementations for the record object can use other objects 
+         * internally and render to json using this function (ex: coordinatesRecord using 3 floatRecrods for 
+         * lat, lon & alt.
+         * @returns: The number of bytes that were written.
+        */
+        virtual int fieldsToCbor();
+
+    protected:
+
+
+        /*
+        renders all the fields to json, including the starting and ending brackets.
+        Inheriters can extend this function if they want to add extra fields to the json output
+        */
+        virtual void contentToJson();
+
+        
+
+        //renders all the fields to cbor format. renders all the fields of the object including the 
+        //length info at the beginning
+        virtual int contentToCbor();
+
+
+        
+
+        //This function is called by the root SenMLPack object to indicate that the object
+        //should adjust it's time info relative to the new base time (if applicable)
+        //doesn't do anything by default
+        virtual void adjustToBaseTime(double prev, double time);
+        
+
+        //called while parsing a senml message, when the parser found the value for an SenMLJsonListener
+        virtual void actuate(const void* value, int dataLength, SenMLDataType dataType);
+
+        //calculates the nr of fields that this record will produce.
+        //The default implementation already adds 1 field for the value.
+        int getFieldLength();
+
+    private:
+        double _time;
+        int _updateTime;
+        String _name;
+        SenMLUnit _unit;
+};
+
+#endif // SENMLRECORD
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_record_t.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_record_t.h	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,130 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * record base class for simple data types
+ */
+
+#ifndef SENMLRECORDTEMPLATE
+#define SENMLRECORDTEMPLATE
+
+#include <senml_record.h>
+
+
+/**
+ * A template class that can be used to create new SenMLRecord types that store a value with a basic
+ * data type (no structs or classes).
+ * When you create a new class, you should always implement the following functions in order
+ * for the new class to operate correctly: fieldsToJson() and fieldsToCbor()
+ */ 
+template <class T>
+class SenMLRecordTemplate: public SenMLRecord
+{
+public:
+    SenMLRecordTemplate(const char* name): SenMLRecord(name), _valAsSum(false){};
+    SenMLRecordTemplate(const char* name, SenMLUnit unit):  SenMLRecord(name, unit), _valAsSum(false){};
+    SenMLRecordTemplate(const char* name, SenMLUnit unit, T value):  SenMLRecord(name, unit), _value(value), _valAsSum(false) {};
+   // ~SenMLRecordTemplate(){};
+
+    /**
+     * Get the value assigned to this SenMLRecord.
+     * This function always returns the absolute value, also when the record is part of a pack that
+     * has a base value assigned.
+     * @returns: the value (including the base-value when applicable). 
+     */
+    T get() {return _value; } ;
+
+    /**
+     * Get if the value should be interpreted as a sum or regular value. These are exclusive: a SenMLRecord
+     * either has a sum or a value, but never both.
+     * Sum and value are always assigned with the function set().
+     * @returns: True when the value is interpreted as a sum, otherwise false.
+     */
+    bool asSum() {return this->_valAsSum; };
+
+	#ifdef __MBED__
+
+	/**
+     * Assign a value to the SenMLRecord. You can optionally assign the time at which the measurement was taken
+     * and if it should be interpreted as a sum or regular value.
+	 * No timestamp is added and the value is interpreted as a regular value, not as a sum
+     * @param  value the value to store in the record
+     * @returns: true if the operation was succesful. See setTime() for more info when the operation can fail.
+     */
+	bool set(T value)
+    {
+        return this->set(value, (double)NAN, false);
+    };
+    
+    /**
+     * Assign a value to the SenMLRecord. You can optionally assign the time at which the measurement was taken
+     * and if it should be interpreted as a sum or regular value.
+	 * The value is interpreted as a regular value, not as a sum
+     * @param  value the value to store in the record
+     * @param time optional (default = NAN, meaning not time info). The time at which the measurement was taken.
+     *             This should always be the absolute time value which will be converted relative to the base
+     *             time when applicable (if the root is a SenMLPack with baseTime) . If you want to set the time 
+     *             manually relative to the basetime of the root-pack, then use setTime() instead.
+     * @returns: true if the operation was succesful. See setTime() for more info when the operation can fail.
+     */
+    bool set(T value, double time)
+    {
+        return this->set(value, time, false);
+    };
+    
+    /**
+     * Assign a value to the SenMLRecord. You can optionally assign the time at which the measurement was taken
+     * and if it should be interpreted as a sum or regular value.
+     * @param  value the value to store in the record
+     * @param time optional (default = NAN, meaning not time info). The time at which the measurement was taken.
+     *             This should always be the absolute time value which will be converted relative to the base
+     *             time when applicable (if the root is a SenMLPack with baseTime) . If you want to set the time 
+     *             manually relative to the basetime of the root-pack, then use setTime() instead.
+     * @param asSum when true, the value will be interpreted as a sum, otherwise as a regular value.
+     * @returns: true if the operation was succesful. See setTime() for more info when the operation can fail.
+     */
+    bool set(T value, double time, bool asSum)
+	
+	#else
+	
+    /**
+     * Assign a value to the SenMLRecord. You can optionally assign the time at which the measurement was taken
+     * and if it should be interpreted as a sum or regular value.
+     * @param  value the value to store in the record
+     * @param time optional (default = NAN, meaning not time info). The time at which the measurement was taken.
+     *             This should always be the absolute time value which will be converted relative to the base
+     *             time when applicable (if the root is a SenMLPack with baseTime) . If you want to set the time 
+     *             manually relative to the basetime of the root-pack, then use setTime() instead.
+     * @param asSum when true, the value will be interpreted as a sum, otherwise as a regular value.
+     * @returns: true if the operation was succesful. See setTime() for more info when the operation can fail.
+     */
+    bool set(T value, double time = (double)NAN, bool asSum = false)
+	#endif
+    {
+        this->_value = value; 
+        this->_valAsSum = asSum;
+        return this->setTime(time);
+    };
+
+protected:
+
+
+private:
+    T _value;
+    bool _valAsSum;
+};
+
+#endif // SENMLRECORDTEMPLATE
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_string_actuator.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_string_actuator.cpp	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,33 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * support for string actuators
+ */
+
+#include <senml_string_actuator.h>
+#include <senml_logging.h>
+
+void SenMLStringActuator::actuate(const void* value, int dataLength, SenMLDataType dataType)
+{
+    if(dataType == SENML_TYPE_STRING || dataType == CBOR_TYPE_STRING){
+        this->set((char*)value);
+        if(this->callback)
+            this->callback((char*)value);
+    }
+    else
+        log_debug("invalid type");
+}
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_string_actuator.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_string_actuator.h	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,50 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * support for string actuators
+ */
+
+#ifndef SENMLSTRINGACTUATOR
+#define SENMLSTRINGACTUATOR
+
+#include <senml_string_record.h>
+
+#define STRING_ACTUATOR_SIGNATURE void (*callback)(const char*)
+
+/**
+ * A SenMLRecord that stores text data and supports actuation.
+ */ 
+class SenMLStringActuator: public SenMLStringRecord
+{
+public:
+    SenMLStringActuator(const char* name, STRING_ACTUATOR_SIGNATURE): SenMLStringRecord(name, SENML_UNIT_NONE, NULL), callback(callback) {};
+    SenMLStringActuator(const char* name, SenMLUnit unit, STRING_ACTUATOR_SIGNATURE): SenMLStringRecord(name, unit, NULL), callback(callback) {};
+    SenMLStringActuator(const char* name, SenMLUnit unit, const char* value, STRING_ACTUATOR_SIGNATURE):  SenMLStringRecord(name, unit, value), callback(callback) {};
+    ~SenMLStringActuator(){};
+
+protected:
+
+    /**
+     * called while parsing a senml message, when the parser found the value for an SenMLJsonListener
+     */
+    virtual void actuate(const void* value, int dataLength, SenMLDataType dataType);
+
+private:
+    STRING_ACTUATOR_SIGNATURE;
+};
+
+#endif // SENMLSTRINGACTUATOR
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_string_record.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_string_record.cpp	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,40 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * support for string sensors
+ */
+
+
+#include <senml_string_record.h>
+#include <cbor.h>
+#include <senml_helpers.h>
+
+void SenMLStringRecord::fieldsToJson()
+{
+    SenMLRecord::fieldsToJson();
+    printText(",\"vs\":\"", 7);
+    String val = this->get();
+    printText(val.c_str(), val.length());
+    printText("\"", 1);
+}
+int SenMLStringRecord::fieldsToCbor()
+{
+    int res = SenMLRecord::fieldsToCbor();
+    res += cbor_serialize_int(SENML_CBOR_VS_LABEL);
+    res += cbor_serialize_unicode_string(this->get().c_str());
+    return res;
+}
+
+
+
+
+
+
+
diff -r 000000000000 -r a9259748d982 senml_string_record.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/senml_string_record.h	Sat May 19 17:35:20 2018 +0000
@@ -0,0 +1,63 @@
+/*  _  __  ____    _   _ 
+ * | |/ / |  _ \  | \ | |
+ * | ' /  | |_) | |  \| |
+ * | . \  |  __/  | |\  |
+ * |_|\_\ |_|     |_| \_|
+ * 
+ * (c) 2018 KPN
+ * License: MIT License.
+ * Author: Jan Bogaerts
+ * 
+ * support for string sensors
+ */
+
+#ifndef SENMLSTRINGRECORD
+#define SENMLSTRINGRECORD
+
+#include <senml_record_t.h>
+
+/**
+ * A SenMLRecord that stores text data.
+ * This type of object can only be used for sensor data. If actuation is needed, use SenMLStringActuator
+ * instead.
+ */ 
+class SenMLStringRecord: public SenMLRecordTemplate<String>
+{
+public:
+    SenMLStringRecord(const char* name):  SenMLRecordTemplate(name){};
+    SenMLStringRecord(const char* name, SenMLUnit unit):  SenMLRecordTemplate(name, unit){};
+    SenMLStringRecord(const char* name, SenMLUnit unit, const char* value):  SenMLRecordTemplate(name, unit, value){};
+    //~SenMLStringRecord(){};
+
+    /**
+     * renders all the fields to json, without the starting and ending brackets.
+     * Inheriters can extend this function if they want to add extra fields to the json output
+     * note: this is public so that custom implementations for the record object can use other objects 
+     * internally and render to json using this function (ex: coordinatesRecord using 3 floatRecrods for lat, lon & alt.
+     * @returns: None
+    */
+    virtual void fieldsToJson();
+
+    /**
+     * renders all the fields to cbor format. renders all the fields of the object without the length info 
+     * at the beginning
+     * note: this is public so that custom implementations for the record object can use other objects 
+     * internally and render to json using this function (ex: coordinatesRecord using 3 floatRecrods for 
+     * lat, lon & alt.
+     * @returns: The number of bytes that were written.
+    */
+    virtual int fieldsToCbor();
+protected:
+
+    
+private:
+};
+
+#endif // SENMLSTRINGRECORD
+
+
+
+
+
+
+