Add files via upload

This commit is contained in:
Gunar Schorcht 2017-09-14 14:30:55 +02:00 committed by GitHub
parent b83c2629b9
commit b8282537c0
4 changed files with 702 additions and 0 deletions

206
extras/sht3x/README.md Normal file
View file

@ -0,0 +1,206 @@
# Driver for SHT3x digital temperature and humity sensor
This driver is written for usage with the ESP8266 and FreeRTOS ([esp-open-rtos](https://github.com/SuperHouse/esp-open-rtos) and [esp-open-rtos-driver-i2c](https://github.com/kanflo/esp-open-rtos-driver-i2c)).
Please note: The driver supports multiple sensor connected to different I2C with differen addresses.
## About the sensor
SHT3x is a digital temperature and humity sensor that uses I2C interface as slave with up to 1 MHz communication speed. It can operate with three levels of *repeatability* (low, medium and high) in two different modes, the *single shot data aquisition mode* and the *periodic data aquisition mode*.
### Single shot data aquisition mode
In this mode one issued measurement command triggers the acquisition of one data pair. Each data pair
consists of temperature and humidity as 16 bit decimal values.
Repeatability setting influences the measurement duration as well as the current consumption of the sensor. The measuerment takes 3 ms at low repeatability, 5 ms at low repeatability, and 13.5 ms at high respectively. Average current consumption while sensor is measuring at lowest repeatability is 800 uA. That is, the higher repeatability level is, the longer the measurement takes and the higher the power consumption is. In standby the sensor consumes only 0.2 uA.
### Periodic data aquisition mode
In this mode one issued measurement command yields a stream of data pairs. Each data pair
consists of temperature and humidity as 16 bit decimal values. Once the measurement command is sent to the sensor, it executes measurements periodically by itself with a rate of 0.5, 1, 2, 4 or 10 measurements per second (mps). The data pairs can be read by a fetch command at the same rate.
As in single shot data aquisition mode, the repeatability setting influences the measurement duration as well as the current consumption of the sensor, see above.
## How the driver works
To avoid blocking of user tasks during the measurements, a seperate task running in background is created for each connectd SHT3x sensor using the function *sht3x_create_sensor*. The background task realizes the measurement procedure which is executed periodically at a rate that can be defined by the user using function *sht3x_set_measurement_period* (default period is 1000 ms).
### Measurement
Since predefined rates of *periodic data aquisition mode* of the sensor itself are normally not compatible with user requirements, the measurement procedure is realized in *single shot data aquisition mode* at the highest level of repeatability. Because of the sensor power characteristics, *single shot data aquisition mode* is more power efficient than using *periodic data aquisition mode* in most use cases.
However, using the *single shot data aquisition mode* produces a delay of 20 ms for each measurement. This is the time needed from issuing the measurement command until measured sensor data are available and can be read. Since the delay is realized using *vTaskDelay* function in the background task, neither the system nor any user task is blocked during the measurement.
Please note: Since each measurement produces a delay of 20 ms, the minimum period that can be defined with function *sht3x_set_measurement_period* is 20 ms. Therefore, a maximum measurement rate of 50 mps could be reached.
### Measured data
At each measurement, *actual sensor values* for temperature as well as humidity are determined as floating point values from measuered data pairs. Temerature is given in °C as well in °F. Humiditiy is given in percent. Based on these *actual sensor values* the background task also computes *average sensor values* successive at each measurement using the exponential moving average
Average[k] = W * Value + (1-W) * Average [k-1]
where coefficient W represents the degree of weighting decrease, a constant smoothing factor between 0 and 1. A higher W discounts older observations faster. The coefficient W (smoothing factor) can be defined by the user (default W is 0.2) using function *sht3x_set_average_weight*.
### Getting results.
To get measured values by a user task, there are two possibilities, the defining a callback function or calling function *sht3x_get_values*.
#### Using callback function
For each connected SHT3 sensor, the user can register a callback function using function *sht3x_set_callback_function*. If a callback function is registered, it is called after each measurement by the background task to pass the *actual sensor values* and *average sensor values* to user tasks. Thus, the user gets the results automatically with same rate as the periodic measurement is executed.
Using the callback function is the easiest way to get the results.
#### Using function sht3x_get_values
If there is no callback function registered, the user has to use function *sht3x_get_values* explicitly to get *actual sensor values* and *average sensor values*. To ensure that these values are up-to-date, the rate of periodic measurement in background task should be at least the same as the rate of using function *sht3x_get_values*
## Usage
Before using the sht3x driver, function *sht3x_init* needs to be called to setup each I2C interface.
```
#define I2C_BUS 0
#define I2C_SCL_PIN GPIO_ID_PIN((5))
#define I2C_SDA_PIN GPIO_ID_PIN((4))
#define SHT3x_ADDR 0x45
...
i2c_init(I2C_BUS, I2C_SCL_PIN, I2C_SDA_PIN, I2C_FREQ_100K))
...
```
Next, the SHT3x driver itself has to be initialized using function *sht3x_init*.
```
sht3x_init();
```
If SHT3x driver initialisation was successful, function *sht3x_create_sensor* has to be called for each sensor to initialize the sensor connected to a certain bus with slave address, to check its availability and to start a background task for measurments.
```
sensor = sht3x_create_sensor (I2C_BUS, SHT3x_ADDR);
```
This function returns the id of the sensor between 0 and *SHT3x_MAX_SENSORS* on success or -1 on error. This id is used to identify the sensor in all other functions used later.
If you want to use a callback function defined like following
```
static void my_callback_function (uint32_t sensor,
sht3x_value_set_t actual,
sht3x_value_set_t average)
{
...
}
```
you have to register it using *sht3x_set_callback_function*
```
sht3x_set_callback_function (sensor, &my_callback_function);
```
Optionally, you could set the measurement period using function *sht3x_set_measurement_period* or the weight (smoothing factor) for exponential moving average computation using function *sht3x_set_average_weight*.
```
sht3x_set_measurement_period (sensor, 1000);
sht3x_set_average_weight (sensor, 0.2);
```
All functions return a boolean that allows effecitve error handling.
## Full Example
```
#include "espressif/esp_common.h"
#include "esp/uart.h"
#include "FreeRTOS.h"
// include SHT3x driver
#include "sht3x/sht3x.h"
// define I2C interface at which SHTx3 sensor is connected
#define I2C_BUS 0
#define I2C_SCL_PIN GPIO_ID_PIN((5))
#define I2C_SDA_PIN GPIO_ID_PIN((4))
#define SHT3x_ADDR 0x45
static uint32_t sensor;
static void my_callback_function (uint32_t sensor,
sht3x_value_set_t actual,
sht3x_value_set_t average)
{
printf("%.3f Sensor %d: %.2f (%.2f) C, %.2f (%.2f) F, %.2f (%.2f) \n",
(double)sdk_system_get_time()*1e-3, sensor,
actual.c_temperature, average.c_temperature,
actual.f_temperature, average.f_temperature,
actual.humidity, average.humidity);
}
void user_init(void)
{
// Set UART Parameter
uart_set_baud(0, 115200);
// Give the UART some time to settle
sdk_os_delay_us(500);
// Init I2C bus interface at which SHT3x sensor is connected
i2c_init(I2C_BUS, I2C_SCL_PIN, I2C_SDA_PIN, I2C_FREQ_100K);
// Init SHT3x driver
if (!sht3x_init())
{
// error message
return;
}
// Create sensors
if ((sensor = sht3x_create_sensor (I2C_BUS, SHT3x_ADDR)) < 0)
{
// error message
return;
}
// Set the callback function
if (!sht3x_set_callback_function (sensor, &my_callback_function))
{
// error message
return;
}
/*
// Optional: Set the period of measurements (default 1000 ms)
if (!sht3x_set_measurement_period (sensor, 1000))
{
// error message
return;
}
// Optional: Set the weight for expontial moving average (default 0.2)
if (!sht3x_set_average_weight (sensor, 0.2))
{
// error message
return;
}
*/
// That's it.
}
```
## Further Examples
For further examples see [Examples directory](../../examples/sht3x/README.md)

View file

@ -0,0 +1,9 @@
# Component makefile for extras/sht3x
# expected anyone using bmp driver includes it as 'sht3x/sht3x.h'
INC_DIRS += $(sht3x_ROOT)..
# args for passing into compile rule generation
sht3x_SRC_DIR = $(sht3x_ROOT)
$(eval $(call component_compile_rules,sht3x))

308
extras/sht3x/sht3x.c Normal file
View file

@ -0,0 +1,308 @@
#include <string.h>
#include "sht3x.h"
#include "FreeRTOS.h"
#include "queue.h"
#include "task.h"
#include "espressif/esp_common.h"
#include "espressif/sdk_private.h"
#define SHT3x_CB_TASK_PRIORITY 9
#define SHT3x_STATUS_CMD 0xF32D
#define SHT3x_CLEAR_STATUS_CMD 0x3041
#define SHT3x_RESET_CMD 0x30A2
#define SHT3x_MEASURE_ONE_SHOT_CMD 0x2C06
#define SHT3x_MEASURE_PERIODIC_1_CMD 0x2130
#define SHT3x_MEASURE_PERIODIC_2_CMD 0x2236
#define SHT3x_MEASURE_PERIODIC_4_CMD 0x2434
#define SHT3x_MEASURE_PERIODIC_10_CMD 0x2737
#define SHT3x_FETCH_DATA_CMD 0xE000
#ifdef SHT3x_DEBUG
#define DEBUG_PRINTF0(s) printf(s);
#define DEBUG_PRINTF1(s,a) printf(s,a);
#define DEBUG_PRINTF2(s,a,b) printf(s,a,b);
#define DEBUG_PRINTF3(s,a,b,c) printf(s,a,b,c);
#else
#define DEBUG_PRINTF0(s)
#define DEBUG_PRINTF1(s,a)
#define DEBUG_PRINTF2(s,a,b)
#define DEBUG_PRINTF3(s,a,b,c)
#endif
sht3x_sensor_t sht3x_sensors[SHT3x_MAX_SENSORS];
static bool sht3x_send_command(uint32_t id, uint16_t cmd)
{
uint8_t data[2] = { cmd / 256, cmd % 256 };
DEBUG_PRINTF2("%s: Send MSB command byte %0x\n", __FUNCTION__, data[0]);
DEBUG_PRINTF2("%s: Send LSB command byte %0x\n", __FUNCTION__, data[1]);
taskENTER_CRITICAL();
int error = i2c_slave_write(sht3x_sensors[id].bus, sht3x_sensors[id].addr, 0, data, 2);
taskEXIT_CRITICAL();
if (error)
{
printf("%s: Error %d on write command %0x to i2c slave on bus %d with addr %0x.\n",
__FUNCTION__, error, cmd, sht3x_sensors[id].bus, sht3x_sensors[id].addr);
return false;
}
return true;
}
static bool sht3x_read_data(uint32_t id, uint8_t *data, uint32_t len)
{
taskENTER_CRITICAL();
int error = i2c_slave_read(sht3x_sensors[id].bus, sht3x_sensors[id].addr, 0, data, len);
taskEXIT_CRITICAL();
if (error)
{
printf("%s: Error %d on read %d byte from i2c slave on bus %d with addr %0x.\n",
__FUNCTION__, error, len, sht3x_sensors[id].bus, sht3x_sensors[id].addr);
return false;
}
# ifdef SHT3x_DEBUG
printf("%s: Read following bytes: ", __FUNCTION__);
for (int i=0; i < len; i++)
printf("%0x ", data[i]);
printf("\n");
# endif
return true;
}
static bool sht3x_is_available (uint32_t sensor)
{
uint8_t data[3];
if (!sht3x_send_command(sensor, SHT3x_STATUS_CMD))
return false;
if (!sht3x_read_data(sensor, data, 3))
return false;
return true;
}
static bool sht3x_valid_sensor (uint32_t sensor, const char* function)
{
if (sensor < 0 || sensor > SHT3x_MAX_SENSORS)
{
DEBUG_PRINTF2("%s: Wrong sensor id %d.\n", function, sensor);
return false;
}
if (!sht3x_sensors[sensor].active)
{
DEBUG_PRINTF2("%s: Sensor with id %d is not active.\n", function, sensor);
return false;
}
return true;
}
static void sht3x_compute_values (uint8_t id, uint8_t* data)
{
sht3x_value_set_t act;
sht3x_value_set_t avg = sht3x_sensors[id].average;
float w = sht3x_sensors[id].average_weight;
act.c_temperature = ((((data[0] * 256.0) + data[1]) * 175) / 65535.0) - 45;
act.f_temperature = ((((data[0] * 256.0) + data[1]) * 347) / 65535.0) - 49;
act.humidity = ((((data[3] * 256.0) + data[4]) * 100) / 65535.0);
if (sht3x_sensors[id].first_measurement)
{
sht3x_sensors[id].first_measurement = false;
avg = act;
}
else
{
avg.c_temperature = w * act.c_temperature + (1-w) * avg.c_temperature;
avg.f_temperature = w * act.f_temperature + (1-w) * avg.f_temperature;
avg.humidity = w * act.humidity + (1-w) * avg.humidity;
}
sht3x_sensors[id].actual = act;
sht3x_sensors[id].average = avg;
}
static void sht3x_callback_task (void *pvParameters)
{
uint8_t data[6];
uint32_t sensor = (uint32_t)pvParameters;
while (1)
{
// DEBUG_PRINTF3("%.3f Sensor %d: %s\n", (double)sdk_system_get_time()*1e-3, sensor, __FUNCTION__);
if (sht3x_send_command(sensor, SHT3x_MEASURE_ONE_SHOT_CMD))
{
vTaskDelay (20 / portTICK_PERIOD_MS);
if (sht3x_read_data(sensor, data, 6))
{
sht3x_compute_values(sensor, data);
// DEBUG_PRINTF3("%.2f C, %.2f F, %.2f \n",
// sht3x_sensors[sensor].actual.c_temperature,
// sht3x_sensors[sensor].actual.f_temperature,
// sht3x_sensors[sensor].actual.humidity);
if (sht3x_sensors[sensor].cb_function)
sht3x_sensors[sensor].cb_function(sensor,
sht3x_sensors[sensor].actual,
sht3x_sensors[sensor].average);
}
vTaskDelay((sht3x_sensors[sensor].period-20) / portTICK_PERIOD_MS);
}
else
vTaskDelay(sht3x_sensors[sensor].period / portTICK_PERIOD_MS);
}
}
bool sht3x_init()
{
for (int id=0; id < SHT3x_MAX_SENSORS; id++)
sht3x_sensors[id].active = false;
return true;
}
uint32_t sht3x_create_sensor(uint8_t bus, uint8_t addr)
{
static uint32_t id;
static char cb_task_name[20];
// search for first free sensor data structure
for (id=0; id < SHT3x_MAX_SENSORS; id++)
{
DEBUG_PRINTF3("%s: id=%d active=%d\n", __FUNCTION__, id, sht3x_sensors[id].active);
if (!sht3x_sensors[id].active)
break;
}
DEBUG_PRINTF2("%s: id=%d\n", __FUNCTION__, id);
if (id == SHT3x_MAX_SENSORS)
{
DEBUG_PRINTF1("%s: No more sensor data structures available.\n", __FUNCTION__);
return -1;
}
// init sensor data structure
sht3x_sensors[id].bus = bus;
sht3x_sensors[id].addr = addr;
sht3x_sensors[id].period = 1000;
sht3x_sensors[id].first_measurement = true;
sht3x_sensors[id].average_weight = 0.2;
sht3x_sensors[id].cb_function = NULL;
sht3x_sensors[id].cb_task = NULL;
// check whether sensor is available
if (!sht3x_is_available(id))
return -1;
// clear sensor status register
if (!sht3x_send_command(id, SHT3x_CLEAR_STATUS_CMD))
return -1;
snprintf (cb_task_name, 20, "sht3x_cb_task_%d", id);
if (xTaskCreate (sht3x_callback_task, cb_task_name, 256, (void*)id,
SHT3x_CB_TASK_PRIORITY,
&sht3x_sensors[id].cb_task) != pdPASS)
{
vTaskDelete(sht3x_sensors[id].cb_task);
printf("%s: Could not create task %s\n", __FUNCTION__, cb_task_name);
return false;
}
sht3x_sensors[id].active = true;
return id;
}
bool sht3x_delete_sensor(uint32_t sensor)
{
if (!sht3x_valid_sensor(sensor, __FUNCTION__))
return false;
if (sht3x_sensors[sensor].cb_task)
vTaskDelete(sht3x_sensors[sensor].cb_task);
if (memset(&sht3x_sensors[sensor], 1, sizeof(sht3x_sensor_t)) == NULL)
{
DEBUG_PRINTF2("%s: Could not initialize memory for sensor with id %d.\n", __FUNCTION__, sensor);
return false;
}
return true;
}
bool sht3x_set_measurement_period (uint32_t sensor, uint32_t period)
{
if (!sht3x_valid_sensor(sensor, __FUNCTION__))
return false;
if (period < 20)
DEBUG_PRINTF3("%s: Period of %d ms is less than the minimum period of 20 ms for sensor with id %d.\n", __FUNCTION__, period, sensor);
sht3x_sensors[sensor].period = period;
return true;
}
bool sht3x_set_callback_function (uint32_t sensor, sht3x_cb_function_t user_function)
{
if (!sht3x_valid_sensor(sensor, __FUNCTION__))
return false;
sht3x_sensors[sensor].cb_function = user_function;
DEBUG_PRINTF1("%s: Set callback mode done.\n", __FUNCTION__);
return false;
}
bool sht3x_get_values(uint32_t sensor, sht3x_value_set_t *actual, sht3x_value_set_t *average)
{
if (!sht3x_valid_sensor(sensor, __FUNCTION__))
return false;
*actual = sht3x_sensors[sensor].actual;
*average = sht3x_sensors[sensor].average;
return true;
}
bool sht3x_set_average_weight (uint32_t sensor, float weight)
{
sht3x_sensors[sensor].first_measurement = true;
sht3x_sensors[sensor].average_weight = weight;
return true;
}

179
extras/sht3x/sht3x.h Normal file
View file

@ -0,0 +1,179 @@
/*
* Driver for Sensirion SHT3x digital temperature and humity sensor
* connected to I2C
*
* Part of esp-open-rtos
* Copyright (C) 2017 Gunar Schorcht (https://github.com/gschorcht)
* BSD Licensed as described in the file LICENSE
*/
#ifndef DRIVER_SHT3x_H_
#define DRIVER_SHT3x_H_
#include "stdint.h"
#include "stdbool.h"
#include "FreeRTOS.h"
#include "queue.h"
#include "i2c/i2c.h"
// Uncomment to enable debug output
#define SHT3x_DEBUG
// Change this if you need more than 3 SHT3x sensors
#define SHT3x_MAX_SENSORS 3
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
float c_temperature;
float f_temperature;
float humidity;
} sht3x_value_set_t;
typedef void (*sht3x_cb_function_t)(uint32_t sensor,
sht3x_value_set_t actual,
sht3x_value_set_t average);
// SHT3x sensor data structure
typedef struct {
bool active;
bool first_measurement;
uint8_t bus;
uint8_t addr;
uint32_t period;
sht3x_value_set_t actual;
sht3x_value_set_t average;
float average_weight;
sht3x_cb_function_t cb_function;
TaskHandle_t cb_task;
} sht3x_sensor_t;
/**
* Initialize the SHT3x driver. This function must be called only once at the
* beginning.
*
* @return true on success, false on error
*/
bool sht3x_init ();
/**
* Initialize the SHT3x sensor connected to a certain bus with slave
* address, check its availability and start a background task for
* measurments.
*
* The background task carries out measurements periodically using SHT3x's
* single shot data aquisition mode with a default period of 1000 ms.
* This period be changed using function @set_measurment_period.
*
* At each measurement, actual sensor values are determined and exponential
* moving average values are computed. The weight (smoothing factor) for this
* average computation is 0.2 by default. It can be changed using function
* @sht3x_set_average_weight.
*
* If a callback method is registered using function
* @sht3x_set_callback_function, it is called after each measurement to pass
* measurement results to user tasks. Otherwise, user tasks have to use
* function @sht3x_get_values explicitly to get the results.
*
* @param bus I2C bus at which SHT3x sensor is connected
* @param addr I2C addr of the SHT3x sensor
*
* @return id of the sensor (0 or greater on success or -1 on error)
*/
uint32_t sht3x_create_sensor (uint8_t bus, uint8_t addr);
/**
* Set the period of the background measurement task for a certain sensor.
*
* Please note the minimum period is 20 ms since the measurement takes
* about 20 ms.
*
* @param sensor id of the sensor
* @param period Measurement period in ms (default 1000 ms)
*
* @return true on success, false on error
*/
bool sht3x_set_measurement_period (uint32_t sensor, uint32_t period);
/**
* Set the callback function for the background measurement task for a certain
* sensor.
*
* If a callback method is registered, it is called after each measurement to
* pass measurement results to user tasks. Thus, callback function is executed
* at the same rate as measurements.
*
* @param sensor id of the sensor
* @param function user function called after each measurement
* (NULL to delete it for the sensor)
*
* @return true on success, false on error
*/
bool sht3x_set_callback_function (uint32_t sensor,
sht3x_cb_function_t user_function);
/**
* Deletes the SHT3x sensor given by its id.
*
* @param sensor id of the sensor
*
* @return true on success, false on error
*/
bool sht3x_delete_sensor (uint32_t sensor);
/**
* Returns actual and average sensor values of last measurement.
*
* This function is only needed, if there is no callback function registered
* for the sensor.
*
* @param sensor id of the sensor
* @param actual pointer to a data structure for actual sensor values
* @param average pointer to a data structure for average sensor values
*
* @return true on success, false on error
*/
bool sht3x_get_values (uint32_t sensor,
sht3x_value_set_t *actual,
sht3x_value_set_t *average);
/**
* At each measurement carried out by the background task, actual
* sensor values are determined and exponential moving avarage values are
* computed accoroding to following equation
*
* Average[k] = W * Value + (1-W) * Average [k-1]
*
* where coefficient W represents the degree of weighting decrease, a
* constant smoothing factor between 0 and 1. A higher W discounts older
* observations faster.
*
* @param sensor id of the sensor
* @param weight coefficient W (default is 0.2)
*
* @return true on success, false on error
*/
bool sht3x_set_average_weight (uint32_t sensor, float weight);
#ifdef __cplusplus
}
#endif
#endif /* DRIVER_SHT3x_H_ */