From 786895a378dd80493c4c962e004392710b6af727 Mon Sep 17 00:00:00 2001 From: Hauke Petersen Date: Wed, 18 Nov 2015 16:06:16 +0100 Subject: [PATCH] sys: added module for handling physical data --- Makefile.dep | 16 ++++ sys/include/phydat.h | 175 ++++++++++++++++++++++++++++++++++++++++ sys/phydat/Makefile | 1 + sys/phydat/phydat_str.c | 81 +++++++++++++++++++ 4 files changed, 273 insertions(+) create mode 100644 sys/include/phydat.h create mode 100644 sys/phydat/Makefile create mode 100644 sys/phydat/phydat_str.c diff --git a/Makefile.dep b/Makefile.dep index bd95089a1d..db312db47c 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -390,3 +390,19 @@ endif ifneq (,$(filter xtimer,$(USEMODULE))) FEATURES_REQUIRED += periph_timer endif + +ifneq (,$(filter saul_reg,$(USEMODULE))) + USEMODULE += saul +endif + +ifneq (,$(filter saul_default,$(USEMODULE))) + USEMODULE += saul +endif + +ifneq (,$(filter saul,$(USEMODULE))) + USEMODULE += phydat +endif + +ifneq (,$(filter phydat,$(USEMODULE))) + USEMODULE += fmt +endif diff --git a/sys/include/phydat.h b/sys/include/phydat.h new file mode 100644 index 0000000000..568008e1f7 --- /dev/null +++ b/sys/include/phydat.h @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2015 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. + */ + +/** + * @defgroup sys_phydat Phydat + * @ingroup sys + * @brief Generic data container for physical data and utility functions + * + * The purpose of this module is to introduce a common view on physical data + * throughout RIOT. This data is typically the output from sensor readings, data + * aggregation, and also the input for actuators. + * + * The idea is to enable different sensor/actuator drivers and other RIOT + * modules to exchange and have the same view on this kind of data. Labeling + * data with a unit type it's scaling makes it possible to pipe data between + * modules in an automated fashion without the need of specialized software + * wrappers and/or data normalization modules. + * + * @todo It might make sense to introduce additional data types for + * increased precision, i.e. something like phydat_float_t... + * + * @{ + * + * @file + * @brief Generic data container for physical data interface + * + * @author Hauke Petersen + */ + +#ifndef SECT_DATA_H +#define SECT_DATA_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The fixed number of dimensions we work with + * + * We use a fixed number of 3 dimensions, as many physical values we encounter + * can be expressed this way. In practice we have e.g. readings from + * accelerometers, gyros, color sensors, or set data for RGB LEDs. + * + * When expressing 1-dimensional data we just ignore the 2 higher dimension. + * This leads to a slight overhead of some byte of memory - but we benefit from + * a unified data structure for passing around physical data. + */ +#define PHYDAT_DIM (3U) + +/** + * @brief The maximum length of a scaling string + */ +#define PHYDAT_SCALE_STR_MAXLEN (sizeof("*E-128\0")) + +/** + * @brief Definition of physical units and comparable data types + * + * This list should contain all needed physical units (e.g. SI units), but also + * non-physical units that can be used to define the type of data passed around. + * This can be for example BOOL or aggregate values. As rule of thumb, the unit + * list can contain anything that helps two modules automatically negotiate, if + * they can understand each other. + * + * @note Extent this list as needed. + */ +enum { + /* generic values */ + UNIT_UNDEF, /**< unit undefined */ + UNIT_NONE, /**< data has no physical unit */ + /* temperature */ + UNIT_TEMP_C, /**< degree Celsius */ + UNIT_TEMP_F, /**< degree Fahrenheit */ + UNIT_TEMP_K, /**< Kelvin */ + /* dimension */ + UNIT_M, /**< meters */ + UNIT_M2, /**< square meters */ + UNIT_M3, /**< cubic meters */ + /* kinetic */ + UNIT_G, /**< gravitational force */ + UNIT_DPS, /**< degree per second */ + /* weight */ + UNIT_GR, /**< grams - not using the SI unit (kg) here to make scale + * handling simpler */ + /* electricity */ + UNIT_A, /**< Ampere */ + UNIT_V, /**< Volts */ + UNIT_GS, /**< gauss */ + /* pressure */ + UNIT_BAR, /**< Beer? */ + UNIT_PA, /**< Pascal */ + /* light */ + UNIT_CD, /**< Candela */ + /* logical */ + UNIT_BOOL, /**< boolean value [0|1] */ + UNIT_PERCENT, /**< out of 100 */ + UNIT_PERMILL, /**< out of 1000 */ + UNIT_PPM, /**< part per million */ + /* aggregate values */ + UNIT_TIME, /**< the three dimensions contain sec, min, and hours */ + UNIT_DATE /**< the 3 dimensions contain days, months and years */ + /* extend this list as needed */ +}; + +/** + * @brief Generic data structure for expressing physical values + * + * Physical data is express in a 3-dimensional touple of values. In addition to + * the data fields, this struct contains further the (physical) unit and the + * scale factor of the data. The unit is expressed as constant. The scale factor + * is expressed as power of 10 (10^factor). + * + * The combination of signed 16-bit numbers with and the scale factor gives us a + * very high dynamic range (from -32*10^-131 to 32*10^130). I a wider sense we + * are saving the values as fixed floating points... + * + * The scale factor is identical for all 3 values. + * + * In a traditional (scientific) computational system the obvious choice for the + * used data type would be to use floats. We are however on heavily resource + * constrained (even 8-bit) embedded systems, so we use int16_t here. As most + * sensor are in some way ADC based, they normally do not use a higher accuracy + * than 12-14bit, so using 16-bit integers to represent this data is good enough + * in most cases. + */ +typedef struct { + int16_t val[PHYDAT_DIM]; /**< the 3 generic dimensions of data */ + uint8_t unit; /**< the (physical) unit of the data */ + int8_t scale; /**< the scale factor, 10^*scale* */ +} phydat_t; + +/** + * @brief Dump the given data container to STDIO + * + * @param[in] data data container to dump + * @param[in] dim number of dimension of @p data to dump + */ +void phydat_dump(phydat_t *data, uint8_t dim); + +/** + * @brief Convert the given unit to a string + * + * @param[in] unit unit to convert + * + * @return string representation of given unit (e.g. V or m) + * @return NULL if unit was not recognized + */ +const char *phydat_unit_to_str(uint8_t unit); + +/** + * @brief Convert the given scale factor to a NULL terminated string + * + * The given scaling factor will be given as SI unit (e.g. M for Mega, u for + * micro, etc) for obvious cases or in scientific notation (e.g. 2E11, 1E-22, + * etc) otherwise. + * + * @param[in] scale scale factor to convert + * @param[in] str buffer to write the result into, MUST be at least of + * length @p PHYDAT_SCALE_STR_MAXLEN + */ +void phydat_scale_to_str(int8_t scale, char *str); + +#ifdef __cplusplus +} +#endif + +#endif /* SECT_DATA_H */ +/** @} */ diff --git a/sys/phydat/Makefile b/sys/phydat/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/sys/phydat/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/phydat/phydat_str.c b/sys/phydat/phydat_str.c new file mode 100644 index 0000000000..ca54fda178 --- /dev/null +++ b/sys/phydat/phydat_str.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2015 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 driver_sensif + * @{ + * + * @file + * @brief Generic sensor/actuator data handling + * + * @author Hauke Petersen + * + * @} + */ + +#include +#include + +#include "fmt.h" +#include "phydat.h" + +void phydat_dump(phydat_t *data, uint8_t dim) +{ + if (data == NULL || dim > PHYDAT_DIM) { + puts("Unable to display data object"); + return; + } + printf("Data:"); + for (uint8_t i = 0; i < dim; i++) { + char tmp[PHYDAT_SCALE_STR_MAXLEN]; + phydat_scale_to_str(data->scale, tmp); + printf("\t[%i] %i%s%s\n", (int)i, (int)data->val[i], tmp, + phydat_unit_to_str(data->unit)); + } +} + +const char *phydat_unit_to_str(uint8_t unit) +{ + switch (unit) { + case UNIT_TEMP_C: return "°C"; + case UNIT_TEMP_F: return "°F"; + case UNIT_TEMP_K: return "K"; + case UNIT_M: return "m"; + case UNIT_G: return "g"; + case UNIT_DPS: return "dps"; + case UNIT_GR: return "G"; + case UNIT_A: return "A"; + case UNIT_V: return "V"; + case UNIT_GS: return "Gs"; + case UNIT_BAR: return "Bar"; + case UNIT_PA: return "Pa"; + case UNIT_CD: return "cd"; + default: return ""; + } +} + +void phydat_scale_to_str(int8_t scale, char *str) +{ + switch (scale) { + case 0: *str = '\0'; return; + case -3: *str = 'm'; break; + case -6: *str = 'u'; break; + case -9: *str = 'n'; break; + case -12: *str = 'p'; break; + case -15: *str = 'f'; break; + case 3: *str = 'k'; break; + case 6: *str = 'M'; break; + case 9: *str = 'G'; break; + case 12: *str = 'T'; break; + case 15: *str = 'P'; break; + default: + *str++ = 'E'; + str += fmt_s32_dec(str, scale) -1; + } + *++str = '\0'; +}