diff --git a/sys/include/phydat.h b/sys/include/phydat.h index 9702bbbf86..a85968bfbc 100644 --- a/sys/include/phydat.h +++ b/sys/include/phydat.h @@ -240,6 +240,51 @@ char phydat_prefix_from_scale(int8_t scale); */ void phydat_fit(phydat_t *dat, const int32_t *values, unsigned int dim); +/** + * @brief Convert the given phydat_t structure into a JSON string + * + * The output string written to @p buf will be `\0` terminated. You must make + * sure, that the given @p buf is large enough to hold the resulting string. You + * can call the function with `@p buf := NULL` to simply calculate the size of + * the JSON string without writing anything. + * + * The formatted JSON string will have the following format: + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.json} + * // case (dim == 1): + * { + * "d": 21.45, + * "u": "°C" + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.json} + * // case (dim > 1), dim := 3 in this case: + * { + * "d": [1.02, 0.23, -0.81], + * "u": "g" + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * The data will be encoded as fixed point number based on the given scale + * factor. + * + * For encoding the unit, this function uses the extended + * phydat_unit_to_str_verbose() function to also print units for non-SI types, + * e.g. it will produce `..."u":"date"}` for @ref UNIT_DATE or `..."u":"none"}` + * for @ref UNIT_NONE. + * + * @param[in] data data to encode + * @param[in] dim dimensions used in @p data, MUST be > 0 and < PHYDAT_DIM + * @param[out] buf target buffer for the JSON string, or NULL + * + * @pre @p dim > 0 + * @pre @p dim < PHYDAT_DIM + * + * @return number of bytes (potentially) written to @p buf, including `\0` + * terminator + */ +size_t phydat_to_json(const phydat_t *data, size_t dim, char *buf); + #ifdef __cplusplus } #endif diff --git a/sys/phydat/phydat_json.c b/sys/phydat/phydat_json.c new file mode 100644 index 0000000000..d2c3675772 --- /dev/null +++ b/sys/phydat/phydat_json.c @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2017 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup sys_phydat + * @{ + * + * @file + * @brief Convert phydat_t structs to human readable JSON strings + * + * @author Hauke Petersen + * + * @} + */ + +#include + +#include "fmt.h" +#include "assert.h" +#include "phydat.h" + +#define STATIC_LEN (14U) + +/** + * @note @p buf must be at least 5 bytes of size + */ +static size_t _bool_to_str(int16_t val, char *buf) +{ + if (val) { + memcpy(buf, "true", 4); + return 4; + } + else { + memcpy(buf, "false", 5); + return 5; + } +} + +size_t phydat_to_json(const phydat_t *data, size_t dim, char *buf) +{ + assert((dim > 0) && (dim < PHYDAT_DIM)); + + size_t pos = 0; + + if (buf == NULL) { + pos = STATIC_LEN; + if (dim > 1) { + pos += (2 + (dim - 1)); /* array parens + separating commas */ + } + for (size_t i = 0; i < dim; i++) { + if (data->unit != UNIT_BOOL) { + pos += fmt_s16_dfp(NULL, data->val[i], (int)data->scale); + } + else { + pos += (data->val[i]) ? 4 : 5; /* true: 4, false: 5 */ + } + } + pos += strlen(phydat_unit_to_str_verbose(data->unit)); + } + else { + memcpy(buf, "{\"d\":", 5); + pos += 5; + /* write data */ + if (dim > 1) { + buf[pos++] = '['; + } + for (size_t i = 0; i < dim; i++) { + if (data->unit != UNIT_BOOL) { + pos += fmt_s16_dfp(&buf[pos], data->val[i], (int)data->scale); + } + else { + pos += _bool_to_str(data->val[i], &buf[pos]); + } + buf[pos++] = ','; + } + /* override last comma if needed */ + if (dim > 1) { + buf[pos - 1] = ']'; + buf[pos++] = ','; + } + /* add unit */ + memcpy(&buf[pos], "\"u\":\"", 5); + pos += 5; + const char *u = phydat_unit_to_str_verbose(data->unit); + strcpy(&buf[pos], u); + pos += strlen(u); + /* terminate the JSON string */ + memcpy(&buf[pos], "\"}", 2); + pos += 2; + buf[pos++] = '\0'; + } + + return pos; +}