From 396b76c03959c5449e5b90b695df96b729f86c86 Mon Sep 17 00:00:00 2001
From: daniel-k <github@daniel-krebs.net>
Date: Tue, 1 Sep 2015 17:05:23 +0200
Subject: [PATCH] drivers/at30tse75x: add device driver for AT30TSE75x
 temperature sensor

---
 Makefile.dep                        |   4 +
 drivers/at30tse75x/Makefile         |   1 +
 drivers/at30tse75x/at30tse75x.c     | 317 ++++++++++++++++++++++++++++
 drivers/include/at30tse75x.h        | 312 +++++++++++++++++++++++++++
 sys/shell/commands/Makefile         |   3 +
 sys/shell/commands/sc_at30tse75x.c  | 160 ++++++++++++++
 sys/shell/commands/shell_commands.c |   7 +
 tests/driver_at30tse75x/Makefile    |  12 ++
 tests/driver_at30tse75x/main.c      |  35 +++
 9 files changed, 851 insertions(+)
 create mode 100644 drivers/at30tse75x/Makefile
 create mode 100644 drivers/at30tse75x/at30tse75x.c
 create mode 100644 drivers/include/at30tse75x.h
 create mode 100644 sys/shell/commands/sc_at30tse75x.c
 create mode 100644 tests/driver_at30tse75x/Makefile
 create mode 100644 tests/driver_at30tse75x/main.c

diff --git a/Makefile.dep b/Makefile.dep
index 0c29e594ef..d2eb6eb02a 100644
--- a/Makefile.dep
+++ b/Makefile.dep
@@ -425,6 +425,10 @@ ifneq (,$(filter ltc4150,$(USEMODULE)))
     USEMODULE += xtimer
 endif
 
+ifneq (,$(filter at30tse75x,$(USEMODULE)))
+  USEMODULE += xtimer
+endif
+
 ifneq (,$(filter pthread,$(USEMODULE)))
     USEMODULE += xtimer
     USEMODULE += vtimer
