diff --git a/Makefile.dep b/Makefile.dep
index bd95089a1d517b70e3ead8bee24bb16a35fce4d2..db312db47c297fdbe02be66984cb3f6fbd2b507f 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 0000000000000000000000000000000000000000..568008e1f7a061e3285d6e6abc7037f60b8ed83b
--- /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 <hauke.petersen@fu-berlin.de>
+ */
+
+#ifndef SECT_DATA_H
+#define SECT_DATA_H
+
+#include <stdint.h>
+#include <errno.h>
+
+#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 0000000000000000000000000000000000000000..48422e909a47d7cd428d10fa73825060ccc8d8c2
--- /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 0000000000000000000000000000000000000000..ca54fda178c1859fa456484208fafc4c26cf1b96
--- /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 <hauke.petersen@fu-berlin.de>
+ *
+ * @}
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+
+#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';
+}