aboutsummaryrefslogtreecommitdiffstats
path: root/os/various
diff options
context:
space:
mode:
authorFabio Utzig <utzig@utzig.org>2016-02-15 19:29:07 -0200
committerFabio Utzig <utzig@utzig.org>2016-02-15 19:29:07 -0200
commit1548bca80f5af0fe3f79331ae5a3d51b18f1c077 (patch)
tree4fe545b2beb11175624e45b7b3b3d675345dfee4 /os/various
parentfac5a0d26c0d5a13d0847984ee96103441fab6cd (diff)
parent7b5ddf1928e900fc0a0647e24cc0d4b8c49c1f42 (diff)
downloadChibiOS-Contrib-1548bca80f5af0fe3f79331ae5a3d51b18f1c077.tar.gz
ChibiOS-Contrib-1548bca80f5af0fe3f79331ae5a3d51b18f1c077.tar.bz2
ChibiOS-Contrib-1548bca80f5af0fe3f79331ae5a3d51b18f1c077.zip
Merge pull request #35 from sdalu/sensor_hdc1000
Sensor hdc1000, mcp9808, tsl2561, tsl2591
Diffstat (limited to 'os/various')
-rw-r--r--os/various/bswap.h146
-rw-r--r--os/various/devices_lib/sensors/hdc1000.c265
-rw-r--r--os/various/devices_lib/sensors/hdc1000.h240
-rw-r--r--os/various/devices_lib/sensors/mcp9808.c207
-rw-r--r--os/various/devices_lib/sensors/mcp9808.h204
-rw-r--r--os/various/devices_lib/sensors/sensor.h81
-rw-r--r--os/various/devices_lib/sensors/tsl2561.c386
-rw-r--r--os/various/devices_lib/sensors/tsl2561.h241
-rw-r--r--os/various/devices_lib/sensors/tsl2591.c272
-rw-r--r--os/various/devices_lib/sensors/tsl2591.h238
-rw-r--r--os/various/i2c_helpers.h283
11 files changed, 2563 insertions, 0 deletions
diff --git a/os/various/bswap.h b/os/various/bswap.h
new file mode 100644
index 0000000..30ae1d6
--- /dev/null
+++ b/os/various/bswap.h
@@ -0,0 +1,146 @@
+/*
+ Copyright (C) 2016 Stephane D'Alu
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#ifndef BSWAP_H
+#define BSWAP_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#if !(defined(ARCH_BIG_ENDIAN) || defined(ARCH_LITTLE_ENDIAN))
+#error "Need to define one: ARCH_BIG_ENDIAN or ARCH_LITTLE_ENDIAN"
+#endif
+
+#if defined(ARCH_BIG_ENDIAN) && defined(ARCH_LITTLE_ENDIAN)
+#error "ARCH_BIG_ENDIAN and ARCH_LITTLE_ENDIAN are both set"
+#endif
+
+
+#define BSWAP_16(x) \
+ (uint16_t)((((x) & 0xFF00) >> 8) | \
+ (((x) & 0x00FF) << 8))
+#define BSWAP_32(x) \
+ (uint32_t)((((x) & 0xFF000000UL) >> 24UL) | \
+ (((x) & 0x00FF0000UL) >> 8UL) | \
+ (((x) & 0x0000FF00UL) << 8UL) | \
+ (((x) & 0x000000FFUL) << 24UL))
+
+
+#if defined(ARCH_BIG_ENDIAN)
+#define le16_to_cpu(x) bswap_16(x)
+#define le32_to_cpu(x) bswap_32(x)
+#define be16_to_cpu(x) (x)
+#define be32_to_cpu(x) (x)
+#define cpu_to_le16(x) bswap_16(x)
+#define cpu_to_le32(x) bswap_32(x)
+#define cpu_to_be16(x) (x)
+#define cpu_to_be32(x) (x)
+#define LE16_TO_CPU(x) BSWAP_16(x)
+#define LE32_TO_CPU(x) BSWAP_32(x)
+#define BE16_TO_CPU(x) (x)
+#define BE32_TO_CPU(x) (x)
+#define CPU_TO_LE16(x) BSWAP_16(x)
+#define CPU_TO_LE32(x) BSWAP_32(x)
+#define CPU_TO_BE16(x) (x)
+#define CPU_TO_BE32(x) (x)
+#endif
+
+
+#if defined(ARCH_LITTLE_ENDIAN)
+#define le16_to_cpu(x) (x)
+#define le32_to_cpu(x) (x)
+#define be16_to_cpu(x) bswap_16(x)
+#define be32_to_cpu(x) bswap_32(x)
+#define cpu_to_le16(x) (x)
+#define cpu_to_le32(x) (x)
+#define cpu_to_be16(x) bswap_16(x)
+#define cpu_to_be32(x) bswap_32(x)
+#define LE16_TO_CPU(x) (x)
+#define LE32_TO_CPU(x) (x)
+#define BE16_TO_CPU(x) BSWAP_16(x)
+#define BE32_TO_CPU(x) BSWAP_32(x)
+#define CPU_TO_LE16(x) (x)
+#define CPU_TO_LE32(x) (x)
+#define CPU_TO_BE16(x) BSWAP_16(x)
+#define CPU_TO_BE32(x) BSWAP_32(x)
+#endif
+
+
+static inline uint16_t bswap_16(const uint16_t x)
+ __attribute__ ((warn_unused_result))
+ __attribute__ ((const))
+ __attribute__ ((always_inline));
+
+static inline uint16_t bswap_16(const uint16_t x) {
+ if (__builtin_constant_p(x))
+ return BSWAP_16(x);
+
+ uint8_t tmp;
+ union { uint16_t x; uint8_t b[2]; } data;
+
+ data.x = x;
+ tmp = data.b[0];
+ data.b[0] = data.b[1];
+ data.b[1] = tmp;
+
+ return data.x;
+}
+
+static inline uint32_t bswap_32(const uint32_t x)
+ __attribute__ ((warn_unused_result))
+ __attribute__ ((const))
+ __attribute__ ((always_inline));
+
+
+static inline uint32_t bswap_32(const uint32_t x) {
+ if (__builtin_constant_p(x))
+ return BSWAP_32(x);
+
+ uint8_t tmp;
+ union { uint32_t x; uint8_t b[4]; } data;
+
+ data.x = x;
+ tmp = data.b[0];
+ data.b[0] = data.b[3];
+ data.b[3] = tmp;
+ tmp = data.b[1];
+ data.b[1] = data.b[2];
+ data.b[2] = tmp;
+
+ return data.x;
+}
+
+static inline void bswap_n(void* const data, uint8_t len)
+ __attribute__ ((nonnull (1)));
+
+static inline void bswap_n(void* const data, uint8_t len) {
+ uint8_t* ptr = (uint8_t*)data;
+
+ for ( ; len > 1 ; ptr++, len -= 2 ) {
+ uint8_t tmp = *ptr;
+ *ptr = *(ptr + len - 1);
+ *(ptr + len - 1) = tmp;
+ }
+}
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
+
+
diff --git a/os/various/devices_lib/sensors/hdc1000.c b/os/various/devices_lib/sensors/hdc1000.c
new file mode 100644
index 0000000..39e47ec
--- /dev/null
+++ b/os/various/devices_lib/sensors/hdc1000.c
@@ -0,0 +1,265 @@
+/*
+ HDC100x for ChibiOS/RT - Copyright (C) 2016 Stephane D'Alu
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+/**
+ * @file HDC1000.c
+ * @brief HDC1000 interface module code.
+ *
+ * @addtogroup hdc1000
+ * @{
+ */
+
+#define I2C_HELPERS_AUTOMATIC_DRV TRUE
+
+#include "hal.h"
+#include "i2c_helpers.h"
+#include "hdc1000.h"
+
+/* DOC: http://www.ti.com/lit/ds/symlink/hdc1008.pdf
+ */
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/* I2C Register */
+#define HDC1000_REG_TEMP_HUMID 0x00
+#define HDC1000_REG_TEMP 0x00
+#define HDC1000_REG_HUMID 0x01
+#define HDC1000_REG_CONFIG 0x02
+#define HDC1000_REG_SERIAL 0xFB
+#define HDC1000_REG_SERIAL_1 0xFB
+#define HDC1000_REG_SERIAL_2 0xFC
+#define HDC1000_REG_SERIAL_3 0xFD
+#define HDC1000_REG_MANUF_ID 0xFE
+#define HDC1000_REG_DEVICE_ID 0xFF
+
+/* Configuration */
+#define HDC1000_CONFIG_RST (1 << 15)
+#define HDC1000_CONFIG_HEATER (1 << 13)
+#define HDC1000_CONFIG_MODE_ONE (0 << 12)
+#define HDC1000_CONFIG_MODE_BOTH (1 << 12)
+#define HDC1000_CONFIG_BATT (1 << 11)
+#define HDC1000_CONFIG_TRES_14 (0)
+#define HDC1000_CONFIG_TRES_11 (1 << 10)
+#define HDC1000_CONFIG_HRES_14 (0)
+#define HDC1000_CONFIG_HRES_11 (1 << 8)
+#define HDC1000_CONFIG_HRES_8 (1 << 9)
+
+/* Value */
+#define HDC1000_MANUF_ID 0x5449
+#define HDC1000_DEVICE_ID 0x1000
+
+/* Delay in micro seconds */
+#define HDC1000_DELAY_ACQUIRE_SAFETY 1000
+#define HDC1000_DELAY_ACQUIRE_TRES_14 6350
+#define HDC1000_DELAY_ACQUIRE_TRES_11 3650
+#define HDC1000_DELAY_ACQUIRE_HRES_14 6500
+#define HDC1000_DELAY_ACQUIRE_HRES_11 3850
+#define HDC1000_DELAY_ACQUIRE_HRES_8 2500
+#define HDC1000_DELAY_STARTUP 15000
+
+// Deefault config (high res)
+#define HDC1000_CONFIG_RES (HDC1000_CONFIG_TRES_14 | \
+ HDC1000_CONFIG_HRES_14)
+#define HDC1000_DELAY_ACQUIRE (HDC1000_DELAY_ACQUIRE_TRES_14 + \
+ HDC1000_DELAY_ACQUIRE_HRES_14)
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+static inline msg_t
+_apply_config(HDC1000_drv *drv) {
+ struct __attribute__((packed)) {
+ uint8_t reg;
+ uint16_t conf;
+ } tx = { HDC1000_REG_CONFIG, cpu_to_be16(drv->cfg) };
+
+ return i2c_send((uint8_t*)&tx, sizeof(tx));
+}
+
+static inline msg_t
+_decode_measure(HDC1000_drv *drv,
+ uint32_t val, float *temperature, float *humidity) {
+ (void)drv;
+
+ /* Temperature */
+ if (temperature) {
+ float temp = (val >> 16);
+ temp /= 65536;
+ temp *= 165;
+ temp -= 40;
+ *temperature = temp;
+ }
+
+ /* Humidiy */
+ if (humidity) {
+ float hum = (val & 0xFFFF);
+ hum /= 65535;
+ hum *= 100;
+ *humidity = hum;
+ }
+
+ /* ok */
+ return MSG_OK;
+}
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+void
+HDC1000_init(HDC1000_drv *drv, HDC1000_config *config) {
+ drv->config = config;
+ drv->cfg = HDC1000_CONFIG_RST | HDC1000_CONFIG_MODE_BOTH |
+ HDC1000_CONFIG_RES;
+ drv->delay = (HDC1000_DELAY_ACQUIRE +
+ HDC1000_DELAY_ACQUIRE_SAFETY) / 1000;
+ drv->state = SENSOR_INIT;
+}
+
+msg_t
+HDC1000_check(HDC1000_drv *drv) {
+ uint16_t manuf, device;
+
+ msg_t msg;
+ if (((msg = i2c_reg_recv16_be(HDC1000_REG_MANUF_ID, &manuf )) < MSG_OK) ||
+ ((msg = i2c_reg_recv16_be(HDC1000_REG_DEVICE_ID, &device)) < MSG_OK))
+ return msg;
+
+ if ((manuf != HDC1000_MANUF_ID) || (device != HDC1000_DEVICE_ID))
+ return SENSOR_NOTFOUND;
+
+ return MSG_OK;
+}
+
+
+msg_t
+HDC1000_start(HDC1000_drv *drv) {
+ osalDbgAssert((drv->state == SENSOR_INIT ) ||
+ (drv->state == SENSOR_ERROR ) ||
+ (drv->state == SENSOR_STOPPED),
+ "invalid state");
+ msg_t msg;
+ if ((msg = _apply_config(drv)) < MSG_OK) {
+ drv->state = SENSOR_ERROR;
+ return msg;
+ }
+ drv->state = SENSOR_STARTED;
+ return MSG_OK;
+}
+
+msg_t
+HDC1000_stop(HDC1000_drv *drv) {
+ drv->state = SENSOR_STOPPED;
+ return MSG_OK;
+}
+
+msg_t
+HDC1000_setHeater(HDC1000_drv *drv, bool on) {
+ if (on) { drv->cfg |= HDC1000_CONFIG_HEATER; }
+ else { drv->cfg &= ~HDC1000_CONFIG_HEATER; }
+
+ msg_t msg;
+ if ((msg = _apply_config(drv)) < MSG_OK) {
+ drv->state = SENSOR_ERROR;
+ return msg;
+ }
+ return MSG_OK;
+}
+
+msg_t
+HDC1000_startMeasure(HDC1000_drv *drv) {
+ msg_t msg;
+ osalDbgAssert(drv->state == SENSOR_STARTED, "invalid state");
+ if ((msg = i2c_reg(HDC1000_REG_TEMP_HUMID)) < MSG_OK)
+ return msg;
+ drv->state = SENSOR_MEASURING;
+ return MSG_OK;
+}
+
+
+msg_t
+HDC1000_readSerial(HDC1000_drv *drv, uint8_t *serial) {
+ msg_t msg;
+ osalDbgAssert(drv->state == SENSOR_STARTED, "invalid state");
+
+ if (((msg = i2c_reg_recv16(HDC1000_REG_SERIAL_1,
+ (uint16_t*)&serial[0])) < MSG_OK) ||
+ ((msg = i2c_reg_recv16(HDC1000_REG_SERIAL_2,
+ (uint16_t*)&serial[2])) < MSG_OK) ||
+ ((msg = i2c_reg_recv8 (HDC1000_REG_SERIAL_3,
+ (uint8_t*) &serial[4])) < MSG_OK))
+ return msg;
+ return MSG_OK;
+}
+
+
+msg_t
+HDC1000_readMeasure(HDC1000_drv *drv,
+ float *temperature, float *humidity) {
+ msg_t msg;
+ uint32_t val;
+
+ osalDbgAssert((drv->state == SENSOR_MEASURING) ||
+ (drv->state == SENSOR_READY ),
+ "invalid state");
+
+ if ((msg = i2c_recv32_be(&val)) < MSG_OK) {
+ drv->state = SENSOR_ERROR;
+ return msg;
+ }
+
+ drv->state = SENSOR_STARTED;
+
+ return _decode_measure(drv, val, temperature, humidity);
+}
+
+msg_t
+HDC1000_readTemperatureHumidity(HDC1000_drv *drv,
+ float *temperature, float *humidity) {
+ msg_t msg;
+ uint32_t val;
+
+ osalDbgAssert(drv->state == SENSOR_STARTED, "invalid state");
+
+ /* Request value */
+ if ((msg = i2c_reg(HDC1000_REG_TEMP_HUMID)) < MSG_OK)
+ return msg;
+
+ /* Wait */
+ osalThreadSleepMilliseconds(drv->delay);
+
+ /* Get value */
+ if ((msg = i2c_recv32_be(&val)) < MSG_OK) {
+ drv->state = SENSOR_ERROR;
+ return msg;
+ }
+
+ return _decode_measure(drv, val, temperature, humidity);
+}
+
+
+/** @} */
diff --git a/os/various/devices_lib/sensors/hdc1000.h b/os/various/devices_lib/sensors/hdc1000.h
new file mode 100644
index 0000000..e4eae4c
--- /dev/null
+++ b/os/various/devices_lib/sensors/hdc1000.h
@@ -0,0 +1,240 @@
+/*
+ HDC100x for ChibiOS/RT - Copyright (C) 2016 Stephane D'Alu
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+/**
+ * @file hdc1000.h
+ * @brief HDC1000 Temperature/Humidiry sensor interface module header.
+ *
+ * When changing sensor settings, you generally need to wait
+ * for 2 * getAquisitionTime(), as usually the first acquisition
+ * will be corrupted by the change of settings.
+ *
+ * No locking is done.
+ *
+ * @{
+ */
+
+#ifndef _SENSOR_HDC1000_H_
+#define _SENSOR_HDC1000_H_
+
+#include <math.h>
+#include <stdbool.h>
+#include "i2c_helpers.h"
+#include "sensor.h"
+
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+#define HDC1000_CONTINUOUS_ACQUISITION_SUPPORTED FALSE
+
+/* I2C address */
+#define HDC1000_I2CADDR_1 0x40
+#define HDC1000_I2CADDR_2 0x41
+#define HDC1000_I2CADDR_3 0x42
+#define HDC1000_I2CADDR_4 0x43
+
+#define HDC1000_SERIAL_SIZE 5 /**< @brief Size of serial (40bits) */
+
+/**
+ * @brief Time necessary for the sensor to boot
+ */
+#define HDC1000_BOOTUP_TIME 15
+
+/**
+ * @brief Time necessary for the sensor to start
+ */
+#define HDC1000_STARTUP_TIME 0
+
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+
+#define HDC1000_I2CADDR_DEFAULT HDC1000_I2CADDR_1
+
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/**
+ * @brief HDC1000 configuration structure.
+ */
+typedef struct {
+ I2CHelper i2c; /* keep it first */
+} HDC1000_config;
+
+/**
+ * @brief HDC1000 configuration structure.
+ */
+typedef struct {
+ HDC1000_config *config;
+ sensor_state_t state;
+ unsigned int delay;
+ uint16_t cfg;
+} HDC1000_drv;
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+/**
+ * @brief Initialize the sensor driver
+ */
+void
+HDC1000_init(HDC1000_drv *drv,
+ HDC1000_config *config);
+
+/**
+ * @brief Start the sensor
+ */
+msg_t
+HDC1000_start(HDC1000_drv *drv);
+
+/**
+ * @brief Stop the sensor
+ *
+ * @details If the sensor support it, it will be put in low energy mode.
+ */
+msg_t
+HDC1000_stop(HDC1000_drv *drv);
+
+/**
+ * @brief Check that the sensor is really present
+ */
+msg_t
+HDC1000_check(HDC1000_drv *drv);
+
+
+msg_t
+HDC1000_readSerial(HDC1000_drv *drv, uint8_t *serial);
+
+/**
+ * @brief Control the HD1000 heater.
+ */
+msg_t
+HDC1000_setHeater(HDC1000_drv *drv,
+ bool on);
+
+
+
+/**
+ * @brief Time in milli-seconds necessary for acquiring a naw measure
+ *
+ * @returns
+ * unsigned int time in millis-seconds
+ */
+static inline unsigned int
+HDC1000_getAcquisitionTime(HDC1000_drv *drv) {
+ return drv->delay;
+}
+
+/**
+ * @brief Trigger a mesure acquisition
+ */
+msg_t
+HDC1000_startMeasure(HDC1000_drv *drv);
+
+/**
+ * @brief Read the newly acquiered measure
+ *
+ * @note According the the sensor design the measure read
+ * can be any value acquired after the acquisition time
+ * and the call to readMeasure.
+ */
+msg_t
+HDC1000_readMeasure(HDC1000_drv *drv,
+ float *temperature, float *humidity);
+
+
+/**
+ * @brief Read temperature and humidity
+ *
+ * @details According to the sensor specification/configuration
+ * (see #HDC1000_CONTINUOUS_ACQUISITION_SUPPORTED),
+ * if the sensor is doing continuous measurement
+ * it's value will be requested and returned immediately.
+ * Otherwise a measure is started, the necessary amount of
+ * time for acquiring the value is spend sleeping (not spinning),
+ * and finally the measure is read.
+ *
+ * @note In continuous measurement mode, if you just started
+ * the sensor, you will need to wait getAcquisitionTime()
+ * in addition to the usual #HDC1000_STARTUP_TIME
+
+ * @note If using several sensors, it is better to start all the
+ * measure together, wait for the sensor having the longuest
+ * aquisition time, and finally read all the values
+ */
+msg_t
+HDC1000_readTemperatureHumidity(HDC1000_drv *drv,
+ float *temperature, float *humidity);
+
+/**
+ * @brief Return the humidity value in percent.
+ *
+ * @details Use readTemperatureHumidity() for returning the humidity value.
+ *
+ * @note Prefere readTemperatureHumidity(), if you need both temperature
+ * and humidity, or if you need better error handling.
+ *
+ * @returns
+ * float humidity percent
+ * NAN on failure
+ */
+static inline float
+HDC1000_getHumidity(HDC1000_drv *drv) {
+ float humidity = NAN;
+ HDC1000_readTemperatureHumidity(drv, NULL, &humidity);
+ return humidity;
+}
+
+/**
+ * @brief Return the temperature value in °C.
+ *
+ * @details Use readTemperatureHumidity() for returning the humidity value.
+ *
+ * @note Prefere readTemperatureHumidity(), if you need both temperature
+ * and humidity, or if you need better error handling.
+ *
+ * @returns
+ * float humidity percent
+ * NAN on failure
+ */
+static inline float
+HDC1000_getTemperature(HDC1000_drv *drv) {
+ float temperature = NAN;
+ HDC1000_readTemperatureHumidity(drv, &temperature, NULL);
+ return temperature;
+}
+
+
+#endif
+
+/**
+ * @}
+ */
diff --git a/os/various/devices_lib/sensors/mcp9808.c b/os/various/devices_lib/sensors/mcp9808.c
new file mode 100644
index 0000000..4b22ea9
--- /dev/null
+++ b/os/various/devices_lib/sensors/mcp9808.c
@@ -0,0 +1,207 @@
+/*
+ MCP9808 for ChibiOS/RT - Copyright (C) 2016 Stephane D'Alu
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#define I2C_HELPERS_AUTOMATIC_DRV TRUE
+
+#include "hal.h"
+#include "i2c_helpers.h"
+#include "mcp9808.h"
+
+// http://www.mouser.com/ds/2/268/25095A-15487.pdf
+// http://ww1.microchip.com/downloads/en/DeviceDoc/25095A.pdf
+
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/* I2C Register */
+#define MCP9808_REG_CONFIG 0x01
+#define MCP9808_REG_UPPER_TEMP 0x02
+#define MCP9808_REG_LOWER_TEMP 0x03
+#define MCP9808_REG_CRIT_TEMP 0x04
+#define MCP9808_REG_AMBIENT_TEMP 0x05
+#define MCP9808_REG_MANUF_ID 0x06
+#define MCP9808_REG_DEVICE_ID 0x07
+#define MCP9808_REG_RESOLUTION 0x08
+
+/* Config */
+#define MCP9808_REG_CONFIG_SHUTDOWN 0x0100
+#define MCP9808_REG_CONFIG_CRITLOCKED 0x0080
+#define MCP9808_REG_CONFIG_WINLOCKED 0x0040
+#define MCP9808_REG_CONFIG_INTCLR 0x0020
+#define MCP9808_REG_CONFIG_ALERTSTAT 0x0010
+#define MCP9808_REG_CONFIG_ALERTCTRL 0x0008
+#define MCP9808_REG_CONFIG_ALERTSEL 0x0002
+#define MCP9808_REG_CONFIG_ALERTPOL 0x0002
+#define MCP9808_REG_CONFIG_ALERTMODE 0x0001
+
+/* Device Id */
+#define MCP9808_MANUF_ID 0x0054
+#define MCP9808_DEVICE_ID 0x0400
+
+/* Resolution */
+#define MCP9808_RES_2 0x00 /* 1/2 = 0.5 */
+#define MCP9808_RES_4 0x01 /* 1/4 = 0.25 */
+#define MCP9808_RES_8 0x10 /* 1/8 = 0.125 */
+#define MCP9808_RES_16 0x11 /* 1/16 = 0.0625 */
+
+/* Time in milli-seconds */
+#define MCP9808_DELAY_ACQUIRE_RES_2 30
+#define MCP9808_DELAY_ACQUIRE_RES_4 65
+#define MCP9808_DELAY_ACQUIRE_RES_8 130
+#define MCP9808_DELAY_ACQUIRE_RES_16 250
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+static inline msg_t
+_apply_config(MCP9808_drv *drv) {
+ struct __attribute__((packed)) {
+ uint8_t reg;
+ uint16_t conf;
+ } tx = { MCP9808_REG_CONFIG, cpu_to_be16(drv->cfg) };
+
+ return i2c_send((uint8_t*)&tx, sizeof(tx));
+}
+
+static inline msg_t
+_decode_measure(MCP9808_drv *drv,
+ uint16_t val, float *temperature) {
+
+ /* Temperature */
+ if (temperature) {
+ float temp = val & 0x0fff;
+ if (val & 0x1000) temp -= 0x1000;
+
+ float factor = 16.0F;
+ switch(drv->resolution) {
+ case RES_2 : factor = 2.0F; break;
+ case RES_4 : factor = 4.0F; break;
+ case RES_8 : factor = 8.0F; break;
+ case RES_16: factor = 16.0F; break;
+ }
+
+ *temperature = temp / factor;
+ }
+
+ /* Ok */
+ return MSG_OK;
+}
+
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+void
+MCP9808_init(MCP9808_drv *drv, MCP9808_config *config) {
+ drv->config = config;
+ drv->cfg = 0;
+ drv->resolution = RES_16; /* power up default */
+ drv->state = SENSOR_INIT;
+}
+
+msg_t
+MCP9808_check(MCP9808_drv *drv) {
+ uint16_t manuf, device;
+
+ msg_t msg;
+ if (((msg = i2c_reg_recv16_be(MCP9808_REG_MANUF_ID, &manuf )) < MSG_OK) ||
+ ((msg = i2c_reg_recv16_be(MCP9808_REG_DEVICE_ID, &device)) < MSG_OK))
+ return msg;
+
+ if ((manuf != MCP9808_MANUF_ID) || (device != MCP9808_DEVICE_ID))
+ return SENSOR_NOTFOUND;
+
+ return MSG_OK;
+}
+
+msg_t
+MCP9808_setResolution(MCP9808_drv *drv, MCP9808_resolution_t res) {
+ struct __attribute__((packed)) {
+ uint8_t reg;
+ uint8_t resolution;
+ } tx = { MCP9808_REG_RESOLUTION, res };
+
+ msg_t msg;
+ if ((msg = i2c_send((uint8_t*)&tx, sizeof(tx))) < MSG_OK)
+ return msg;
+
+ drv->resolution = res;
+ return MSG_OK;
+}
+
+msg_t
+MCP9808_start(MCP9808_drv *drv) {
+ drv->cfg &= ~(MCP9808_REG_CONFIG_SHUTDOWN);
+ return _apply_config(drv);
+}
+
+msg_t
+MCP9808_stop(MCP9808_drv *drv) {
+ drv->cfg |= (MCP9808_REG_CONFIG_SHUTDOWN);
+ return _apply_config(drv);
+}
+
+unsigned int
+MCP9808_getAcquisitionTime(MCP9808_drv *drv) {
+ switch(drv->resolution) {
+ case RES_2 : return MCP9808_DELAY_ACQUIRE_RES_2;
+ case RES_4 : return MCP9808_DELAY_ACQUIRE_RES_4;
+ case RES_8 : return MCP9808_DELAY_ACQUIRE_RES_8;
+ case RES_16: return MCP9808_DELAY_ACQUIRE_RES_16;
+ }
+ osalDbgAssert(false, "OOPS");
+ return 0;
+}
+
+msg_t
+MCP9808_readMeasure(MCP9808_drv *drv,
+ float *temperature) {
+
+ msg_t msg;
+ uint16_t val;
+
+ if ((msg = i2c_reg_recv16_be(MCP9808_REG_AMBIENT_TEMP, &val)) < MSG_OK)
+ return msg;
+
+ return _decode_measure(drv, val, temperature);
+}
+
+
+msg_t
+MCP9808_readTemperature(MCP9808_drv *drv,
+ float *temperature) {
+ osalDbgAssert(drv->state == SENSOR_STARTED, "invalid state");
+
+ msg_t msg;
+ uint16_t val;
+
+ if ((msg = i2c_reg_recv16_be(MCP9808_REG_AMBIENT_TEMP, &val)) < MSG_OK)
+ return msg;
+
+ return _decode_measure(drv, val, temperature);
+}
diff --git a/os/various/devices_lib/sensors/mcp9808.h b/os/various/devices_lib/sensors/mcp9808.h
new file mode 100644
index 0000000..857f2f9
--- /dev/null
+++ b/os/various/devices_lib/sensors/mcp9808.h
@@ -0,0 +1,204 @@
+/*
+ MCP9808 for ChibiOS/RT - Copyright (C) 2016 Stephane D'Alu
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#ifndef _SENSOR_MCP9808_H_
+#define _SENSOR_MCP9808_H_
+
+#include <math.h>
+#include "i2c_helpers.h"
+#include "sensor.h"
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+#define MCP9808_CONTINUOUS_ACQUISITION_SUPPORTED TRUE
+
+
+#define MCP9808_I2CADDR_FIXED 0x18
+
+/**
+ * @brief Time necessary for the sensor to boot
+ */
+#define MCP9808_BOOTUP_TIME 0
+
+/**
+ * @brief Time necessary for the sensor to start
+ */
+#define MCP9808_STARTUP_TIME 0
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+
+#define MCP9808_I2CADDR_DEFAULT MCP9808_I2CADDR_FIXED
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/**
+ * @brief Different possible resolution
+ */
+typedef enum {
+ RES_2 = 0x00, /**< @brief Resolution of 1/2 = 0.5 */
+ RES_4 = 0x01, /**< @brief Resolution of 1/4 = 0.25 */
+ RES_8 = 0x10, /**< @brief Resolution of 1/8 = 0.125 */
+ RES_16 = 0x11, /**< @brief Resolution of 1/16 = 0.0625 */
+} MCP9808_resolution_t;
+
+/**
+ * @brief MCP9808 configuration structure.
+ */
+typedef struct {
+ I2CHelper i2c; /* keep it first */
+} MCP9808_config;
+
+/**
+ * @brief MCP9808 configuration structure.
+ */
+typedef struct {
+ MCP9808_config *config;
+ sensor_state_t state;
+ MCP9808_resolution_t resolution;
+ uint16_t cfg;
+} MCP9808_drv;
+
+/**
+ * @brief MCP9808 measure reading
+ */
+typedef struct {
+ float temperature;
+} MCP9808_measure;
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+/**
+ * @brief Initialize the sensor driver
+ */
+void
+MCP9808_init(MCP9808_drv *drv,
+ MCP9808_config *config);
+
+/**
+ * @brief Check that the sensor is really present
+ */
+msg_t
+MCP9808_check(MCP9808_drv *drv);
+
+/**
+ * @brief Start the sensor
+ */
+msg_t
+MCP9808_start(MCP9808_drv *drv);
+
+/**
+ * @brief Stop the sensor
+ *
+ * @details If the sensor support it, it will be put in low energy mode.
+ */
+msg_t
+MCP9808_stop(MCP9808_drv *drv);
+
+/**
+ * @brief Control the MCP9809 resolution.
+ */
+msg_t
+MCP9808_setResolution(MCP9808_drv *drv,
+ MCP9808_resolution_t res);
+
+/**
+ * @brief Time in milli-seconds necessary for acquiring a naw measure
+ *
+ * @returns
+ * unsigned int time in millis-seconds
+ */
+unsigned int
+MCP9808_getAcquisitionTime(MCP9808_drv *drv);
+
+/**
+ * @brief Trigger a mesure acquisition
+ */
+static inline msg_t
+MCP9808_startMeasure(MCP9808_drv *drv) {
+ (void)drv;
+ return MSG_OK;
+}
+
+/**
+ * @brief Read the newly acquiered measure
+ *
+ * @note According the the sensor design the measure read
+ * can be any value acquired after the acquisition time
+ * and the call to readMeasure.
+ */
+msg_t
+MCP9808_readMeasure(MCP9808_drv *drv,
+ float *temperature);
+
+
+/**
+ * @brief Read temperature and humidity
+ *
+ * @details According to the sensor specification/configuration
+ * (see #MCP9808_CONTINUOUS_ACQUISITION_SUPPORTED),
+ * if the sensor is doing continuous measurement
+ * it's value will be requested and returned immediately.
+ * Otherwise a measure is started, the necessary amount of
+ * time for acquiring the value is spend sleeping (not spinning),
+ * and finally the measure is read.
+ *
+ * @note In continuous measurement mode, if you just started
+ * the sensor, you will need to wait getAcquisitionTime()
+ * in addition to the usual getStartupTime()
+
+ * @note If using several sensors, it is better to start all the
+ * measure together, wait for the sensor having the longuest
+ * aquisition time, and finally read all the values
+ */
+msg_t
+MCP9808_readTemperature(MCP9808_drv *drv,
+ float *temperature);
+
+/**
+ * @brief Return the temperature value in °C.
+ *
+ * @note Prefere readTemperature(), if you need better error handling.
+ *
+ * @return The temperature in °C
+ * @retval float humidity percent
+ * @retval NAN on failure
+ */
+static inline float
+MCP9808_getTemperature(MCP9808_drv *drv) {
+ float temperature = NAN;
+ MCP9808_readTemperature(drv, &temperature);
+ return temperature;
+}
+
+#endif
+
diff --git a/os/various/devices_lib/sensors/sensor.h b/os/various/devices_lib/sensors/sensor.h
new file mode 100644
index 0000000..bd544b1
--- /dev/null
+++ b/os/various/devices_lib/sensors/sensor.h
@@ -0,0 +1,81 @@
+/*
+ Copyright (C) 2016 Stephane D'Alu
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+/**
+ *
+ * Example of function calls.
+ *
+ * @code
+ * static SENSOR_config sensor_config = {
+ * };
+ * static SENSOR_drv sensor_drv;
+ * @endcode
+ *
+ *
+ * @code
+ * osalThreadSleepMilliseconds(SENSOR_BOOTUP_TIME);
+ * SENSOR_init(&sensor_drv);
+ * @endcode
+ *
+ * @code
+ * SENSOR_start(&sensor_drv, &sensor_config);
+ * osalThreadSleepMilliseconds(SENSOR_STARTUP_TIME);
+ * @endcode
+ *
+ * If using SENSOR_startMeasure()/SENSOR_readMeasure()
+ * @code
+ * while(true) {
+ * SENSOR_startMeasure(&sensor_drv);
+ * osalThreadSleepMilliseconds(SENSOR_getAcquisitionTime());
+ * SENSOR_readMeasure(&sensor_drv, ...);
+ * }
+ * @endcode
+ *
+ * If using SENSOR_readValue() or SENSOR_getValue()
+ * @code
+ * #if SENSOR_CONTINUOUS_ACQUISITION_SUPPORTED == TRUE
+ * osalThreadSleepMilliseconds(SENSOR_getAcquisitionTime())
+ * #endif
+ *
+ * while(true) {
+ * SENSOR_readValue(&sensor_drv, ...);
+ * }
+ * @encode
+ */
+#ifndef _SENSOR_H_
+#define _SENSOR_H_
+
+#define SENSOR_OK MSG_OK /**< @brief Operation successful. */
+#define SENSOR_TIMEOUT MSG_TIMEOUT /**< @brief Communication timeout */
+#define SENSOR_RESET MSG_REST /**< @brief Communication error. */
+#define SENSOR_NOTFOUND (msg_t)-20 /**< @brief Sensor not found. */
+
+
+/**
+ * @brief Driver state machine possible states.
+ */
+typedef enum __attribute__ ((__packed__)) {
+ SENSOR_UNINIT = 0, /**< Not initialized. */
+ SENSOR_INIT = 1, /**< Initialized. */
+ SENSOR_STARTED = 2, /**< Started. */
+ SENSOR_MEASURING = 4, /**< Measuring. */
+ SENSOR_READY = 3, /**< Ready. */
+ SENSOR_STOPPED = 5, /**< Stopped. */
+ SENSOR_ERROR = 6, /**< Error. */
+} sensor_state_t;
+
+#endif
+
+
diff --git a/os/various/devices_lib/sensors/tsl2561.c b/os/various/devices_lib/sensors/tsl2561.c
new file mode 100644
index 0000000..a4ac8ec
--- /dev/null
+++ b/os/various/devices_lib/sensors/tsl2561.c
@@ -0,0 +1,386 @@
+/*
+ TSL2561 for ChibiOS/RT - Copyright (C) 2016 Stephane D'Alu
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+/**
+ * Illuminance calculation code provided by www.taosinc.com
+ * DOC: http://ams.com/eng/content/download/250096/975518/143687
+ */
+#define I2C_HELPERS_AUTOMATIC_DRV TRUE
+
+#include "hal.h"
+#include "i2c_helpers.h"
+#include "tsl2561.h"
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+// Integration time in µs
+#define TSL2561_DELAY_INTTIME_SHORT 13700 // 13.7 ms
+#define TSL2561_DELAY_INTTIME_MEDIUM 120000 // 120.0 ms
+#define TSL2561_DELAY_INTTIME_LONG 450000 // 450.0 ms
+
+
+#define TSL2561_COMMAND_BIT (0x80)
+#define TSL2561_CLEAR_BIT (0x40)
+#define TSL2561_WORD_BIT (0x20)
+#define TSL2561_BLOCK_BIT (0x10)
+
+#define TSL2561_CONTROL_POWERON (0x03)
+#define TSL2561_CONTROL_POWEROFF (0x00)
+
+#define TSL2561_LUX_LUXSCALE (14)
+#define TSL2561_LUX_RATIOSCALE (9)
+#define TSL2561_LUX_CHSCALE (10) // Scale channel values by 2^10
+#define TSL2561_LUX_CHSCALE_TINT0 (0x7517) // 322/11 * 2^TSL2561_LUX_CHSCALE
+#define TSL2561_LUX_CHSCALE_TINT1 (0x0FE7) // 322/81 * 2^TSL2561_LUX_CHSCALE
+
+
+// I2C Register
+#define TSL2561_REG_CONTROL 0x00
+#define TSL2561_REG_TIMING 0x01
+#define TSL2561_REG_THRESHHOLDLLOW 0x02
+#define TSL2561_REG_THRESHHOLDLHIGH 0x03
+#define TSL2561_REG_THRESHHOLDHLOW 0x04
+#define TSL2561_REG_THRESHHOLDHHIGH 0x05
+#define TSL2561_REG_INTERRUPT 0x06
+#define TSL2561_REG_CRC 0x08
+#define TSL2561_REG_ID 0x0A
+#define TSL2561_REG_DATA0LOW 0x0C
+#define TSL2561_REG_DATA0HIGH 0x0D
+#define TSL2561_REG_DATA1LOW 0x0E
+#define TSL2561_REG_DATA1HIGH 0x0F
+
+
+// Auto-gain thresholds
+#define TSL2561_AGC_THI_SHORT (4850) // Max value at Ti 13ms = 5047
+#define TSL2561_AGC_TLO_SHORT (100)
+#define TSL2561_AGC_THI_MEDIUM (36000) // Max value at Ti 101ms = 37177
+#define TSL2561_AGC_TLO_MEDIUM (200)
+#define TSL2561_AGC_THI_LONG (63000) // Max value at Ti 402ms = 65535
+#define TSL2561_AGC_TLO_LONG (500)
+
+// Clipping thresholds
+#define TSL2561_CLIPPING_SHORT (4900)
+#define TSL2561_CLIPPING_MEDIUM (37000)
+#define TSL2561_CLIPPING_LONG (65000)
+
+// T, FN and CL package values
+#define TSL2561_LUX_K1T (0x0040) // 0.125 * 2^RATIO_SCALE
+#define TSL2561_LUX_B1T (0x01f2) // 0.0304 * 2^LUX_SCALE
+#define TSL2561_LUX_M1T (0x01be) // 0.0272 * 2^LUX_SCALE
+#define TSL2561_LUX_K2T (0x0080) // 0.250 * 2^RATIO_SCALE
+#define TSL2561_LUX_B2T (0x0214) // 0.0325 * 2^LUX_SCALE
+#define TSL2561_LUX_M2T (0x02d1) // 0.0440 * 2^LUX_SCALE
+#define TSL2561_LUX_K3T (0x00c0) // 0.375 * 2^RATIO_SCALE
+#define TSL2561_LUX_B3T (0x023f) // 0.0351 * 2^LUX_SCALE
+#define TSL2561_LUX_M3T (0x037b) // 0.0544 * 2^LUX_SCALE
+#define TSL2561_LUX_K4T (0x0100) // 0.50 * 2^RATIO_SCALE
+#define TSL2561_LUX_B4T (0x0270) // 0.0381 * 2^LUX_SCALE
+#define TSL2561_LUX_M4T (0x03fe) // 0.0624 * 2^LUX_SCALE
+#define TSL2561_LUX_K5T (0x0138) // 0.61 * 2^RATIO_SCALE
+#define TSL2561_LUX_B5T (0x016f) // 0.0224 * 2^LUX_SCALE
+#define TSL2561_LUX_M5T (0x01fc) // 0.0310 * 2^LUX_SCALE
+#define TSL2561_LUX_K6T (0x019a) // 0.80 * 2^RATIO_SCALE
+#define TSL2561_LUX_B6T (0x00d2) // 0.0128 * 2^LUX_SCALE
+#define TSL2561_LUX_M6T (0x00fb) // 0.0153 * 2^LUX_SCALE
+#define TSL2561_LUX_K7T (0x029a) // 1.3 * 2^RATIO_SCALE
+#define TSL2561_LUX_B7T (0x0018) // 0.00146 * 2^LUX_SCALE
+#define TSL2561_LUX_M7T (0x0012) // 0.00112 * 2^LUX_SCALE
+#define TSL2561_LUX_K8T (0x029a) // 1.3 * 2^RATIO_SCALE
+#define TSL2561_LUX_B8T (0x0000) // 0.000 * 2^LUX_SCALE
+#define TSL2561_LUX_M8T (0x0000) // 0.000 * 2^LUX_SCALE
+
+// CS package values
+#define TSL2561_LUX_K1C (0x0043) // 0.130 * 2^RATIO_SCALE
+#define TSL2561_LUX_B1C (0x0204) // 0.0315 * 2^LUX_SCALE
+#define TSL2561_LUX_M1C (0x01ad) // 0.0262 * 2^LUX_SCALE
+#define TSL2561_LUX_K2C (0x0085) // 0.260 * 2^RATIO_SCALE
+#define TSL2561_LUX_B2C (0x0228) // 0.0337 * 2^LUX_SCALE
+#define TSL2561_LUX_M2C (0x02c1) // 0.0430 * 2^LUX_SCALE
+#define TSL2561_LUX_K3C (0x00c8) // 0.390 * 2^RATIO_SCALE
+#define TSL2561_LUX_B3C (0x0253) // 0.0363 * 2^LUX_SCALE
+#define TSL2561_LUX_M3C (0x0363) // 0.0529 * 2^LUX_SCALE
+#define TSL2561_LUX_K4C (0x010a) // 0.520 * 2^RATIO_SCALE
+#define TSL2561_LUX_B4C (0x0282) // 0.0392 * 2^LUX_SCALE
+#define TSL2561_LUX_M4C (0x03df) // 0.0605 * 2^LUX_SCALE
+#define TSL2561_LUX_K5C (0x014d) // 0.65 * 2^RATIO_SCALE
+#define TSL2561_LUX_B5C (0x0177) // 0.0229 * 2^LUX_SCALE
+#define TSL2561_LUX_M5C (0x01dd) // 0.0291 * 2^LUX_SCALE
+#define TSL2561_LUX_K6C (0x019a) // 0.80 * 2^RATIO_SCALE
+#define TSL2561_LUX_B6C (0x0101) // 0.0157 * 2^LUX_SCALE
+#define TSL2561_LUX_M6C (0x0127) // 0.0180 * 2^LUX_SCALE
+#define TSL2561_LUX_K7C (0x029a) // 1.3 * 2^RATIO_SCALE
+#define TSL2561_LUX_B7C (0x0037) // 0.00338 * 2^LUX_SCALE
+#define TSL2561_LUX_M7C (0x002b) // 0.00260 * 2^LUX_SCALE
+#define TSL2561_LUX_K8C (0x029a) // 1.3 * 2^RATIO_SCALE
+#define TSL2561_LUX_B8C (0x0000) // 0.000 * 2^LUX_SCALE
+#define TSL2561_LUX_M8C (0x0000) // 0.000 * 2^LUX_SCALE
+
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+#define CEILING(x,y) (((x) + (y) - 1) / (y))
+
+static inline unsigned int
+calculateIlluminance(TSL2561_integration_time_t integration_time,
+ TSL2561_gain_t gain,
+ uint16_t broadband, uint16_t ir,
+ unsigned int partno) {
+ unsigned long channel_1;
+ unsigned long channel_0;
+
+ /* Get value for channel scaling, and clipping */
+ uint16_t clip_threshold = 0;
+ unsigned long channel_scale = 0;
+ switch (integration_time) {
+ case TSL2561_INTEGRATIONTIME_SHORT:
+ clip_threshold = TSL2561_CLIPPING_SHORT;
+ channel_scale = TSL2561_LUX_CHSCALE_TINT0;
+ break;
+ case TSL2561_INTEGRATIONTIME_MEDIUM:
+ clip_threshold = TSL2561_CLIPPING_MEDIUM;
+ channel_scale = TSL2561_LUX_CHSCALE_TINT1;
+ break;
+ case TSL2561_INTEGRATIONTIME_LONG:
+ clip_threshold = TSL2561_CLIPPING_LONG;
+ channel_scale = (1 << TSL2561_LUX_CHSCALE);
+ break;
+ default:
+ // assert failed
+ break;
+ }
+
+ /* Check for saturated sensor (ie: clipping) */
+ if ((broadband > clip_threshold) || (ir > clip_threshold)) {
+ return TSL2561_OVERLOADED;
+ }
+
+ /* Scale for gain (1x or 16x) */
+ if (gain == TSL2561_GAIN_1X)
+ channel_scale <<= 4;
+
+ /* Scale the channel values */
+ channel_0 = (broadband * channel_scale) >> TSL2561_LUX_CHSCALE;
+ channel_1 = (ir * channel_scale) >> TSL2561_LUX_CHSCALE;
+
+ /* Find the ratio of the channel values (Channel_1/Channel_0) */
+ unsigned long _ratio = 0;
+ if (channel_0 != 0)
+ _ratio = (channel_1 << (TSL2561_LUX_RATIOSCALE+1)) / channel_0;
+ unsigned long ratio = (_ratio + 1) >> 1; /* round the ratio value */
+
+ /* Find linear approximation */
+ unsigned int b = 0;
+ unsigned int m = 0;
+
+ switch (partno) {
+#if TSL2561_WITH_CS
+ case 0x1: // 0001 = TSL2561 CS
+ if ((ratio >= 0) && (ratio <= TSL2561_LUX_K1C))
+ { b=TSL2561_LUX_B1C; m=TSL2561_LUX_M1C; }
+ else if (ratio <= TSL2561_LUX_K2C)
+ { b=TSL2561_LUX_B2C; m=TSL2561_LUX_M2C; }
+ else if (ratio <= TSL2561_LUX_K3C)
+ { b=TSL2561_LUX_B3C; m=TSL2561_LUX_M3C; }
+ else if (ratio <= TSL2561_LUX_K4C)
+ { b=TSL2561_LUX_B4C; m=TSL2561_LUX_M4C; }
+ else if (ratio <= TSL2561_LUX_K5C)
+ { b=TSL2561_LUX_B5C; m=TSL2561_LUX_M5C; }
+ else if (ratio <= TSL2561_LUX_K6C)
+ { b=TSL2561_LUX_B6C; m=TSL2561_LUX_M6C; }
+ else if (ratio <= TSL2561_LUX_K7C)
+ { b=TSL2561_LUX_B7C; m=TSL2561_LUX_M7C; }
+ else if (ratio > TSL2561_LUX_K8C)
+ { b=TSL2561_LUX_B8C; m=TSL2561_LUX_M8C; }
+ break;
+#endif
+#if TSL2561_WITH_T_FN_CL
+ case 0x5: // 0101 = TSL2561 T/FN/CL
+ if ((ratio >= 0) && (ratio <= TSL2561_LUX_K1T))
+ { b=TSL2561_LUX_B1T; m=TSL2561_LUX_M1T; }
+ else if (ratio <= TSL2561_LUX_K2T)
+ { b=TSL2561_LUX_B2T; m=TSL2561_LUX_M2T; }
+ else if (ratio <= TSL2561_LUX_K3T)
+ { b=TSL2561_LUX_B3T; m=TSL2561_LUX_M3T; }
+ else if (ratio <= TSL2561_LUX_K4T)
+ { b=TSL2561_LUX_B4T; m=TSL2561_LUX_M4T; }
+ else if (ratio <= TSL2561_LUX_K5T)
+ { b=TSL2561_LUX_B5T; m=TSL2561_LUX_M5T; }
+ else if (ratio <= TSL2561_LUX_K6T)
+ { b=TSL2561_LUX_B6T; m=TSL2561_LUX_M6T; }
+ else if (ratio <= TSL2561_LUX_K7T)
+ { b=TSL2561_LUX_B7T; m=TSL2561_LUX_M7T; }
+ else if (ratio > TSL2561_LUX_K8T)
+ { b=TSL2561_LUX_B8T; m=TSL2561_LUX_M8T; }
+ break;
+#endif
+ default:
+ // assert failed
+ break;
+ }
+
+ /* Compute illuminance */
+ long ill = ((channel_0 * b) - (channel_1 * m));
+ if (ill < 0) ill = 0; /* Do not allow negative lux value */
+ ill += (1 << (TSL2561_LUX_LUXSCALE-1)); /* Round lsb (2^(LUX_SCALE-1)) */
+ ill >>= TSL2561_LUX_LUXSCALE; /* Strip fractional part */
+
+ /* Signal I2C had no errors */
+ return ill;
+}
+
+static inline msg_t
+_readChannel(TSL2561_drv *drv, uint16_t *broadband, uint16_t *ir) {
+ msg_t msg;
+ if (((msg = i2c_reg_recv16_le(
+ TSL2561_COMMAND_BIT | TSL2561_WORD_BIT | TSL2561_REG_DATA0LOW,
+ broadband)) < MSG_OK) ||
+ ((msg = i2c_reg_recv16_le(
+ TSL2561_COMMAND_BIT | TSL2561_WORD_BIT | TSL2561_REG_DATA1LOW,
+ ir )) < MSG_OK))
+ return msg;
+ return MSG_OK;
+}
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+void
+TSL2561_init(TSL2561_drv *drv, TSL2561_config *config) {
+ drv->config = config;
+ drv->gain = TSL2561_GAIN_1X;
+ drv->integration_time = TSL2561_INTEGRATIONTIME_LONG;
+ drv->state = SENSOR_INIT;
+
+ i2c_reg_recv8(TSL2561_COMMAND_BIT | TSL2561_REG_ID,
+ (uint8_t*)&drv->id);
+}
+
+msg_t
+TSL2561_check(TSL2561_drv *drv) {
+ uint8_t rx;
+
+ msg_t msg;
+ if ((msg = i2c_reg_recv8(TSL2561_REG_ID, &rx)) < MSG_OK)
+ return msg;
+ if (!(rx & 0x0A))
+ return SENSOR_NOTFOUND;
+ return MSG_OK;
+}
+
+msg_t
+TSL2561_stop(TSL2561_drv *drv) {
+ struct __attribute__((packed)) {
+ uint8_t reg;
+ uint8_t conf;
+ } tx = { TSL2561_COMMAND_BIT | TSL2561_REG_CONTROL,
+ TSL2561_CONTROL_POWEROFF };
+
+ return i2c_send((uint8_t*)&tx, sizeof(tx));
+}
+
+msg_t
+TSL2561_start(TSL2561_drv *drv) {
+ struct __attribute__((packed)) {
+ uint8_t reg;
+ uint8_t conf;
+ } tx = { TSL2561_COMMAND_BIT | TSL2561_REG_CONTROL,
+ TSL2561_CONTROL_POWERON };
+
+ return i2c_send((uint8_t*)&tx, sizeof(tx));
+}
+
+msg_t
+TSL2561_setIntegrationTime(TSL2561_drv *drv,
+ TSL2561_integration_time_t time) {
+ struct __attribute__((packed)) {
+ uint8_t reg;
+ uint8_t conf;
+ } tx = { TSL2561_COMMAND_BIT | TSL2561_REG_TIMING,
+ (uint8_t)(time | drv->gain) };
+
+ msg_t msg;
+ if ((msg = i2c_send((uint8_t*)&tx, sizeof(tx))) < MSG_OK)
+ return msg;
+
+ drv->integration_time = time;
+
+ return MSG_OK;
+}
+
+msg_t
+TSL2561_setGain(TSL2561_drv *drv,
+ TSL2561_gain_t gain) {
+ struct __attribute__((packed)) {
+ uint8_t reg;
+ uint8_t conf;
+ } tx = { TSL2561_COMMAND_BIT | TSL2561_REG_TIMING,
+ (uint8_t)(drv->integration_time | gain) };
+
+ msg_t msg;
+ if ((msg = i2c_send((uint8_t*)&tx, sizeof(tx))) < MSG_OK)
+ return msg;
+
+ drv->gain = gain;
+
+ return MSG_OK;
+}
+
+unsigned int
+TSL2561_getAcquisitionTime(TSL2561_drv *drv) {
+ switch (drv->integration_time) {
+ case TSL2561_INTEGRATIONTIME_SHORT:
+ return CEILING(TSL2561_DELAY_INTTIME_SHORT , 1000);
+ case TSL2561_INTEGRATIONTIME_MEDIUM:
+ return CEILING(TSL2561_DELAY_INTTIME_MEDIUM, 1000);
+ case TSL2561_INTEGRATIONTIME_LONG:
+ return CEILING(TSL2561_DELAY_INTTIME_LONG , 1000);
+ }
+ return -1;
+}
+
+
+msg_t
+TSL2561_readIlluminance(TSL2561_drv *drv,
+ unsigned int *illuminance) {
+ uint16_t broadband;
+ uint16_t ir;
+
+ /* Read channels */
+ msg_t msg;
+ if ((msg = _readChannel(drv, &broadband, &ir)) < MSG_OK)
+ return msg;
+
+ /* Calculate illuminance */
+ *illuminance =
+ calculateIlluminance(drv->integration_time, drv->gain,
+ broadband, ir, drv->id.partno);
+ /* Ok */
+ return SENSOR_OK;
+}
+
diff --git a/os/various/devices_lib/sensors/tsl2561.h b/os/various/devices_lib/sensors/tsl2561.h
new file mode 100644
index 0000000..75e7c78
--- /dev/null
+++ b/os/various/devices_lib/sensors/tsl2561.h
@@ -0,0 +1,241 @@
+/*
+ TSL2561 for ChibiOS/RT - Copyright (C) 2016 Stephane D'Alu
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+/**
+ * @file tsl2561.h
+ * @brief TSL2561 Light sensor interface module header.
+ *
+ * @{
+ */
+
+#ifndef _SENSOR_TSL2561_H_
+#define _SENSOR_TSL2561_H_
+
+#include <math.h>
+#include "i2c_helpers.h"
+#include "sensor.h"
+
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+#define TSL2561_CONTINUOUS_ACQUISITION_SUPPORTED TRUE
+
+#define TSL2561_OVERLOADED (-1)
+
+
+/* I2C address */
+#define TSL2561_I2CADDR_LOW (0x29)
+#define TSL2561_I2CADDR_FLOAT (0x39)
+#define TSL2561_I2CADDR_HIGH (0x49)
+
+/**
+ * @brief Time necessary for the sensor to boot
+ */
+#define TSL2561_BOOTUP_TIME 0
+
+/**
+ * @brief Time necessary for the sensor to start
+ */
+#define TSL2561_STARTUP_TIME 0
+
+
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+#ifndef TSL2561_WITH_CS
+#define TSL2561_WITH_CS 0
+#endif
+
+#ifndef TSL2561_WITH_T_FN_CL
+#define TSL2561_WITH_T_FN_CL 1
+#endif
+
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+
+
+#define TSL2561_I2CADDR_DEFAULT TSL2561_I2CADDR_FLOAT
+
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/**
+ * @brief TSL2561 configuration structure.
+ */
+typedef struct {
+ I2CHelper i2c; /* keep it first */
+} TSL2561_config;
+
+/**
+ * @brief Available integration time
+ *
+ * @details Available integration time are:
+ * 13.7ms, 101ms, 402ms
+ */
+typedef enum {
+ TSL2561_INTEGRATIONTIME_SHORT = 0x00, /**< @brief 13.7ms */
+ TSL2561_INTEGRATIONTIME_MEDIUM = 0x01, /**< @brief 101.0ms */
+ TSL2561_INTEGRATIONTIME_LONG = 0x02, /**< @brief 402.0ms */
+} TSL2561_integration_time_t;
+
+/**
+ * @brief Available gain
+ *
+ * @details Available gain are 1x, 16x
+ */
+typedef enum {
+ TSL2561_GAIN_1X = 0x00, /**< @brief 1x gain */
+ TSL2561_GAIN_16X = 0x10, /**< @brief 16x gain */
+} TSL2561_gain_t;
+
+/**
+ * @brief TSL2561 configuration structure.
+ */
+typedef struct {
+ TSL2561_config *config;
+ sensor_state_t state;
+ TSL2561_gain_t gain;
+ TSL2561_integration_time_t integration_time;
+ struct PACKED {
+ uint8_t revno : 4;
+ uint8_t partno : 4; } id;
+} TSL2561_drv;
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+/**
+ * @brief Initialize the sensor driver
+ */
+void
+TSL2561_init(TSL2561_drv *drv,
+ TSL2561_config *config);
+
+/**
+ * @brief Start the sensor
+ */
+msg_t
+TSL2561_start(TSL2561_drv *drv);
+
+/**
+ * @brief Stop the sensor
+ *
+ * @details If the sensor support it, it will be put in low energy mode.
+ */
+msg_t
+TSL2561_stop(TSL2561_drv *drv);
+
+/**
+ * @brief Check that the sensor is really present
+ */
+msg_t
+TSL2561_check(TSL2561_drv *drv);
+
+/**
+ * @brief Time in milli-seconds necessary for acquiring a naw measure
+ *
+ * @returns
+ * unsigned int time in millis-seconds
+ */
+unsigned int
+TSL2561_getAcquisitionTime(TSL2561_drv *drv);
+
+/**
+ * @brief Trigger a mesure acquisition
+ */
+static inline msg_t
+TSL2561_startMeasure(TSL2561_drv *drv) {
+ (void)drv;
+ return MSG_OK;
+};
+
+/**
+ * @brief Read the newly acquiered measure
+ *
+ * @note According the the sensor design the measure read
+ * can be any value acquired after the acquisition time
+ * and the call to readMeasure.
+ */
+msg_t
+TSL2561_readMeasure(TSL2561_drv *drv,
+ unsigned int illuminance);
+
+msg_t
+TSL2561_setGain(TSL2561_drv *drv,
+ TSL2561_gain_t gain);
+
+msg_t
+TSL2561_setIntegrationTime(TSL2561_drv *drv,
+ TSL2561_integration_time_t time);
+
+/**
+ * @brief Read temperature and humidity
+ *
+ * @details According to the sensor specification/configuration
+ * (see #TSL2561_CONTINUOUS_ACQUISITION_SUPPORTED),
+ * if the sensor is doing continuous measurement
+ * it's value will be requested and returned immediately.
+ * Otherwise a measure is started, the necessary amount of
+ * time for acquiring the value is spend sleeping (not spinning),
+ * and finally the measure is read.
+ *
+ * @note In continuous measurement mode, if you just started
+ * the sensor, you will need to wait getAcquisitionTime()
+ * in addition to the usual getStartupTime()
+
+ * @note If using several sensors, it is better to start all the
+ * measure together, wait for the sensor having the longuest
+ * aquisition time, and finally read all the values
+ */
+msg_t
+TSL2561_readIlluminance(TSL2561_drv *drv,
+ unsigned int *illuminance);
+
+/**
+ * @brief Return the illuminance value in Lux
+ *
+ * @details Use readIlluminance() for returning the humidity value.
+ *
+ * @note Prefere readIlluminance()if you need better error handling.
+ *
+ * @return Illuminance in Lux
+ * @retval unsigned int illuminace value
+ * @retval -1 on failure
+ */
+static inline unsigned int
+TSL2561_getIlluminance(TSL2561_drv *drv) {
+ unsigned int illuminance = -1;
+ TSL2561_readIlluminance(drv, &illuminance);
+ return illuminance;
+}
+
+
+#endif
+
diff --git a/os/various/devices_lib/sensors/tsl2591.c b/os/various/devices_lib/sensors/tsl2591.c
new file mode 100644
index 0000000..c0bbee0
--- /dev/null
+++ b/os/various/devices_lib/sensors/tsl2591.c
@@ -0,0 +1,272 @@
+/*
+ TSL2591 for ChibiOS/RT - Copyright (C) 2016 Stephane D'Alu
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+/**
+ *
+ * DOC: http://ams.com/eng/content/download/389383/1251117/221235
+ */
+
+#define I2C_HELPERS_AUTOMATIC_DRV TRUE
+
+#include "hal.h"
+#include "i2c_helpers.h"
+#include "tsl2591.h"
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+#define TSL2591_LUX_DF (408.0F)
+#define TSL2591_LUX_COEFB (1.64F) // CH0 coefficient
+#define TSL2591_LUX_COEFC (0.59F) // CH1 coefficient A
+#define TSL2591_LUX_COEFD (0.86F) // CH2 coefficient B
+
+/* I2C registers */
+#define TSL2591_REG_ENABLE 0x00
+#define TSL2591_REG_CONFIG 0x01 /**< @brief gain and integration */
+#define TSL2591_REG_AILTL 0x04
+#define TSL2591_REG_AILTH 0x05
+#define TSL2591_REG_AIHTL 0x06
+#define TSL2591_REG_AIHTH 0x07
+#define TSL2591_REG_NPAILTL 0x08
+#define TSL2591_REG_NPAILTH 0x09
+#define TSL2591_REG_NPAIHTL 0x0A
+#define TSL2591_REG_NPAIHTH 0x0B
+#define TSL2591_REG_PERSIST 0x0C
+#define TSL2591_REG_PID 0x11 /**< @brief Package ID */
+#define TSL2591_REG_ID 0x12 /**< @brief Device ID */
+#define TSL2591_REG_STATUS 0x13 /**< @brief Device status */
+#define TSL2591_REG_C0DATAL 0x14 /**< @brief CH0 ADC low data byte */
+#define TSL2591_REG_C0DATAH 0x15 /**< @brief CH0 ADC high data byte */
+#define TSL2591_REG_C1DATAL 0x16 /**< @brief CH1 ADC low data byte */
+#define TSL2591_REG_C1DATAH 0x17 /**< @brief CH1 ADC high data byte */
+
+#define TSL2591_REG_COMMAND 0x80 /**< @brief Select command register */
+#define TSL2591_REG_NORMAL 0x20 /**< @brief Normal opearation */
+#define TSL2591_REG_SPECIAL 0x60 /**< @brief Special function */
+
+#define TSL2591_ID_TSL2591 0x50
+
+#define TSL2591_VISIBLE (2) // channel 0 - channel 1
+#define TSL2591_INFRARED (1) // channel 1
+#define TSL2591_FULLSPECTRUM (0) // channel 0
+
+#define TSL2591_ENABLE_POWERON (0x01)
+#define TSL2591_ENABLE_POWEROFF (0x00)
+#define TSL2591_ENABLE_AEN (0x02)
+#define TSL2591_ENABLE_AIEN (0x10)
+
+#define TSL2591_CONTROL_RESET (0x80)
+
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+static inline uint32_t
+calculateIlluminance(TSL2591_integration_time_t integration_time,
+ TSL2591_gain_t gain,
+ uint16_t broadband, uint16_t ir) {
+ uint16_t atime, again;
+
+ /* Check for overflow conditions first */
+ if ((broadband == 0xFFFF) | (ir == 0xFFFF)) {
+ return 0xFFFFFFFF; /* Signal overflow */
+ }
+
+ switch (integration_time) {
+ case TSL2591_INTEGRATIONTIME_100MS : atime = 100; break;
+ case TSL2591_INTEGRATIONTIME_200MS : atime = 200; break;
+ case TSL2591_INTEGRATIONTIME_300MS : atime = 300; break;
+ case TSL2591_INTEGRATIONTIME_400MS : atime = 400; break;
+ case TSL2591_INTEGRATIONTIME_500MS : atime = 500; break;
+ case TSL2591_INTEGRATIONTIME_600MS : atime = 600; break;
+ }
+
+ switch (gain) {
+ case TSL2591_GAIN_1X : again = 1; break;
+ case TSL2591_GAIN_25X : again = 25; break;
+ case TSL2591_GAIN_415X : again = 415; break;
+ case TSL2591_GAIN_10000X : again = 10000; break;
+ }
+
+ // cpl = (ATIME * AGAIN) / DF
+ float cpl = ((float)(atime * again)) / ((float)TSL2591_LUX_DF);
+ float lux1 = ( ((float)broadband) - (TSL2591_LUX_COEFB * (float)ir) ) / cpl;
+ float lux2 = ( (TSL2591_LUX_COEFC * (float)broadband) -
+ (TSL2591_LUX_COEFD * (float)ir ) ) / cpl;
+
+ return (uint32_t) (lux1 > lux2 ? lux1 : lux2);
+}
+
+static inline msg_t
+_readChannel(TSL2591_drv *drv, uint16_t *broadband, uint16_t *ir) {
+ msg_t msg;
+ if (((msg = i2c_reg_recv16_le(
+ TSL2591_REG_COMMAND | TSL2591_REG_NORMAL | TSL2591_REG_C0DATAL,
+ broadband)) < MSG_OK) ||
+ ((msg = i2c_reg_recv16_le(
+ TSL2591_REG_COMMAND | TSL2591_REG_NORMAL | TSL2591_REG_C1DATAL,
+ ir )) < MSG_OK))
+ return msg;
+
+ return MSG_OK;
+}
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+void
+TSL2591_init(TSL2591_drv *drv, TSL2591_config *config) {
+ drv->config = config;
+ drv->gain = TSL2591_GAIN_1X;
+ drv->integration_time = TSL2591_INTEGRATIONTIME_100MS;
+ drv->state = SENSOR_INIT;
+}
+
+msg_t
+TSL2591_check(TSL2591_drv *drv) {
+ uint8_t id;
+
+ msg_t msg;
+ if ((msg = i2c_reg_recv8(TSL2591_REG_COMMAND | TSL2591_REG_NORMAL |
+ TSL2591_REG_ID, &id)) < MSG_OK)
+ return msg;
+
+ if (id != TSL2591_ID_TSL2591)
+ return SENSOR_NOTFOUND;
+
+ return MSG_OK;
+}
+
+msg_t
+TSL2591_start(TSL2591_drv *drv) {
+ struct __attribute__((packed)) {
+ uint8_t reg;
+ uint8_t conf;
+ } tx_config = {
+ TSL2591_REG_COMMAND | TSL2591_REG_NORMAL | TSL2591_REG_CONFIG,
+ (uint8_t)(drv->integration_time | drv->gain) };
+
+ struct __attribute__((packed)) {
+ uint8_t reg;
+ uint8_t conf;
+ } tx_start = {
+ TSL2591_REG_COMMAND | TSL2591_REG_NORMAL | TSL2591_REG_ENABLE,
+ TSL2591_ENABLE_POWERON };
+
+ msg_t msg;
+
+ if (((msg = i2c_send((uint8_t*)&tx_config, sizeof(tx_config))) < MSG_OK) ||
+ ((msg = i2c_send((uint8_t*)&tx_start, sizeof(tx_start ))) < MSG_OK)) {
+ drv->state = SENSOR_ERROR;
+ return msg;
+ }
+
+ drv->state = SENSOR_STARTED;
+ return MSG_OK;
+}
+
+msg_t
+TSL2591_stop(TSL2591_drv *drv) {
+ struct __attribute__((packed)) {
+ uint8_t reg;
+ uint8_t conf;
+ } tx_stop = {
+ TSL2591_REG_COMMAND | TSL2591_REG_NORMAL | TSL2591_REG_ENABLE,
+ TSL2591_ENABLE_POWEROFF };
+
+ return i2c_send((uint8_t*)&tx_stop, sizeof(tx_stop));
+}
+
+msg_t
+TSL2591_setIntegrationTime(TSL2591_drv *drv,
+ TSL2591_integration_time_t time) {
+ struct __attribute__((packed)) {
+ uint8_t reg;
+ uint8_t conf;
+ } tx = { TSL2591_REG_COMMAND | TSL2591_REG_NORMAL | TSL2591_REG_CONFIG,
+ (uint8_t)(time | drv->gain) };
+
+ msg_t msg;
+ if ((msg = i2c_send((uint8_t*)&tx, sizeof(tx))) < MSG_OK)
+ return msg;
+
+ drv->integration_time = time;
+
+ return MSG_OK;
+}
+
+msg_t
+TSL2591_setGain(TSL2591_drv *drv,
+ TSL2591_gain_t gain) {
+ struct __attribute__((packed)) {
+ uint8_t reg;
+ uint8_t conf;
+ } tx = { TSL2591_REG_COMMAND | TSL2591_REG_NORMAL | TSL2591_REG_CONFIG,
+ (uint8_t)(drv->integration_time | gain) };
+
+ msg_t msg;
+ if ((msg = i2c_send((uint8_t*)&tx, sizeof(tx))) < MSG_OK)
+ return msg;
+
+ drv->gain = gain;
+
+ return MSG_OK;
+}
+
+unsigned int
+TSL2591_getAcquisitionTime(TSL2591_drv *drv) {
+ switch (drv->integration_time) {
+ case TSL2591_INTEGRATIONTIME_100MS : return 100;
+ case TSL2591_INTEGRATIONTIME_200MS : return 200;
+ case TSL2591_INTEGRATIONTIME_300MS : return 300;
+ case TSL2591_INTEGRATIONTIME_400MS : return 400;
+ case TSL2591_INTEGRATIONTIME_500MS : return 500;
+ case TSL2591_INTEGRATIONTIME_600MS : return 600;
+ }
+ return -1;
+}
+
+
+msg_t
+TSL2591_readIlluminance(TSL2591_drv *drv,
+ unsigned int *illuminance) {
+ uint16_t broadband;
+ uint16_t ir;
+
+ /* Read channels */
+ msg_t msg;
+ if ((msg = _readChannel(drv, &broadband, &ir)) < MSG_OK)
+ return msg;
+
+ /* Calculate illuminance */
+ *illuminance =
+ calculateIlluminance(drv->integration_time, drv->gain,
+ broadband, ir);
+ /* Ok */
+ return SENSOR_OK;
+}
+
diff --git a/os/various/devices_lib/sensors/tsl2591.h b/os/various/devices_lib/sensors/tsl2591.h
new file mode 100644
index 0000000..8320eb8
--- /dev/null
+++ b/os/various/devices_lib/sensors/tsl2591.h
@@ -0,0 +1,238 @@
+/*
+ TSL2591 for ChibiOS/RT - Copyright (C) 2016 Stephane D'Alu
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+/**
+ * @file tsl2591.h
+ * @brief TSL2591 Light sensor interface module header.
+ *
+ * @{
+ */
+
+#ifndef _SENSOR_TSL2591_H_
+#define _SENSOR_TSL2591_H_
+
+#include <math.h>
+#include "i2c_helpers.h"
+#include "sensor.h"
+
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/**
+ * @brief Device sensor continuous acquisition support.
+ */
+#define TSL2591_CONTINUOUS_ACQUISITION_SUPPORTED TRUE
+
+/**
+ * @brief I2C address.
+ */
+#define TSL2591_I2CADDR_FIXED 0x29
+
+/**
+ * @brief Time necessary for the sensor to boot
+ */
+#define TSL2591_BOOTUP_TIME 0
+
+/**
+ * @brief Time necessary for the sensor to start
+ */
+#define TSL2591_STARTUP_TIME 0
+
+
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+
+/**
+ * @brief Default I2C address (when pin unconfigured)
+ */
+#define TSL2591_I2CADDR_DEFAULT TSL2591_I2CADDR_FIXED
+
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/**
+ * @brief TSL2591 configuration structure.
+ */
+typedef struct {
+ I2CHelper i2c; /* keep it first */
+} TSL2591_config;
+
+/**
+ * @brief Available integration time
+ *
+ * @details Available integration time are:
+ * 100ms, 200ms, 300ms, 400ms, 500ms and 600ms
+ */
+typedef enum {
+ TSL2591_INTEGRATIONTIME_100MS = 0x00, /**< @brief 100ms */
+ TSL2591_INTEGRATIONTIME_200MS = 0x01, /**< @brief 200ms */
+ TSL2591_INTEGRATIONTIME_300MS = 0x02, /**< @brief 300ms */
+ TSL2591_INTEGRATIONTIME_400MS = 0x03, /**< @brief 400ms */
+ TSL2591_INTEGRATIONTIME_500MS = 0x04, /**< @brief 500ms */
+ TSL2591_INTEGRATIONTIME_600MS = 0x05, /**< @brief 600ms */
+} TSL2591_integration_time_t;
+
+/**
+ * @brief Available gain
+ *
+ * @details Available gain are 1x, 25x, 415x, 10000x
+ */
+typedef enum {
+ TSL2591_GAIN_1X = 0x00, /**< @brief 1x gain */
+ TSL2591_GAIN_25X = 0x10, /**< @brief 25x gain */
+ TSL2591_GAIN_415X = 0x20, /**< @brief 415x gain */
+ TSL2591_GAIN_10000X = 0x30, /**< @brief 10000x gain */
+} TSL2591_gain_t;
+
+/**
+ * @brief TSL2591 configuration structure.
+ */
+typedef struct {
+ TSL2591_config *config;
+ sensor_state_t state;
+ TSL2591_gain_t gain;
+ TSL2591_integration_time_t integration_time;
+} TSL2591_drv;
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+/**
+ * @brief Initialize the sensor driver
+ */
+void
+TSL2591_init(TSL2591_drv *drv,
+ TSL2591_config *config);
+
+/**
+ * @brief Start the sensor
+ */
+msg_t
+TSL2591_start(TSL2591_drv *drv);
+
+/**
+ * @brief Stop the sensor
+ *
+ * @details If the sensor support it, it will be put in low energy mode.
+ */
+msg_t
+TSL2591_stop(TSL2591_drv *drv);
+
+/**
+ * @brief Check that the sensor is really present
+ */
+msg_t
+TSL2591_check(TSL2591_drv *drv);
+
+/**
+ * @brief Time in milli-seconds necessary for acquiring a naw measure
+ *
+ * @returns
+ * unsigned int time in millis-seconds
+ */
+unsigned int
+TSL2591_getAcquisitionTime(TSL2591_drv *drv);
+
+/**
+ * @brief Trigger a mesure acquisition
+ */
+static inline msg_t
+TSL2591_startMeasure(TSL2591_drv *drv) {
+ (void)drv;
+ return MSG_OK;
+};
+
+
+msg_t
+TSL2591_setGain(TSL2591_drv *drv,
+ TSL2591_gain_t gain);
+
+msg_t
+TSL2591_setIntegrationTime(TSL2591_drv *drv,
+ TSL2591_integration_time_t time);
+
+/**
+ * @brief Read the newly acquiered measure
+ *
+ * @note According the the sensor design the measure read
+ * can be any value acquired after the acquisition time
+ * and the call to readMeasure.
+ */
+msg_t
+TSL2591_readMeasure(TSL2591_drv *drv,
+ unsigned int illuminance);
+
+
+/**
+ * @brief Read temperature and humidity
+ *
+ * @details According to the sensor specification/configuration
+ * (see #TSL2591_CONTINUOUS_ACQUISITION_SUPPORTED),
+ * if the sensor is doing continuous measurement
+ * it's value will be requested and returned immediately.
+ * Otherwise a measure is started, the necessary amount of
+ * time for acquiring the value is spend sleeping (not spinning),
+ * and finally the measure is read.
+ *
+ * @note In continuous measurement mode, if you just started
+ * the sensor, you will need to wait getAcquisitionTime()
+ * in addition to the usual getStartupTime()
+
+ * @note If using several sensors, it is better to start all the
+ * measure together, wait for the sensor having the longuest
+ * aquisition time, and finally read all the values
+ */
+msg_t
+TSL2591_readIlluminance(TSL2591_drv *drv,
+ unsigned int *illuminance);
+
+/**
+ * @brief Return the illuminance value in Lux
+ *
+ * @details Use readIlluminance() for returning the humidity value.
+ *
+ * @note Prefere readIlluminance()if you need better error handling.
+ *
+ * @return Illuminance in Lux
+ * @retval unsigned int illuminace value
+ * @retval -1 on failure
+ */
+static inline unsigned int
+TSL2591_getIlluminance(TSL2591_drv *drv) {
+ unsigned int illuminance = -1;
+ TSL2591_readIlluminance(drv, &illuminance);
+ return illuminance;
+}
+
+
+#endif
+
diff --git a/os/various/i2c_helpers.h b/os/various/i2c_helpers.h
new file mode 100644
index 0000000..4b57174
--- /dev/null
+++ b/os/various/i2c_helpers.h
@@ -0,0 +1,283 @@
+/*
+ Copyright (C) 2016 Stephane D'Alu
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#ifndef I2C_HELPERS_H
+#define I2C_HELPERS_H
+
+#include "hal.h"
+#include "bswap.h"
+
+
+typedef struct {
+ /**
+ * @brief Pointer to the I2C driver.
+ */
+ I2CDriver *driver;
+ /**
+ * @brief I2C address.
+ */
+ i2caddr_t addr;
+} I2CHelper;
+
+
+
+#if !defined(I2C_HELPERS_AUTOMATIC_DRV) || (I2C_HELPERS_AUTOMATIC_DRV == FALSE)
+
+#define i2c_send(i2c, txbuf, txbytes) \
+ _i2c_send(i2c, txbuf, txbytes)
+#define i2c_transmit(i2c, txbuf, txbytes, rxbuf, rxbytes) \
+ _i2c_transmit(i2c, txbuf, txbytes, rxbuf, rxbytes)
+#define i2c_receive(i2, rxbuf, rxbytes) \
+ _i2c_receive(i2c, rxbuf, rxbytes)
+
+#define i2c_send_timeout(i2c, txbuf, txbytes) \
+ _i2c_send(i2c, txbuf, txbytes)
+#define i2c_transmit_timeout(i2c, txbuf, txbytes, rxbuf, rxbytes) \
+ _i2c_transmit(i2c, txbuf, txbytes, rxbuf, rxbytes)
+#define i2c_receive_timeout(i2, rxbuf, rxbytes) \
+ _i2c_receive(i2c, rxbuf, rxbytes)
+
+#define i2c_reg(i2c, reg) \
+ _i2c_reg(i2c, reg)
+
+#define i2c_reg_recv8(i2c, reg, val) \
+ _i2c_reg_recv8(i2c, reg, val)
+#define i2c_reg_recv16(i2c, reg, val) \
+ _i2c_reg_recv16(i2c, reg, val)
+#define i2c_reg_recv16_le(i2c, reg, val) \
+ _i2c_reg_recv16_le(i2c, reg, val)
+#define i2c_reg_recv16_be(i2c, reg, val) \
+ _i2c_reg_recv16_be(i2c, reg, val)
+#define i2c_reg_recv32(i2c, reg, val) \
+ _i2c_reg_recv32(i2c, reg, val)
+#define i2c_reg_recv32_le(i2c, reg, val) \
+ _i2c_reg_recv32_le(i2c, reg, val)
+#define i2c_reg_recv32_be(i2c, reg, val) \
+ _i2c_reg_recv32_be(i2c, reg, val)
+
+#define i2c_recv8(i2c, val) \
+ _i2c_recv8(i2c, val)
+#define i2c_recv16(i2c, val) \
+ _i2c_recv16(i2c, val)
+#define i2c_recv16_le(i2c, val) \
+ _i2c_recv16_le(i2c, val)
+#define i2c_recv16_be(i2c, val) \
+ _i2c_recv16_be(i2c, val)
+#define i2c_recv32(i2c, val) \
+ _i2c_recv32(i2c, val)
+#define i2c_recv32_le(i2c, val) \
+ _i2c_recv32_le(i2c, val)
+#define i2c_recv32_be(i2c, val) \
+ _i2c_recv32_be(i2c, val)
+
+#else
+
+#define i2c_send(txbuf, txbytes) \
+ _i2c_send(&drv->config->i2c, txbuf, txbytes)
+#define i2c_transmit(txbuf, txbytes, rxbuf, rxbytes) \
+ _i2c_transmit(&drv->config->i2c, txbuf, txbytes, rxbuf, rxbytes)
+#define i2c_receive(rxbuf, rxbytes) \
+ _i2c_receive(&drv->config->i2c, rxbuf, rxbytes)
+
+#define i2c_send_timeout(txbuf, txbytes) \
+ _i2c_send(&drv->config->i2c, txbuf, txbytes)
+#define i2c_transmit_timeout(txbuf, txbytes, rxbuf, rxbytes) \
+ _i2c_transmit(&drv->config->i2c, txbuf, txbytes, rxbuf, rxbytes)
+#define i2c_receive_timeout(rxbuf, rxbytes) \
+ _i2c_receive(&drv->config->i2c, rxbuf, rxbytes)
+
+
+#define i2c_reg(reg) \
+ _i2c_reg(&drv->config->i2c, reg)
+
+#define i2c_reg_recv8(reg, val) \
+ _i2c_reg_recv8(&drv->config->i2c, reg, val)
+#define i2c_reg_recv16(reg, val) \
+ _i2c_reg_recv16(&drv->config->i2c, reg, val)
+#define i2c_reg_recv16_le(reg, val) \
+ _i2c_reg_recv16_le(&drv->config->i2c, reg, val)
+#define i2c_reg_recv16_be(reg, val) \
+ _i2c_reg_recv16_be(&drv->config->i2c, reg, val)
+#define i2c_reg_recv32(reg, val) \
+ _i2c_reg_recv32(&drv->config->i2c, reg, val)
+#define i2c_reg_recv32_le(reg, val) \
+ _i2c_reg_recv32_le(&drv->config->i2c, reg, val)
+#define i2c_reg_recv32_be(reg, val) \
+ _i2c_reg_recv32_be(&drv->config->i2c, reg, val)
+
+#define i2c_recv8(val) \
+ _i2c_recv8(&drv->config->i2c, val)
+#define i2c_recv16(val) \
+ _i2c_recv16(&drv->config->i2c, val)
+#define i2c_recv16_le(val) \
+ _i2c_recv16_le(&drv->config->i2c, val)
+#define i2c_recv16_be(val) \
+ _i2c_recv16_be(&drv->config->i2c, val)
+#define i2c_recv32(val) \
+ _i2c_recv32(&drv->config->i2c, val)
+#define i2c_recv32_le(val) \
+ _i2c_recv32_le(&drv->config->i2c, val)
+#define i2c_recv32_be(val) \
+ _i2c_recv32_be(&drv->config->i2c, val)
+
+#endif
+
+
+
+
+
+static inline msg_t
+_i2c_send(I2CHelper *i2c, const uint8_t *txbuf, size_t txbytes) {
+ return i2cMasterTransmitTimeout(i2c->driver, i2c->addr,
+ txbuf, txbytes, NULL, 0, TIME_INFINITE);
+};
+
+static inline msg_t
+_i2c_transmit(I2CHelper *i2c, const uint8_t *txbuf, size_t txbytes,
+ uint8_t *rxbuf, size_t rxbytes) {
+ return i2cMasterTransmitTimeout(i2c->driver, i2c->addr,
+ txbuf, txbytes, rxbuf, rxbytes, TIME_INFINITE);
+}
+
+static inline msg_t
+_i2c_receive(I2CHelper *i2c, uint8_t *rxbuf, size_t rxbytes) {
+ return i2cMasterReceiveTimeout(i2c->driver, i2c->addr,
+ rxbuf, rxbytes, TIME_INFINITE);
+};
+
+
+
+static inline msg_t
+_i2c_send_timeout(I2CHelper *i2c, const uint8_t *txbuf, size_t txbytes,
+ systime_t timeout) {
+ return i2cMasterTransmitTimeout(i2c->driver, i2c->addr,
+ txbuf, txbytes, NULL, 0, timeout);
+};
+
+static inline msg_t
+_i2c_transmit_timeout(I2CHelper *i2c, const uint8_t *txbuf, size_t txbytes,
+ uint8_t *rxbuf, size_t rxbytes, systime_t timeout) {
+ return i2cMasterTransmitTimeout(i2c->driver, i2c->addr,
+ txbuf, txbytes, rxbuf, rxbytes, timeout);
+}
+
+static inline msg_t
+_i2c_receive_timeout(I2CHelper *i2c, uint8_t *rxbuf, size_t rxbytes, systime_t timeout) {
+ return i2cMasterReceiveTimeout(i2c->driver, i2c->addr,
+ rxbuf, rxbytes, timeout);
+};
+
+
+/*======================================================================*/
+
+
+static inline msg_t
+_i2c_reg(I2CHelper *i2c, uint8_t reg) {
+ return _i2c_transmit(i2c, &reg, sizeof(reg), NULL, 0);
+};
+
+/*======================================================================*/
+
+static inline msg_t
+_i2c_reg_recv8(I2CHelper *i2c, uint8_t reg, uint8_t *val) {
+ return _i2c_transmit(i2c, &reg, sizeof(reg), (uint8_t*)val, sizeof(val));
+};
+
+static inline msg_t
+_i2c_reg_recv16(I2CHelper *i2c, uint8_t reg, uint16_t *val) {
+ return _i2c_transmit(i2c, &reg, sizeof(reg), (uint8_t*)val, sizeof(val));
+};
+
+static inline msg_t
+_i2c_reg_recv16_le(I2CHelper *i2c, uint8_t reg, uint16_t *val) {
+ int msg = _i2c_reg_recv16(i2c, reg, val);
+ if (msg >= 0) *val = le16_to_cpu(*val);
+ return msg;
+};
+
+static inline msg_t
+_i2c_reg_recv16_be(I2CHelper *i2c, uint8_t reg, uint16_t *val) {
+ int msg = _i2c_reg_recv16(i2c, reg, val);
+ if (msg >= 0) *val = be16_to_cpu(*val);
+ return msg;
+};
+
+static inline msg_t
+_i2c_reg_recv32(I2CHelper *i2c, uint8_t reg, uint32_t *val) {
+ return _i2c_transmit(i2c, &reg, sizeof(reg), (uint8_t*)val, sizeof(val));
+};
+
+static inline msg_t
+_i2c_reg_recv32_le(I2CHelper *i2c, uint8_t reg, uint32_t *val) {
+ int msg = _i2c_reg_recv32(i2c, reg, val);
+ if (msg >= 0) *val = le32_to_cpu(*val);
+ return msg;
+};
+
+static inline msg_t
+_i2c_reg_recv32_be(I2CHelper *i2c, uint8_t reg, uint32_t *val) {
+ int msg = _i2c_reg_recv32(i2c, reg, val);
+ if (msg >= 0) *val = be32_to_cpu(*val);
+ return msg;
+};
+
+
+/*======================================================================*/
+
+static inline msg_t
+_i2c_recv8(I2CHelper *i2c, uint8_t *val) {
+ return _i2c_receive(i2c, (uint8_t*)val, sizeof(val));
+};
+
+static inline msg_t
+_i2c_recv16(I2CHelper *i2c, uint16_t *val) {
+ return _i2c_receive(i2c, (uint8_t*)val, sizeof(val));
+};
+
+static inline msg_t
+_i2c_recv16_le(I2CHelper *i2c, uint16_t *val) {
+ int msg = _i2c_recv16(i2c, val);
+ if (msg >= 0) *val = le16_to_cpu(*val);
+ return msg;
+};
+
+static inline msg_t
+_i2c_recv16_be(I2CHelper *i2c, uint16_t *val) {
+ int msg = _i2c_recv16(i2c, val);
+ if (msg >= 0) *val = be16_to_cpu(*val);
+ return msg;
+};
+
+static inline msg_t
+_i2c_recv32(I2CHelper *i2c, uint32_t *val) {
+ return _i2c_receive(i2c, (uint8_t*)val, sizeof(val));
+};
+
+static inline msg_t
+_i2c_recv32_le(I2CHelper *i2c, uint32_t *val) {
+ int msg = _i2c_recv32(i2c, val);
+ if (msg >= 0) *val = le32_to_cpu(*val);
+ return msg;
+};
+
+static inline msg_t
+_i2c_recv32_be(I2CHelper *i2c, uint32_t *val) {
+ int msg = _i2c_recv32(i2c, val);
+ if (msg >= 0) *val = be32_to_cpu(*val);
+ return msg;
+};
+
+#endif