diff --git a/drivers/at30tse75x/Makefile b/drivers/at30tse75x/Makefile
new file mode 100644
index 0000000000..48422e909a
--- /dev/null
+++ b/drivers/at30tse75x/Makefile
@@ -0,0 +1 @@
+include $(RIOTBASE)/Makefile.base
diff --git a/drivers/at30tse75x/at30tse75x.c b/drivers/at30tse75x/at30tse75x.c
new file mode 100644
index 0000000000..1ae6065c98
--- /dev/null
+++ b/drivers/at30tse75x/at30tse75x.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) Daniel Krebs
+ *
+ * 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.
+ */
+
+/**
+ * @file
+ * @brief       Driver for the AT30TSE75x temperature sensor with serial EEPROM
+ *
+ * @author      Daniel Krebs <github@daniel-krebs.net>
+ */
+
+
+#include "periph/i2c.h"
+#include "xtimer.h"
+
+#include "at30tse75x.h"
+
+#define ENABLE_DEBUG (0)
+#include "debug.h"
+
+static inline float temperature_to_float(uint16_t temp)
+{
+    /* Integer part is 8-bit signed */
+    int8_t temp_int = (temp & AT30TSE75X_INTEGER_MASK) >>
+                        AT30TSE75X_INTEGER_SHIFT;
+    /* Fractional part is multiple of 0.0625 */
+    uint8_t frac_multiplier = (temp & AT30TSE75X_FRACTIONAL_MASK) >>
+                        AT30TSE75X_FRACTIONAL_SHIFT;
+
+    return temp_int + (frac_multiplier * AT30TSE75X_FRACTIONAL_BASE);
+}
+
+static int at30tse75x_get_register(at30tse75x_t* dev, uint8_t reg, uint16_t* data)
+{
+    i2c_acquire(dev->i2c);
+    xtimer_spin(AT30TSE75X_BUS_FREE_TIME_US);
+    if(i2c_read_regs(dev->i2c, dev->addr, reg, (char*) data, 2) <= 0) {
+        DEBUG("[at30tse75x] Can't read register 0x%x\n", reg);
+        i2c_release(dev->i2c);
+        return -1;
+    }
+    i2c_release(dev->i2c);
+
+    return 0;
+}
+
+static int at30tse75x_set_register(at30tse75x_t* dev, uint8_t reg, uint16_t* data)
+{
+    i2c_acquire(dev->i2c);
+    xtimer_spin(AT30TSE75X_BUS_FREE_TIME_US);
+    if(i2c_write_regs(dev->i2c, dev->addr, reg, (char*) data, 2) <= 0) {
+        DEBUG("[at30tse75x] Can't write to register 0x%x\n", reg);
+        i2c_release(dev->i2c);
+        return -1;
+    }
+    i2c_release(dev->i2c);
+
+    return 0;
+}
+
+static int at30tse75x_reset(at30tse75x_t* dev)
+{
+    i2c_acquire(dev->i2c);
+    xtimer_spin(AT30TSE75X_BUS_FREE_TIME_US);
+    if(i2c_write_byte(dev->i2c, 0x00, AT30TSE75X_CMD__GENERAL_CALL_RESET) != 1) {
+        i2c_release(dev->i2c);
+        return -1;
+    }
+    i2c_release(dev->i2c);
+    /* Wait for reset to complete */
+    xtimer_usleep(500);
+    return 0;
+}
+
+int at30tse75x_get_config(at30tse75x_t* dev, uint8_t* data)
+{
+    i2c_acquire(dev->i2c);
+    xtimer_spin(AT30TSE75X_BUS_FREE_TIME_US);
+    if(i2c_read_reg(dev->i2c, dev->addr, AT30TSE75X_REG__CONFIG, (char*) data) <= 0) {
+        DEBUG("[at30tse75x] Can't read CONFIG register\n");
+        i2c_release(dev->i2c);
+        return -1;
+    }
+    i2c_release(dev->i2c);
+
+    return 0;
+}
+
+int at30tse75x_set_config(at30tse75x_t* dev, uint8_t data)
+{
+    i2c_acquire(dev->i2c);
+    xtimer_spin(AT30TSE75X_BUS_FREE_TIME_US);
+    if(i2c_write_reg(dev->i2c, dev->addr, AT30TSE75X_REG__CONFIG, (char) data) <= 0) {
+        DEBUG("[at30tse75x] Can't write to CONFIG register\n");
+        i2c_release(dev->i2c);
+        return -1;
+    }
+    i2c_release(dev->i2c);
+
+    return 0;
+}
+
+int at30tse75x_set_resolution(at30tse75x_t* dev, at30tse75x_resolution_t resolution)
+{
+    uint8_t config;
+
+    if(resolution < AT30TSE75X_RESOLUTION_9BIT ||
+       resolution > AT30TSE75X_RESOLUTION_12BIT) {
+        return -2;
+    }
+
+    if(at30tse75x_get_config(dev, &config) != 0) {
+        return -1;
+    }
+
+    config &= ~(AT30TSE75X_CONFIG__RESOLUTION_MASK);
+    config |= resolution << AT30TSE75X_CONFIG__RESOLUTION_SHIFT;
+
+    if(at30tse75x_set_config(dev, config) != 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+int at30tse75x_set_mode(at30tse75x_t* dev, at30tse75x_mode_t mode)
+{
+    uint8_t config;
+    if(at30tse75x_get_config(dev, &config) != 0) {
+        return -1;
+    }
+
+    switch(mode) {
+    case AT30TSE75X_MODE_ONE_SHOT:
+        config |= AT30TSE75X_CONFIG__SHUTDOWN_BIT;
+        /* Don't touch alarm mode */
+        break;
+    case AT30TSE75X_MODE_COMPARATOR:
+        config &= ~(AT30TSE75X_CONFIG__SHUTDOWN_BIT);
+        config &= ~(AT30TSE75X_CONFIG__ALARM_MODE_BIT);
+        break;
+    case AT30TSE75X_MODE_INTERRUPT:
+        config &= ~(AT30TSE75X_CONFIG__SHUTDOWN_BIT);
+        config |= AT30TSE75X_CONFIG__ALARM_MODE_BIT;
+        break;
+    default:
+        return -2;
+    }
+
+    if(at30tse75x_set_config(dev, config) != 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+int at30tse75x_set_alarm_polarity(at30tse75x_t* dev, at30tse75x_alarm_polatity_t polarity)
+{
+    uint8_t config;
+    if(at30tse75x_get_config(dev, &config) != 0) {
+        return -1;
+    }
+
+    switch(polarity) {
+    case AT30TSE75X_ALARM_ACTIVE_HIGH:
+        config |= AT30TSE75X_CONFIG__ALERT_POL_BIT;
+        break;
+    case AT30TSE75X_ALARM_ACTIVE_LOW:
+        config &= ~(AT30TSE75X_CONFIG__ALERT_POL_BIT);
+        break;
+    default:
+        return -2;
+    }
+
+    if(at30tse75x_set_config(dev, config) != 0) {
+            return -1;
+        }
+
+    return 0;
+}
+
+int at30tse75x_set_fault_tolerance(at30tse75x_t* dev, at30tse75x_fault_tolerance_t tolerance)
+{
+    if(tolerance < AT30TSE75X_ALARM_AFTER_1 ||
+       tolerance > AT30TSE75X_ALARM_AFTER_6) {
+        return -2;
+    }
+
+    uint8_t config;
+    if(at30tse75x_get_config(dev, &config) != 0) {
+        return -1;
+    }
+
+    config &= ~(AT30TSE75X_CONFIG__FTQ_MASK);
+    config |= tolerance << AT30TSE75X_CONFIG__FTQ_SHIFT;
+
+    if(at30tse75x_set_config(dev, config) != 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+int at30tse75x_set_limit_low(at30tse75x_t* dev, int8_t t_low)
+{
+    uint16_t tmp = (t_low << 8) | (0x00);
+    return at30tse75x_set_register(dev, AT30TSE75X_REG__LIMIT_LOW, &tmp);
+}
+
+int at30tse75x_set_limit_high(at30tse75x_t* dev, int8_t t_high)
+{
+    uint16_t tmp = (t_high << 8) | (0x00);
+    return at30tse75x_set_register(dev, AT30TSE75X_REG__LIMIT_HIGH, &tmp);
+}
+
+int at30tse75x_save_config(at30tse75x_t* dev)
+{
+    i2c_acquire(dev->i2c);
+    xtimer_spin(AT30TSE75X_BUS_FREE_TIME_US);
+    if(i2c_write_byte(dev->i2c, dev->addr, AT30TSE75X_CMD__SAVE_TO_NVRAM) != 1) {
+        i2c_release(dev->i2c);
+        return -1;
+    }
+    i2c_release(dev->i2c);
+    /* Wait for copy to complete */
+    xtimer_usleep(5000);
+    return 0;
+}
+
+int at30tse75x_restore_config(at30tse75x_t* dev)
+{
+    i2c_acquire(dev->i2c);
+    xtimer_spin(AT30TSE75X_BUS_FREE_TIME_US);
+    if(i2c_write_byte(dev->i2c, dev->addr, AT30TSE75X_CMD__RESTORE_FROM_NVRAM) != 1) {
+        i2c_release(dev->i2c);
+        return -1;
+    }
+    i2c_release(dev->i2c);
+    /* Wait for copy to complete */
+    xtimer_usleep(200);
+    return 0;
+}
+
+int at30tse75x_get_temperature(at30tse75x_t* dev, float* temperature)
+{
+    uint16_t tmp;
+    uint8_t config;
+
+    if(at30tse75x_get_config(dev, &config) != 0) {
+        return -1;
+    }
+
+    /* If sensor is shutdown trigger One-Shot mode*/
+    if(config & AT30TSE75X_CONFIG__SHUTDOWN_BIT) {
+
+        config |= AT30TSE75X_CONFIG__OS_BIT;
+        if(at30tse75x_set_config(dev, config) != 0) {
+            return -1;
+        }
+
+        /* Use resolution to calculate conversion time */
+         uint8_t resolution = (config & AT30TSE75X_CONFIG__RESOLUTION_MASK) >>
+                                   AT30TSE75X_CONFIG__RESOLUTION_SHIFT;
+
+        /* Wait until conversion is finished */
+        xtimer_usleep((uint32_t)(25000 << resolution));
+    }
+
+    /* Read temperature */
+    if(at30tse75x_get_register(dev, AT30TSE75X_REG__TEMPERATURE, &tmp) != 0) {
+        return -1;
+    }
+
+    /* Convert fixed point to float */
+    *temperature = temperature_to_float(tmp);
+
+    return 0;
+}
+
+int at30tse75x_init(at30tse75x_t* dev, i2c_t i2c, i2c_speed_t speed, uint8_t addr)
+{
+    uint8_t config;
+
+    dev->i2c = i2c;
+
+    if( (addr < 0x48) || (addr > 0x4f) ) {
+        DEBUG("[at30tse75x] Invalid address\n");
+        return -2;
+    }
+    dev->addr = addr;
+
+    i2c_acquire(dev->i2c);
+    if(i2c_init_master(dev->i2c, speed) != 0) {
+        DEBUG("[at30tse75x] Can't initialize I2C master\n");
+        i2c_release(dev->i2c);
+        return -1;
+    }
+    i2c_release(dev->i2c);
+
+    /* Reset the device */
+    if(at30tse75x_reset(dev) != 0) {
+        return -1;
+    }
+
+    /* Poll the device, fail if unavailable */
+    if(at30tse75x_get_config(dev, &config) != 0) {
+        return -1;
+    }
+
+    DEBUG("[at30tse75x] Config: 0x%x\n", config);
+
+    return 0;
+}
diff --git a/drivers/include/at30tse75x.h b/drivers/include/at30tse75x.h
new file mode 100644
index 0000000000..1c35d8ff53
--- /dev/null
+++ b/drivers/include/at30tse75x.h
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) Daniel Krebs
+ *
+ * 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    at30tse75x AT30TSE75x temperature sensor with EEPROM
+ * @ingroup     drivers
+ *
+ * The connection between the MCU and the AT30TSE75x is based on the
+ * I2C-interface. There are 3 versions of this IC, with either 2/4/8 Kb of
+ * EEPROM.
+ *
+ * @{
+ *
+ * @file
+ * @brief       Driver for the AT30TSE75x temperature sensor with serial EEPROM
+ *
+ * @author      Daniel Krebs <github@daniel-krebs.net>
+ */
+
+#ifndef AT30TSE75X_H_
+#define AT30TSE75X_H_
+
+#include <stdint.h>
+#include "periph/i2c.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @name AT30TSE75x I2C addresses
+ * @{
+ */
+#define AT30TSE75X_TEMP_ADDR            (0x48)
+#define AT30TSE75X_EEPROM_ADDR          (0x50)
+/** @} */
+
+/**
+ * @name AT30TSE75x register addresses
+ * @{
+ */
+#define AT30TSE75X_REG__TEMPERATURE     (0x00)
+#define AT30TSE75X_REG__CONFIG          (0x01)
+#define AT30TSE75X_REG__LIMIT_LOW       (0x02)
+#define AT30TSE75X_REG__LIMIT_HIGH      (0x03)
+#define AT30TSE75X_REG__NV_CONFIG       (0x11)
+#define AT30TSE75X_REG__NV_LIMIT_LOW    (0x12)
+#define AT30TSE75X_REG__NV_LIMIR_HIGH   (0x13)
+/** @} */
+
+/**
+ * @name AT30TSE75x number formatting
+ * @{
+ */
+#define AT30TSE75X_INTEGER_MASK         (0x00ff)
+#define AT30TSE75X_INTEGER_SHIFT        (0)
+#define AT30TSE75X_FRACTIONAL_MASK      (0xf000)
+#define AT30TSE75X_FRACTIONAL_SHIFT     (12)
+#define AT30TSE75X_FRACTIONAL_BASE      (0.0625f)
+/** @} */
+
+
+/**
+ * @name AT30TSE75x configuration register
+ *
+ * Only upper byte can be read/written, so treat as 8-bit register.
+ * @{
+ */
+#define AT30TSE75X_CONFIG__OS_BIT               (1 << 7)
+#define AT30TSE75X_CONFIG__RESOLUTION_MASK      (0x60)
+#define AT30TSE75X_CONFIG__RESOLUTION_SHIFT     (5)
+#define AT30TSE75X_CONFIG__FTQ_MASK             (0x18)
+#define AT30TSE75X_CONFIG__FTQ_SHIFT            (3)
+#define AT30TSE75X_CONFIG__ALERT_POL_BIT        (1 << 2)
+#define AT30TSE75X_CONFIG__ALARM_MODE_BIT       (1 << 1)
+#define AT30TSE75X_CONFIG__SHUTDOWN_BIT         (1 << 0)
+/** @} */
+
+/**
+ * @name AT30TSE75x commands
+ *
+ * @{
+ */
+#define AT30TSE75X_CMD__SAVE_TO_NVRAM           (0x48)
+#define AT30TSE75X_CMD__RESTORE_FROM_NVRAM      (0xb8)
+#define AT30TSE75X_CMD__GENERAL_CALL_RESET      (0x06)
+#define AT30TSE75X_CMD__GENERAL_CALL_RELATCH    (0x04)
+/** @} */
+
+/**
+ * @name AT30TSE75x constants
+ *
+ * @{
+ */
+#define AT30TSE75X_BUS_FREE_TIME_US             (1U)
+/** @} */
+
+/**
+  * @name AT30TSE75x configuration types
+  *
+  * @brief   Temperature resolution
+  *
+  * @{
+  */
+typedef enum {
+    AT30TSE75X_RESOLUTION_9BIT = 0,
+    AT30TSE75X_RESOLUTION_10BIT = 1,
+    AT30TSE75X_RESOLUTION_11BIT = 2,
+    AT30TSE75X_RESOLUTION_12BIT = 3
+} at30tse75x_resolution_t;
+/** @} */
+
+/**
+  * @brief   Operation mode
+  *
+  * The device can run in continous or one-shot mode. While in one-shot mode it
+  * is effectively shutdown and only wakes up to perform a single measurement.
+  * When in comparator or interrupt mode, the device samples contiously the
+  * temperature and sets the ALERT pin according to the chosen mode.
+  * @{
+  */
+typedef enum {
+    AT30TSE75X_MODE_COMPARATOR,
+    AT30TSE75X_MODE_INTERRUPT,
+    AT30TSE75X_MODE_ONE_SHOT
+} at30tse75x_mode_t;
+/** @} */
+
+/**
+  * @brief   After how many limit exceeding measurements the ALERT pin is set
+  * @{
+  */
+typedef enum {
+    AT30TSE75X_ALARM_AFTER_1 = 0,
+    AT30TSE75X_ALARM_AFTER_2 = 1,
+    AT30TSE75X_ALARM_AFTER_4 = 2,
+    AT30TSE75X_ALARM_AFTER_6 = 3
+} at30tse75x_fault_tolerance_t;
+/** @} */
+
+/**
+  * @brief   Polarity of the ALERT pin
+  * @{
+  */
+typedef enum {
+    AT30TSE75X_ALARM_ACTIVE_LOW,
+    AT30TSE75X_ALARM_ACTIVE_HIGH
+} at30tse75x_alarm_polatity_t;
+/** @} */
+
+/**
+  * @brief   Device descriptor for a AT30TSE75x device
+  * @{
+  */
+typedef struct {
+    i2c_t i2c;          /**< I2C device that sensor is connected to */
+    uint8_t addr;       /**< I2C address of this particular sensor */
+} at30tse75x_t;
+/** @} */
+
+/**
+ * @brief   Initialize a AT30TSE75x device
+ *
+ * @param[out] dev          device descriptor
+ * @param[in] i2c           I2C bus the device is connected to
+ * @param[in] speed         I2C speed to use
+ * @param[in] addr          I2C address of the device
+ *
+ * The valid address range is 0x48 - 0x4f depending on the configuration of the
+ * address pins A0-A2.
+ *
+ * @return                   0 on success
+ * @return                  -1 on error
+ * @return                  -2 on invalid address
+ */
+int at30tse75x_init(at30tse75x_t* dev, i2c_t i2c, i2c_speed_t speed, uint8_t addr);
+
+/**
+ * @brief   Save configuration register to non-volatile backup register
+ *
+ * @param[in] dev           device descriptor
+ *
+ * @return                   0 on success
+ * @return                  -1 on error
+ */
+int at30tse75x_save_config(at30tse75x_t* dev);
+
+/**
+ * @brief   Restore configuration register from non-volatile backup register
+ *
+ * @param[in] dev           device descriptor
+ *
+ * @return                   0 on success
+ * @return                  -1 on error
+ */
+int at30tse75x_restore_config(at30tse75x_t* dev);
+
+/**
+ * @brief   Get content of configuration register
+ *
+ * @param[in] dev           device descriptor
+ * @param[out] data         buffer where config register will be written to
+ *
+ * @return                   0 on success
+ * @return                  -1 on error
+ */
+int at30tse75x_get_config(at30tse75x_t* dev, uint8_t* data);
+
+/**
+ * @brief   Set content of configuration register
+ *
+ * @param[in] dev           device descriptor
+ * @param[in] data          new value for configuration register
+ *
+ * @return                   0 on success
+ * @return                  -1 on error
+ */
+int at30tse75x_set_config(at30tse75x_t* dev, uint8_t data);
+
+/**
+ * @brief   Set temperature resolution
+ *
+ * @param[in] dev           device descriptor
+ * @param[in] resolution    temperature resolution
+ *
+ * @return                   0 on success
+ * @return                  -1 on error
+ * @return                  -2 on bad user input
+ */
+int at30tse75x_set_resolution(at30tse75x_t* dev, at30tse75x_resolution_t resolution);
+
+/**
+ * @brief   Set operation mode
+ *
+ * @param[in] dev           device descriptor
+ * @param[in] mode          operation mode
+ *
+ * @return                   0 on success
+ * @return                  -1 on device error
+ * @return                  -2 on bad user input
+ */
+int at30tse75x_set_mode(at30tse75x_t* dev, at30tse75x_mode_t mode);
+
+/**
+ * @brief   Set polarity of ALERT pin
+ *
+ * @param[in] dev           device descriptor
+ * @param[in] polarity      polarity of ALERT pin
+ *
+ * @return                   0 on success
+ * @return                  -1 on device error
+ * @return                  -2 on bad user input
+ */
+int at30tse75x_set_alarm_polarity(at30tse75x_t* dev, at30tse75x_alarm_polatity_t polarity);
+
+/**
+ * @brief   Set tolerance to outlying measurements
+ *
+ * @param[in] dev           device descriptor
+ * @param[in] tolerance     tolerance
+ *
+ * @return                   0 on success
+ * @return                  -1 on device error
+ * @return                  -2 on bad user input
+ */
+int at30tse75x_set_fault_tolerance(at30tse75x_t* dev, at30tse75x_fault_tolerance_t tolerance);
+
+/**
+ * @brief   Set T_Low limit
+ *
+ * @param[in] dev           device descriptor
+ * @param[in] t_low         lower temperature limit
+ *
+ * @return                   0 on success
+ * @return                  -1 on device error
+ * @return                  -2 on bad user input
+ */
+int at30tse75x_set_limit_low(at30tse75x_t* dev, int8_t t_low);
+
+/**
+ * @brief   Set T_High limit
+ *
+ * @param[in] dev           device descriptor
+ * @param[in] t_high        upper temperature limit
+ *
+ * @return                   0 on success
+ * @return                  -1 on error
+ */
+int at30tse75x_set_limit_high(at30tse75x_t* dev, int8_t t_high);
+
+/**
+ * @brief   Get measured temperature
+ *
+ * @param[in] dev           device descriptor
+ * @param[out] temperature  float buffer where temperature will be written to
+ *
+ * @return                   0 on success
+ * @return                  -1 on error
+ */
+int at30tse75x_get_temperature(at30tse75x_t* dev, float* temperature);
+
+#ifdef __cplusplus
+}
+#endif
+
+/** @} */
+#endif /* AT30TSE75X_H_ */
diff --git a/sys/shell/commands/Makefile b/sys/shell/commands/Makefile
index 4e6f14cc65..27ef3c0a16 100644
--- a/sys/shell/commands/Makefile
+++ b/sys/shell/commands/Makefile
@@ -38,6 +38,9 @@ endif
 ifneq (,$(filter lsm303dlhc,$(USEMODULE)))
   SRC += sc_lsm303dlhc.c
 endif
+ifneq (,$(filter at30tse75x,$(USEMODULE)))
+    SRC += sc_at30tse75x.c
+endif
 ifneq (,$(filter gnrc_netif,$(USEMODULE)))
   SRC += sc_netif.c
 endif
diff --git a/sys/shell/commands/sc_at30tse75x.c b/sys/shell/commands/sc_at30tse75x.c
new file mode 100644
index 0000000000..32304f43aa
--- /dev/null
+++ b/sys/shell/commands/sc_at30tse75x.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2015 Daniel Krebs
+ *
+ * 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_shell_commands
+ * @{
+ *
+ * @file
+ * @brief       Provides shell commands to test AT30TSE75x temperature sensor
+ *
+ * @author      Daniel Krebs <github@daniel-krebs.net>
+ *
+ * @}
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include "at30tse75x.h"
+
+#ifdef MODULE_AT30TSE75X
+
+static bool initialized = false;
+static at30tse75x_t dev;
+
+int _at30tse75x_handler(int argc, char **argv)
+{
+    if(argc <= 1) {
+        printf("Usage: %s init|read|mode|resolution|save|restore|config\n", argv[0]);
+        return -1;
+    }
+
+    if(strcmp(argv[1], "init") == 0) {
+        if(argc == 2) {
+            printf("Usage: %s init #I2C [addr]\n"
+                   "  e.g. %s init 0\n", argv[0], argv[0]);
+            return -1;
+        }
+
+        int error;
+        unsigned addr = 0x48;   /* default to A0-A2 connected to GND */
+
+        /* Try to parse i2c dev */
+        i2c_t i2c_dev = (i2c_t) strtol(argv[2], &argv[1] + 1, 10);
+
+        /* Address given */
+        if(argc == 4) {
+            char* hex = strstr(argv[3], "0x");
+            if(hex) {
+                addr = strtoul(hex+2, NULL, 16);
+            }
+        }
+
+        error = at30tse75x_init(&dev, i2c_dev, I2C_SPEED_NORMAL, addr);
+        if (error) {
+            printf("Error initializing AT30TSE75x sensor on I2C #%u @ 0x%x\n", i2c_dev, addr);
+            initialized = false;
+            return 1;
+        }
+        else {
+            printf("Initialized AT30TSE75x sensor on I2C #%u @ 0x%x\n", i2c_dev, addr);
+            initialized = true;
+        }
+    } else {
+        if(!initialized) {
+            puts("Please initialize first");
+            return -1;
+        }
+        if(strcmp(argv[1], "read") == 0) {
+            float temperature;
+            if(at30tse75x_get_temperature(&dev, &temperature) != 0) {
+                puts("Reading temperature failed");
+                return -1;
+            }
+            printf("Temperature: %i.%03u °C\n",
+                    (int)temperature,
+                    (unsigned)((temperature - (int)temperature) * 1000));
+        } else if(strcmp(argv[1], "mode") == 0) {
+            if(argc == 2) {
+                printf("Usage: %s mode one-shot|comparator|interrupt\n", argv[0]);
+                return -1;
+            }
+            at30tse75x_mode_t mode;
+            if(strcmp(argv[2], "one-shot") == 0) {
+                mode = AT30TSE75X_MODE_ONE_SHOT;
+            } else if(strcmp(argv[2], "comparator") == 0) {
+                mode = AT30TSE75X_MODE_COMPARATOR;
+            } else if(strcmp(argv[2], "interrupt") == 0) {
+                mode = AT30TSE75X_MODE_INTERRUPT;
+            } else {
+                puts("Invalid mode");
+                return -1;
+            }
+            if(at30tse75x_set_mode(&dev, mode) != 0) {
+                return -1;
+            }
+            printf("Mode set to %s\n", argv[2]);
+        } else if(strcmp(argv[1], "resolution") == 0) {
+            if(argc == 2) {
+                printf("Usage: %s resolution 9|10|11|12\n", argv[0]);
+                return -1;
+            }
+            unsigned resolution = strtoul(argv[2], NULL, 10);
+            if(at30tse75x_set_resolution(&dev, resolution - 9) != 0) {
+                return -1;
+            }
+            printf("Resolution set to %u bits\n", resolution);
+        } else if(strcmp(argv[1], "save") == 0) {
+            uint8_t config;
+            if(at30tse75x_get_config(&dev, &config) != 0) {
+                return -1;
+            }
+            if(at30tse75x_save_config(&dev) != 0) {
+                return -1;
+            }
+            printf("Config (0x%x) saved\n", config);
+        } else if(strcmp(argv[1], "restore") == 0) {
+            uint8_t config;
+            if(at30tse75x_restore_config(&dev) != 0) {
+                return -1;
+            }
+            if(at30tse75x_get_config(&dev, &config) != 0) {
+                return -1;
+            }
+            printf("Config restored to 0x%x\n", config);
+
+        } else if(strcmp(argv[1], "config") == 0) {
+            if(argc == 2) {
+                uint8_t config;
+                if(at30tse75x_get_config(&dev, &config) != 0) {
+                    return -1;
+                }
+                printf("Config: 0x%x\n", config);
+            } else {
+                /* Try to parse config in hex format */
+                uint8_t config;
+                char* hex = strstr(argv[2], "0x");
+                if(!hex) {
+                    printf("Usage: %s config 0x__"
+                           "  to set config\n", argv[0]);
+                    return -1;
+                }
+                config = strtoul(hex+2, NULL, 16);
+                if(at30tse75x_set_config(&dev, config) != 0) {
+                    return -1;
+                }
+                printf("Config set to: 0x%x\n", config);
+            }
+        }
+    }
+    return 0;
+}
+
+#endif /* MODULE_AT30TSE75X */
diff --git a/sys/shell/commands/shell_commands.c b/sys/shell/commands/shell_commands.c
index c5978a823f..c1b675ea10 100644
--- a/sys/shell/commands/shell_commands.c
+++ b/sys/shell/commands/shell_commands.c
@@ -69,6 +69,10 @@ extern int _get_current_handler(int argc, char **argv);
 extern int _reset_current_handler(int argc, char **argv);
 #endif
 
+#ifdef MODULE_AT30TSE75X
+extern int _at30tse75x_handler(int argc, char **argv);
+#endif
+
 #if FEATURE_PERIPH_RTC
 extern int _rtc_handler(int argc, char **argv);
 #endif
@@ -167,6 +171,9 @@ const shell_command_t _shell_command_list[] = {
     {"cur", "Prints current and average power consumption.", _get_current_handler},
     {"rstcur", "Resets coulomb counter.", _reset_current_handler},
 #endif
+#ifdef MODULE_AT30TSE75X
+    {"at30tse75x", "Test AT30TSE75X temperature sensor", _at30tse75x_handler},
+#endif
 #ifdef MODULE_MCI
     {DISK_READ_SECTOR_CMD, "Reads the specified sector of inserted memory card", _read_sector},
     {DISK_READ_BYTES_CMD, "Reads the specified bytes from inserted memory card", _read_bytes},
diff --git a/tests/driver_at30tse75x/Makefile b/tests/driver_at30tse75x/Makefile
new file mode 100644
index 0000000000..45917d2b2a
--- /dev/null
+++ b/tests/driver_at30tse75x/Makefile
@@ -0,0 +1,12 @@
+APPLICATION = driver_at30tse75x
+include ../Makefile.tests_common
+
+FEATURES_REQUIRED = periph_i2c
+
+USEMODULE += at30tse75x
+USEMODULE += shell
+USEMODULE += shell_commands
+
+CFLAGS += -DDEVELHELP
+
+include $(RIOTBASE)/Makefile.include
diff --git a/tests/driver_at30tse75x/main.c b/tests/driver_at30tse75x/main.c
new file mode 100644
index 0000000000..6a0f24bafa
--- /dev/null
+++ b/tests/driver_at30tse75x/main.c
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 Daniel Krebs
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     tests
+ * @{
+ *
+ * @file
+ * @brief       Test application for AT30TSE75x temperature sensor
+ *
+ * @author      Daniel Krebs <github@daniel-krebs.net>
+ *
+ * @}
+ */
+
+#include <stdio.h>
+
+#include "shell.h"
+#include "shell_commands.h"
+
+int main(void)
+{
+    puts("AT30TSE75x device driver test");
+
+    char line_buf[SHELL_DEFAULT_BUFSIZE];
+
+    shell_run(NULL, line_buf, SHELL_DEFAULT_BUFSIZE);
+
+    return 0;
+}
-- 
GitLab