From 25ce12f3ef0e0a49f5a8c9a36dbe0af6ad7d0d65 Mon Sep 17 00:00:00 2001
From: Alexandre Abadie <alexandre.abadie@inria.Fr>
Date: Wed, 18 Jan 2017 22:12:57 +0100
Subject: [PATCH] drivers/tsl2561: initial implementation + saul support

---
 drivers/Makefile.include                    |   3 +
 drivers/include/tsl2561.h                   | 113 ++++++++
 drivers/tsl2561/Makefile                    |   3 +
 drivers/tsl2561/include/tsl2561_internals.h | 108 ++++++++
 drivers/tsl2561/include/tsl2561_params.h    |  79 ++++++
 drivers/tsl2561/tsl2561.c                   | 291 ++++++++++++++++++++
 drivers/tsl2561/tsl2561_saul.c              |  40 +++
 sys/auto_init/auto_init.c                   |   4 +
 sys/auto_init/saul/auto_init_tsl2561.c      |  75 +++++
 9 files changed, 716 insertions(+)
 create mode 100644 drivers/include/tsl2561.h
 create mode 100644 drivers/tsl2561/Makefile
 create mode 100644 drivers/tsl2561/include/tsl2561_internals.h
 create mode 100644 drivers/tsl2561/include/tsl2561_params.h
 create mode 100644 drivers/tsl2561/tsl2561.c
 create mode 100644 drivers/tsl2561/tsl2561_saul.c
 create mode 100644 sys/auto_init/saul/auto_init_tsl2561.c

diff --git a/drivers/Makefile.include b/drivers/Makefile.include
index f0e9712f62..9af613263e 100644
--- a/drivers/Makefile.include
+++ b/drivers/Makefile.include
@@ -73,6 +73,9 @@ endif
 ifneq (,$(filter bme280,$(USEMODULE)))
     USEMODULE_INCLUDES += $(RIOTBASE)/drivers/bme280/include
 endif
+ifneq (,$(filter tsl2561,$(USEMODULE)))
+    USEMODULE_INCLUDES += $(RIOTBASE)/drivers/tsl2561/include
+endif
 ifneq (,$(filter cc2420,$(USEMODULE)))
     USEMODULE_INCLUDES += $(RIOTBASE)/drivers/cc2420/include
 endif
diff --git a/drivers/include/tsl2561.h b/drivers/include/tsl2561.h
new file mode 100644
index 0000000000..a78c98750c
--- /dev/null
+++ b/drivers/include/tsl2561.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2016 Inria
+ *
+ * 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    drivers_tsl2561 TSL2561
+ * @ingroup     drivers_sensors
+ * @brief       Device driver interface for the illuminance  TSL2561 sensor
+ * @{
+ *
+ * @file
+ * @brief       Device driver interface for the illuminance TSL2561 sensor.
+ *
+ * @author      Alexandre Abadie <alexandre.abadie@inria.fr>
+ */
+
+#ifndef TSL2561_H_
+#define TSL2561_H_
+
+#include "saul.h"
+#include "periph/i2c.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @name TSL2561 I2C addresses
+ * @{
+ */
+#define TSL2561_ADDR_LOW                  (0x29)
+#define TSL2561_ADDR_FLOAT                (0x39)
+#define TSL2561_ADDR_HIGH                 (0x49)
+/** @} */
+
+/**
+ * @name TSL2561 integration times
+ * @{
+ */
+#define TSL2561_INTEGRATIONTIME_13MS      (0x00)    /* 13.7ms */
+#define TSL2561_INTEGRATIONTIME_101MS     (0x01)    /* 101ms  */
+#define TSL2561_INTEGRATIONTIME_402MS     (0x02)    /* 402ms  */
+#define TSL2561_INTEGRATIONTIME_NA        (0x03)    /* N/A    */
+/** @} */
+
+/**
+ * @name TSL2561 gains
+ * @{
+ */
+#define TSL2561_GAIN_1X                   (0x00)
+#define TSL2561_GAIN_16X                  (0x10)
+/** @} */
+
+/**
+ * @name tsl2561 driver initialization return codes
+ * @{
+ */
+#define TSL2561_OK                        (0)
+#define TSL2561_NOI2C                     (-1)
+#define TSL2561_BADDEV                    (-2)
+/** @} */
+
+/**
+ * @brief Device descriptor for the TSL2561 sensor
+ */
+typedef struct {
+    i2c_t i2c_dev;                     /**< I2C device which is used */
+    uint8_t addr;                      /**< address on I2C bus */
+    uint8_t gain;                      /**< gain */
+    uint8_t integration;               /**< integration time */
+} tsl2561_t;
+
+
+/**
+ * @brief Device initialization parameters
+ */
+typedef tsl2561_t tsl2561_params_t;
+
+/**
+ * @brief Initialize the given TSL2561 device
+ *
+ * @param[out] dev          Initialized device descriptor of BMP180 device
+ * @param[in]  i2c          I2C bus the sensor is connected to
+ * @param[in]  addr         I2C address of the sensor on the I2C bus
+ * @param[in]  gain         TSL2561 gain
+ * @param[in]  integration  TSL2561 integration time
+ *
+ * @return                  0 on success
+ * @return                  -1 if given I2C is not available
+ * @return                  -2 if not a TSL2561 sensor
+ */
+int tsl2561_init(tsl2561_t *dev, i2c_t i2c, uint8_t addr,
+                 uint8_t gain, uint8_t integration);
+
+/**
+ * @brief Read illuminance value from the given TSL2561 device, returned in lx.
+ *
+ * @param[in]  dev          Device descriptor of TSL2561 device to read from
+ *
+ * @return                  Illuminance in Lux (lx)
+ */
+uint16_t tsl2561_read_illuminance(tsl2561_t *dev);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TSL2561_H_ */
+/** @} */
diff --git a/drivers/tsl2561/Makefile b/drivers/tsl2561/Makefile
new file mode 100644
index 0000000000..9457af6bc6
--- /dev/null
+++ b/drivers/tsl2561/Makefile
@@ -0,0 +1,3 @@
+MODULE = tsl2561
+
+include $(RIOTBASE)/Makefile.base
diff --git a/drivers/tsl2561/include/tsl2561_internals.h b/drivers/tsl2561/include/tsl2561_internals.h
new file mode 100644
index 0000000000..6d0e1932cd
--- /dev/null
+++ b/drivers/tsl2561/include/tsl2561_internals.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2016 Inria
+ *
+ * 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     drivers_tsl2561
+ * @brief       Internal addresses, registers, constants for the TSL2561 sensor.
+ * @{
+ *
+ * @file
+ * @brief       Internal addresses, registers, constants for the TSL2561 sensor.
+ *
+ * @author      Alexandre Abadie <alexandre.abadie@inria.fr>
+ */
+
+#ifndef TSL2561_REGS_H_
+#define TSL2561_REGS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @name TSL2561 identifier
+ * @{
+ */
+#define TSL2561_ID                        (0x50)
+/** @} */
+
+/**
+ * @name TSL2561 internals registers
+ * @{
+ */
+#define TSL2561_REGISTER_CONTROL          (0x00)
+#define TSL2561_REGISTER_TIMING           (0x01)
+#define TSL2561_REGISTER_THRESHOLDLOW     (0x02)
+#define TSL2561_REGISTER_THRESHOLDHIGH    (0x04)
+#define TSL2561_REGISTER_INTERRUPT        (0x06)
+#define TSL2561_REGISTER_ID               (0x0A)
+#define TSL2561_REGISTER_CHAN0            (0x0C)
+#define TSL2561_REGISTER_CHAN1            (0x0E)
+/** @} */
+
+/**
+ * @name TSL2561 commands
+ * @{
+ */
+#define TSL2561_COMMAND_MODE              (0x80)
+#define TSL2561_COMMAND_CLEAR             (0x40)
+#define TSL2561_COMMAND_WORD              (0x20)
+#define TSL2561_COMMAND_BLOCK             (0x10)
+/** @} */
+
+/**
+ * @name TSL2561 controls
+ * @{
+ */
+#define TSL2561_CONTROL_POWERON           (0x03)
+#define TSL2561_CONTROL_POWEROFF          (0x00)
+/** @} */
+
+/**
+ * @name Internals constants
+ * @{
+ */
+#define TSL2561_LUXSCALE                  (14)      /* use 2e14 scaling */
+#define TSL2561_RATIOSCALE                (9)       /* use 2e9 scaling */
+#define TSL2561_CHSCALE                   (10)      /* use 2e10 scaling on
+                                                     * channel values by */
+#define TSL2561_CHSCALE_TINT0             (0x7517)
+#define TSL2561_CHSCALE_TINT1             (0x0FE7)
+
+#define TSL2561_K1T                       (0x0040)
+#define TSL2561_B1T                       (0x01f2)
+#define TSL2561_M1T                       (0x01be)
+#define TSL2561_K2T                       (0x0080)
+#define TSL2561_B2T                       (0x0214)
+#define TSL2561_M2T                       (0x02d1)
+#define TSL2561_K3T                       (0x00c0)
+#define TSL2561_B3T                       (0x023f)
+#define TSL2561_M3T                       (0x037b)
+#define TSL2561_K4T                       (0x0100)
+#define TSL2561_B4T                       (0x0270)
+#define TSL2561_M4T                       (0x03fe)
+#define TSL2561_K5T                       (0x0138)
+#define TSL2561_B5T                       (0x016f)
+#define TSL2561_M5T                       (0x01fc)
+#define TSL2561_K6T                       (0x019a)
+#define TSL2561_B6T                       (0x00d2)
+#define TSL2561_M6T                       (0x00fb)
+#define TSL2561_K7T                       (0x029a)
+#define TSL2561_B7T                       (0x0018)
+#define TSL2561_M7T                       (0x0012)
+#define TSL2561_K8T                       (0x029a)
+#define TSL2561_B8T                       (0x0000)
+#define TSL2561_M8T                       (0x0000)
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TSL2561_REGS_H_ */
+/** @} */
diff --git a/drivers/tsl2561/include/tsl2561_params.h b/drivers/tsl2561/include/tsl2561_params.h
new file mode 100644
index 0000000000..7fc4ae2339
--- /dev/null
+++ b/drivers/tsl2561/include/tsl2561_params.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2017 Inria
+ *
+ * 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     drivers_tsl2561
+ *
+ * @{
+ * @file
+ * @brief       Default configuration for TSL2561
+ *
+ * @author      Alexandre Abadie <alexandre.abadie@inria.fr>
+ */
+
+#ifndef TSL2561_PARAMS_H
+#define TSL2561_PARAMS_H
+
+#include "saul_reg.h"
+#include "tsl2561.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   Set default configuration parameters for the TSL2561
+ * @{
+ */
+#ifndef TSL2561_PARAM_I2C_DEV
+#define TSL2561_PARAM_I2C_DEV         I2C_DEV(0)
+#endif
+#ifndef TSL2561_PARAM_ADDR
+#define TSL2561_PARAM_ADDR            TSL2561_ADDR_FLOAT
+#endif
+#ifndef TSL2561_PARAM_GAIN
+#define TSL2561_PARAM_GAIN            TSL2561_GAIN_1X
+#endif
+#ifndef TSL2561_PARAM_INTEGRATION
+#define TSL2561_PARAM_INTEGRATION     TSL2561_INTEGRATIONTIME_402MS
+#endif
+
+#define TSL2561_PARAMS_DEFAULT        { .i2c_dev     = TSL2561_PARAM_I2C_DEV, \
+                                        .addr        = TSL2561_PARAM_ADDR,    \
+                                        .gain        = TSL2561_PARAM_GAIN,    \
+                                        .integration = TSL2561_PARAM_INTEGRATION }
+/**@}*/
+
+/**
+ * @brief   Configure TSL2561
+ */
+static const tsl2561_params_t tsl2561_params[] =
+{
+#ifdef TSL2561_PARAMS_CUSTOM
+    TSL2561_PARAMS_CUSTOM,
+#else
+    TSL2561_PARAMS_DEFAULT,
+#endif
+};
+
+/**
+ * @brief   Allocate and configure entries to the SAUL registry
+ */
+saul_reg_info_t tsl2561_saul_reg_info[] =
+{
+    {
+        .name= "tsl2561-illuminance"
+    }
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // TSL2561_PARAMS_H
+/** @} */
diff --git a/drivers/tsl2561/tsl2561.c b/drivers/tsl2561/tsl2561.c
new file mode 100644
index 0000000000..51116baf20
--- /dev/null
+++ b/drivers/tsl2561/tsl2561.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2016 Inria
+ *
+ * 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     drivers_tsl2561
+ * @{
+ *
+ * @file
+ * @brief       Device driver implementation for the TSL2561 Luminosity sensor.
+ *
+ * @author      Alexandre Abadie <alexandre.abadie@inria.fr>
+ *
+ * @}
+ */
+
+#include <math.h>
+#include <string.h>
+
+#include "log.h"
+#include "tsl2561.h"
+#include "tsl2561_internals.h"
+#include "periph/i2c.h"
+#include "xtimer.h"
+
+#define ENABLE_DEBUG        (0)
+#include "debug.h"
+
+/* internal helpers */
+static void _enable(tsl2561_t *dev);
+static void _disable(tsl2561_t *dev);
+static void _read_data(tsl2561_t *dev, uint16_t *full, uint16_t *ir);
+static void _print_init_info(tsl2561_t *dev);
+
+/*---------------------------------------------------------------------------*
+ *                          TSL2561 Core API                                 *
+ *---------------------------------------------------------------------------*/
+
+int tsl2561_init(tsl2561_t *dev,
+                 i2c_t i2c, uint8_t addr, uint8_t gain, uint8_t integration)
+{
+    dev->i2c_dev = i2c;
+    dev->addr = addr;
+    dev->gain = gain;
+    dev->integration = integration;
+    _print_init_info(dev);
+
+    /* Initialize I2C interface */
+    if (i2c_init_master(dev->i2c_dev, I2C_SPEED_NORMAL)) {
+        DEBUG("[Error] I2C device not enabled\n");
+        return TSL2561_NOI2C;
+    }
+
+    DEBUG("[Info] I2C device initialized with success!\n");
+
+    /* Acquire exclusive access */
+    i2c_acquire(dev->i2c_dev);
+
+    DEBUG("[Info] Access acquired !\n");
+
+    /* Verify sensor ID */
+    uint8_t id;
+    i2c_read_reg(dev->i2c_dev, dev->addr,
+                 TSL2561_COMMAND_MODE | TSL2561_REGISTER_ID, &id);
+    DEBUG("[Info] ID ? %d\n", id);
+    if (id != TSL2561_ID ) {
+        DEBUG("[Error] not a TSL2561 sensor\n");
+        return TSL2561_BADDEV;
+    }
+
+    _enable(dev);
+
+    /* configuring gain and integration time */
+    i2c_write_reg(dev->i2c_dev, dev->addr,
+                  TSL2561_COMMAND_MODE | TSL2561_REGISTER_TIMING,
+                  dev->integration | dev->gain);
+
+#if ENABLE_DEBUG
+    uint8_t timing;
+    i2c_read_reg(dev->i2c_dev, dev->addr,
+                 TSL2561_COMMAND_MODE | TSL2561_REGISTER_TIMING, &timing);
+    DEBUG("[Info] Timing ? %d (expected: %d)\n",
+          timing, dev->integration | dev->gain);
+#endif
+
+    _disable(dev);
+
+    return TSL2561_OK;
+}
+
+uint16_t tsl2561_read_illuminance(tsl2561_t *dev)
+{
+    /* Read IR and full spectrum values */
+    uint16_t ir = 0;
+    uint16_t full = 0;
+    _read_data(dev, &full, &ir);
+
+    DEBUG("[Info] Full spectrum value: %i\n", (int)full);
+    DEBUG("[Info] IR spectrum value: %i\n", (int)ir);
+
+    /* Compute illuminance */
+    uint32_t channel_scale;
+    uint32_t channel_1;
+    uint32_t channel_0;
+
+    switch (dev->integration) {
+        case TSL2561_INTEGRATIONTIME_13MS:
+            channel_scale = TSL2561_CHSCALE_TINT0;
+            break;
+
+        case TSL2561_INTEGRATIONTIME_101MS:
+            channel_scale = TSL2561_CHSCALE_TINT1;
+            break;
+
+        default: /* No scaling ... integration time = 402ms */
+            channel_scale = (1 << TSL2561_CHSCALE);
+            break;
+    }
+
+    /* Scale for gain (1x or 16x) */
+    if (!dev->gain) {
+        channel_scale = channel_scale << 4;
+    }
+
+    /* scale the channel values */
+    channel_0 = (full * channel_scale) >> TSL2561_CHSCALE;
+    channel_1 = (ir * channel_scale) >> TSL2561_CHSCALE;
+
+    /* find the ratio of the channel values (Channel1/Channel0) */
+    uint32_t ratio_1 = 0;
+    if (channel_0 != 0) {
+        ratio_1 = (channel_1 << (TSL2561_RATIOSCALE + 1)) / channel_0;
+    }
+
+    /* round the ratio value */
+    uint32_t ratio = (ratio_1 + 1) >> 1;
+    uint32_t b, m;
+
+    if (ratio <= TSL2561_K1T) {
+        b = TSL2561_B1T;
+        m = TSL2561_M1T;
+    }
+    else if (ratio <= TSL2561_K2T) {
+        b = TSL2561_B2T;
+        m = TSL2561_M2T;
+    }
+    else if (ratio <= TSL2561_K3T) {
+        b = TSL2561_B3T;
+        m = TSL2561_M3T;
+    }
+    else if (ratio <= TSL2561_K4T) {
+        b = TSL2561_B4T;
+        m = TSL2561_M4T;
+    }
+    else if (ratio <= TSL2561_K5T) {
+        b = TSL2561_B5T;
+        m = TSL2561_M5T;
+    }
+    else if (ratio <= TSL2561_K6T) {
+        b = TSL2561_B6T;
+        m = TSL2561_M6T;
+    }
+    else if (ratio <= TSL2561_K7T) {
+        b = TSL2561_B7T;
+        m = TSL2561_M7T;
+    }
+    else {
+        b = TSL2561_B8T;
+        m = TSL2561_M8T;
+    }
+
+    uint32_t illuminance;
+    illuminance = ((channel_0 * b) - (channel_1 * m));
+
+    /* round lsb (2^(TSL2561_SCALE - 1)) */
+    illuminance += (1 << (TSL2561_LUXSCALE - 1));
+
+    /* return strip off fractional portion */
+    return (uint16_t)(illuminance >> TSL2561_LUXSCALE);
+}
+
+
+static void _enable(tsl2561_t *dev)
+{
+    /* enabling device */
+    i2c_write_reg(dev->i2c_dev, dev->addr,
+                  TSL2561_COMMAND_MODE | TSL2561_REGISTER_CONTROL,
+                  TSL2561_CONTROL_POWERON);
+#if ENABLE_DEBUG
+    uint8_t en;
+    i2c_read_reg(dev->i2c_dev, dev->addr,
+                 TSL2561_COMMAND_MODE | TSL2561_REGISTER_CONTROL, &en);
+    DEBUG("[Info] Enabled ? %s\n", en == 3 ? "true" : "false");
+#endif
+}
+
+
+static void _disable(tsl2561_t *dev)
+{
+    /* disabling device */
+    i2c_write_reg(dev->i2c_dev, dev->addr,
+                  TSL2561_COMMAND_MODE | TSL2561_REGISTER_CONTROL,
+                  TSL2561_CONTROL_POWEROFF );
+
+#if ENABLE_DEBUG
+    uint8_t dis;
+    i2c_read_reg(dev->i2c_dev, dev->addr,
+                 TSL2561_COMMAND_MODE | TSL2561_REGISTER_CONTROL, &dis);
+    DEBUG("[Info] Disabled ? %s\n", dis == 0 ? "true": "false");
+#endif
+}
+
+static void _read_data(tsl2561_t *dev, uint16_t *full, uint16_t *ir)
+{
+    /* Enable the device */
+    _enable(dev);
+
+    /* Wait integration time in ms for ADC to complete */
+    switch (dev->integration) {
+        case TSL2561_INTEGRATIONTIME_13MS:
+            xtimer_usleep(13700);
+            break;
+
+        case TSL2561_INTEGRATIONTIME_101MS:
+            xtimer_usleep(101000);
+            break;
+
+        default: /* TSL2561_INTEGRATIONTIME_402MS */
+            xtimer_usleep(402000);
+            break;
+    }
+
+    char buffer[2] = { 0 };
+    /* Read full spectrum channel */
+    i2c_read_regs(dev->i2c_dev, dev->addr,
+                  TSL2561_COMMAND_MODE | TSL2561_COMMAND_WORD | TSL2561_REGISTER_CHAN0,
+                  buffer, 2);
+    *full = (buffer[1] << 8) | buffer[0];
+
+    memset(buffer, 0, sizeof(buffer));
+
+    /* Read infrared spectrum channel */
+    i2c_read_regs(dev->i2c_dev, dev->addr,
+                  TSL2561_COMMAND_MODE | TSL2561_COMMAND_WORD | TSL2561_REGISTER_CHAN1,
+                  buffer, 2);
+    *ir = (buffer[1] << 8) | buffer[0];
+
+    /* Turn the device off to save power */
+    _disable(dev);
+}
+
+static void _print_init_info(tsl2561_t *dev)
+{
+    DEBUG("[Info] I2C device: %d\n", dev->i2c_dev);
+    DEBUG("[Info] Address: %d\n", dev->addr);
+    switch(dev->gain) {
+        case TSL2561_GAIN_1X:
+            DEBUG("[Info] Gain: 1X\n");
+            break;
+
+        case TSL2561_GAIN_16X:
+            DEBUG("[Info] Gain: 16X\n");
+            break;
+
+        default:
+            DEBUG("[Info] Invalid gain %d\n", dev->gain);
+            break;
+    }
+
+    switch(dev->integration) {
+    case TSL2561_INTEGRATIONTIME_13MS:
+        DEBUG("[Info] Integration time: 13ms\n");
+        break;
+    case TSL2561_INTEGRATIONTIME_101MS:
+        DEBUG("[Info] Integration time: 101ms\n");
+        break;
+    case TSL2561_INTEGRATIONTIME_402MS:
+        DEBUG("[Info] Integration time: 402ms\n");
+        break;
+    case TSL2561_INTEGRATIONTIME_NA:
+        DEBUG("[Info] Integration time: n/a\n");
+        break;
+    default:
+        DEBUG("[Info] Invalid integration time %d\n", dev->integration);
+        break;
+    }
+}
diff --git a/drivers/tsl2561/tsl2561_saul.c b/drivers/tsl2561/tsl2561_saul.c
new file mode 100644
index 0000000000..0e8aebd0df
--- /dev/null
+++ b/drivers/tsl2561/tsl2561_saul.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 Inria
+ *
+ * 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     drivers_tsl2561
+ * @{
+ *
+ * @file
+ * @brief       SAUL adaption for TSL2561 device
+ *
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ * @author      Alexandre Abadie <alexandre.abadie@inria.fr>
+ *
+ * @}
+ */
+
+#include "saul.h"
+#include "tsl2561.h"
+#include "xtimer.h"
+
+static int read_illuminance(void *dev, phydat_t *res)
+{
+    tsl2561_t *d = (tsl2561_t *)dev;
+
+    res->val[0] = tsl2561_read_illuminance(d);
+    res->unit = UNIT_LUX;
+    res->scale = 0;
+    return 1;
+}
+
+const saul_driver_t tsl2561_illuminance_saul_driver = {
+    .read = read_illuminance,
+    .write = saul_notsup,
+    .type = SAUL_SENSE_LIGHT
+};
diff --git a/sys/auto_init/auto_init.c b/sys/auto_init/auto_init.c
index f09d7192ed..c8995e46ef 100644
--- a/sys/auto_init/auto_init.c
+++ b/sys/auto_init/auto_init.c
@@ -315,6 +315,10 @@ void auto_init(void)
     extern void auto_init_jc42(void);
     auto_init_jc42();
 #endif
+#ifdef MODULE_TSL2561
+    extern void auto_init_tsl2561(void);
+    auto_init_tsl2561();
+#endif
 #ifdef MODULE_HDC1000
     extern void auto_init_hdc1000(void);
     auto_init_hdc1000();
diff --git a/sys/auto_init/saul/auto_init_tsl2561.c b/sys/auto_init/saul/auto_init_tsl2561.c
new file mode 100644
index 0000000000..68a29002ed
--- /dev/null
+++ b/sys/auto_init/saul/auto_init_tsl2561.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2016 Inria
+ *
+ * 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     auto_init_saul
+ * @{
+ *
+ * @file
+ * @brief       Auto initialization of TSL2561 driver.
+ *
+ * @author      Alexandre Abadie <alexandre.abadie@inria.fr>
+ *
+ * @}
+ */
+
+#ifdef MODULE_TSL2561
+
+#include "log.h"
+#include "saul_reg.h"
+
+#include "tsl2561_params.h"
+
+/**
+ * @brief   Define the number of configured sensors
+ */
+#define TSL2561_NUMOF    (sizeof(tsl2561_params) / sizeof(tsl2561_params[0]))
+
+/**
+ * @brief   Allocation of memory for device descriptors
+ */
+static tsl2561_t tsl2561_devs[TSL2561_NUMOF];
+
+/**
+ * @brief   Memory for the SAUL registry entries
+ */
+static saul_reg_t saul_entries[TSL2561_NUMOF];
+
+/**
+ * @brief   Reference the driver structs.
+ * @{
+ */
+extern const saul_driver_t tsl2561_illuminance_saul_driver;
+/** @} */
+
+void auto_init_tsl2561(void)
+{
+    for (unsigned i = 0; i < TSL2561_NUMOF; i++) {
+        LOG_DEBUG("[auto_init_saul] initializing tsl2561 #%u\n", i);
+
+        if (tsl2561_init(&tsl2561_devs[i],
+                         tsl2561_params[i].i2c_dev,
+                         tsl2561_params[i].addr,
+                         tsl2561_params[i].gain,
+                         tsl2561_params[i].integration) != TSL2561_OK) {
+            LOG_ERROR("[auto_init_saul] error initializing tsl2561 #%u\n", i);
+            return;
+        }
+
+        /* illuminance */
+        saul_entries[i].dev = &(tsl2561_devs[i]);
+        saul_entries[i].name = tsl2561_saul_reg_info[i].name;
+        saul_entries[i].driver = &tsl2561_illuminance_saul_driver;
+
+        /* register to saul */
+        saul_reg_add(&(saul_entries[i]));
+    }
+}
+#else
+typedef int dont_be_pedantic;
+#endif /* MODULE_TSL2561 */
-- 
GitLab