initial
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
set(COMPONENT_ADD_INCLUDEDIRS .)
|
||||
|
||||
set(COMPONENT_SRCS "bmp280.c")
|
||||
|
||||
#set(COMPONENT_REQUIRES console spi_flash)
|
||||
|
||||
register_component()
|
||||
@@ -0,0 +1,108 @@
|
||||
# Driver for BMP280 and BME280 absolute barometric pressure sensors
|
||||
|
||||
The driver works only with BMP280 and BME280 sensors. For BMP080/BMP180 there's
|
||||
a separate driver. Even though BMP280 is a successor of BMP180 they are not
|
||||
compatible. They have different registers and different operation modes.
|
||||
BMP280 supports two ways of communication: spi and i2c. This driver provides
|
||||
only i2c communication.
|
||||
The driver is written for [esp-open-rtos](https://github.com/SuperHouse/esp-open-rtos)
|
||||
framework and requires [i2c driver](https://github.com/SuperHouse/esp-open-rtos/tree/master/extras/i2c)
|
||||
from it.
|
||||
|
||||
## Features
|
||||
|
||||
* I2C communication.
|
||||
* Forced mode (Similar to BMP180 operation).
|
||||
* Normal mode. Continuous measurement.
|
||||
* Soft reset.
|
||||
|
||||
## Usage
|
||||
|
||||
Connect BMP280 or BME280 module to you ESP8266 module and initialize the I2C SCL and SDA pins:
|
||||
|
||||
```
|
||||
const uint8_t bus = 0;
|
||||
const uint8_t scl_pin = 0;
|
||||
const uint8_t sda_pin = 2;
|
||||
i2c_init(bus, scl_pin, sda_pin, I2C_FREQ_100K);
|
||||
|
||||
```
|
||||
|
||||
Pull up SDO pin of BMP280 in order to have address 0x77 `BMP280_I2C_ADDRESS_1`.
|
||||
Or pull down SDO pin for address 0x76 `BMP280_I2C_ADDRESS_0`. Otherwise your
|
||||
sensor will not work.
|
||||
|
||||
The BMP280 or BME280 are auto-detected at initialization based on the chip ID
|
||||
and this ID is stored in the device descriptor.
|
||||
|
||||
BMP280 supports two operation modes.
|
||||
|
||||
### Forced mode
|
||||
|
||||
In forced mode, a single measurement is performed according to selected
|
||||
configuration. When the measurement is finished, the sensor returns to
|
||||
sleep mode and the measurement results can be read.
|
||||
|
||||
### Normal mode
|
||||
|
||||
Normal mode continuously cycles between measurement period and standby period,
|
||||
whose time is defined by standby_time.
|
||||
|
||||
## Example
|
||||
|
||||
### Forced mode
|
||||
|
||||
```
|
||||
bmp280_params_t params;
|
||||
float pressure, temperature, humidity;
|
||||
|
||||
bmp280_init_default_params(¶ms);
|
||||
params.mode = BMP280_MODE_FORCED;
|
||||
|
||||
bmp280_t bmp280_dev;
|
||||
bmp280_dev.i2c_addr = BMP280_I2C_ADDRESS_0;
|
||||
bmp280_init(&bmp280_dev, ¶ms);
|
||||
bool bme280p = bmp280_dev.id == BME280_CHIP_ID;
|
||||
|
||||
while(1) {
|
||||
bmp280_force_measurement(&bmp280_dev));
|
||||
// wait for measurement to complete
|
||||
while (bmp280_is_measuring(&bmp280_dev)) {};
|
||||
|
||||
bmp280_read_float(&bmp280_dev, &temperature, &pressure, &humidity);
|
||||
printf("Pressure: %.2f Pa, Temperature: %.2f C", pressure, temperature);
|
||||
if (bme280p)
|
||||
printf(", Humidity: %.2f\n", humidity);
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
```
|
||||
|
||||
### Normal mode
|
||||
|
||||
```
|
||||
bmp280_params_t params;
|
||||
float pressure, temperature, humidity;
|
||||
|
||||
bmp280_init_default_params(¶ms);
|
||||
|
||||
bmp280_t bmp280_dev;
|
||||
bmp280_dev.i2c_addr = BMP280_I2C_ADDRESS_0;
|
||||
bmp280_init(&bmp280_dev, ¶ms);
|
||||
bool bme280p = bmp280_dev.id == BME280_CHIP_ID;
|
||||
|
||||
while(1) {
|
||||
bmp280_read_float(&bmp280_dev, &temperature, &pressure, &humidity);
|
||||
printf("Pressure: %.2f Pa, Temperature: %.2f C", pressure, temperature);
|
||||
if (bme280p)
|
||||
printf(", Humidity: %.2f\n", humidity);
|
||||
else
|
||||
printf("\n");
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
The driver is released under MIT license.
|
||||
|
||||
Copyright (c) 2016 sheinz (https://github.com/sheinz)
|
||||
@@ -0,0 +1,383 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2016 sheinz (https://github.com/sheinz)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include "bmp280.h"
|
||||
|
||||
#ifdef BMP280_DEBUG
|
||||
#include <stdio.h>
|
||||
#define debug(fmt, ...) printf("%s" fmt "\n", "bmp280: ", ## __VA_ARGS__);
|
||||
#else
|
||||
#define debug(fmt, ...)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* BMP280 registers
|
||||
*/
|
||||
#define BMP280_REG_TEMP_XLSB 0xFC /* bits: 7-4 */
|
||||
#define BMP280_REG_TEMP_LSB 0xFB
|
||||
#define BMP280_REG_TEMP_MSB 0xFA
|
||||
#define BMP280_REG_TEMP (BMP280_REG_TEMP_MSB)
|
||||
#define BMP280_REG_PRESS_XLSB 0xF9 /* bits: 7-4 */
|
||||
#define BMP280_REG_PRESS_LSB 0xF8
|
||||
#define BMP280_REG_PRESS_MSB 0xF7
|
||||
#define BMP280_REG_PRESSURE (BMP280_REG_PRESS_MSB)
|
||||
#define BMP280_REG_CONFIG 0xF5 /* bits: 7-5 t_sb; 4-2 filter; 0 spi3w_en */
|
||||
#define BMP280_REG_CTRL 0xF4 /* bits: 7-5 osrs_t; 4-2 osrs_p; 1-0 mode */
|
||||
#define BMP280_REG_STATUS 0xF3 /* bits: 3 measuring; 0 im_update */
|
||||
#define BMP280_REG_CTRL_HUM 0xF2 /* bits: 2-0 osrs_h; */
|
||||
#define BMP280_REG_RESET 0xE0
|
||||
#define BMP280_REG_ID 0xD0
|
||||
#define BMP280_REG_CALIB 0x88
|
||||
#define BMP280_REG_HUM_CALIB 0x88
|
||||
|
||||
|
||||
#define BMP280_RESET_VALUE 0xB6
|
||||
|
||||
|
||||
void bmp280_init_default_params(bmp280_params_t *params)
|
||||
{
|
||||
params->mode = BMP280_MODE_NORMAL;
|
||||
params->filter = BMP280_FILTER_OFF;
|
||||
params->oversampling_pressure = BMP280_STANDARD;
|
||||
params->oversampling_temperature = BMP280_STANDARD;
|
||||
params->oversampling_humidity = BMP280_STANDARD;
|
||||
params->standby = BMP280_STANDBY_250;
|
||||
}
|
||||
|
||||
static bool read_register16(i2c_dev_t *dev, uint8_t addr, uint16_t *value)
|
||||
{
|
||||
uint8_t d[] = {0, 0};
|
||||
if (!i2c_slave_read(dev->bus, dev->addr, &addr, d, sizeof(d))) {
|
||||
*value = d[0] | (d[1] << 8);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline int read_data(i2c_dev_t *dev, uint8_t addr, uint8_t *value, uint8_t len)
|
||||
{
|
||||
return i2c_slave_read(dev->bus, dev->addr, &addr, value, len);
|
||||
}
|
||||
|
||||
static bool read_calibration_data(bmp280_t *dev)
|
||||
{
|
||||
|
||||
if (read_register16(&dev->i2c_dev, 0x88, &dev->dig_T1) &&
|
||||
read_register16(&dev->i2c_dev, 0x8a, (uint16_t *)&dev->dig_T2) &&
|
||||
read_register16(&dev->i2c_dev, 0x8c, (uint16_t *)&dev->dig_T3) &&
|
||||
read_register16(&dev->i2c_dev, 0x8e, &dev->dig_P1) &&
|
||||
read_register16(&dev->i2c_dev, 0x90, (uint16_t *)&dev->dig_P2) &&
|
||||
read_register16(&dev->i2c_dev, 0x92, (uint16_t *)&dev->dig_P3) &&
|
||||
read_register16(&dev->i2c_dev, 0x94, (uint16_t *)&dev->dig_P4) &&
|
||||
read_register16(&dev->i2c_dev, 0x96, (uint16_t *)&dev->dig_P5) &&
|
||||
read_register16(&dev->i2c_dev, 0x98, (uint16_t *)&dev->dig_P6) &&
|
||||
read_register16(&dev->i2c_dev, 0x9a, (uint16_t *)&dev->dig_P7) &&
|
||||
read_register16(&dev->i2c_dev, 0x9c, (uint16_t *)&dev->dig_P8) &&
|
||||
read_register16(&dev->i2c_dev, 0x9e, (uint16_t *)&dev->dig_P9)) {
|
||||
|
||||
debug("Calibration data received:");
|
||||
debug("dig_T1=%d", dev->dig_T1);
|
||||
debug("dig_T2=%d", dev->dig_T2);
|
||||
debug("dig_T3=%d", dev->dig_T3);
|
||||
debug("dig_P1=%d", dev->dig_P1);
|
||||
debug("dig_P2=%d", dev->dig_P2);
|
||||
debug("dig_P3=%d", dev->dig_P3);
|
||||
debug("dig_P4=%d", dev->dig_P4);
|
||||
debug("dig_P5=%d", dev->dig_P5);
|
||||
debug("dig_P6=%d", dev->dig_P6);
|
||||
debug("dig_P7=%d", dev->dig_P7);
|
||||
debug("dig_P8=%d", dev->dig_P8);
|
||||
debug("dig_P9=%d", dev->dig_P9);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool read_hum_calibration_data(bmp280_t *dev)
|
||||
{
|
||||
uint16_t h4, h5;
|
||||
|
||||
if (!read_data(&dev->i2c_dev, 0xa1, &dev->dig_H1, 1) &&
|
||||
read_register16(&dev->i2c_dev, 0xe1, (uint16_t *)&dev->dig_H2) &&
|
||||
!read_data(&dev->i2c_dev, 0xe3, &dev->dig_H3, 1) &&
|
||||
read_register16(&dev->i2c_dev, 0xe4, &h4) &&
|
||||
read_register16(&dev->i2c_dev, 0xe5, &h5) &&
|
||||
!read_data(&dev->i2c_dev, 0xe7, (uint8_t *)&dev->dig_H6, 1)) {
|
||||
dev->dig_H4 = (h4 & 0x00ff) << 4 | (h4 & 0x0f00) >> 8;
|
||||
dev->dig_H5 = h5 >> 4;
|
||||
debug("Calibration data received:");
|
||||
debug("dig_H1=%d", dev->dig_H1);
|
||||
debug("dig_H2=%d", dev->dig_H2);
|
||||
debug("dig_H3=%d", dev->dig_H3);
|
||||
debug("dig_H4=%d", dev->dig_H4);
|
||||
debug("dig_H5=%d", dev->dig_H5);
|
||||
debug("dig_H6=%d", dev->dig_H6);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int write_register8(i2c_dev_t *dev, uint8_t addr, uint8_t value)
|
||||
{
|
||||
return i2c_slave_write(dev->bus, dev->addr, &addr, &value, 1);
|
||||
}
|
||||
|
||||
bool bmp280_init(bmp280_t *dev, bmp280_params_t *params)
|
||||
{
|
||||
|
||||
if (dev->i2c_dev.addr != BMP280_I2C_ADDRESS_0 && dev->i2c_dev.addr != BMP280_I2C_ADDRESS_1) {
|
||||
debug("Invalid I2C address");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (read_data(&dev->i2c_dev, BMP280_REG_ID, &dev->id, 1)) {
|
||||
debug("Sensor not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dev->id != BMP280_CHIP_ID && dev->id != BME280_CHIP_ID) {
|
||||
debug("Sensor wrong version");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Soft reset.
|
||||
if (write_register8(&dev->i2c_dev, BMP280_REG_RESET, BMP280_RESET_VALUE)) {
|
||||
debug("Failed resetting sensor");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait until finished copying over the NVP data.
|
||||
while (1) {
|
||||
uint8_t status;
|
||||
if (!read_data(&dev->i2c_dev, BMP280_REG_STATUS, &status, 1) && (status & 1) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!read_calibration_data(dev)) {
|
||||
debug("Failed to read calibration data");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dev->id == BME280_CHIP_ID && !read_hum_calibration_data(dev)) {
|
||||
debug("Failed to read humidity calibration data");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t config = (params->standby << 5) | (params->filter << 2);
|
||||
debug("Writing config reg=%x", config);
|
||||
if (write_register8(&dev->i2c_dev, BMP280_REG_CONFIG, config)) {
|
||||
debug("Failed configuring sensor");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (params->mode == BMP280_MODE_FORCED) {
|
||||
params->mode = BMP280_MODE_SLEEP; // initial mode for forced is sleep
|
||||
}
|
||||
|
||||
uint8_t ctrl = (params->oversampling_temperature << 5) | (params->oversampling_pressure << 2)
|
||||
| (params->mode);
|
||||
|
||||
|
||||
if (dev->id == BME280_CHIP_ID) {
|
||||
// Write crtl hum reg first, only active after write to BMP280_REG_CTRL.
|
||||
uint8_t ctrl_hum = params->oversampling_humidity;
|
||||
debug("Writing ctrl hum reg=%x", ctrl_hum);
|
||||
if (write_register8(&dev->i2c_dev, BMP280_REG_CTRL_HUM, ctrl_hum)) {
|
||||
debug("Failed controlling sensor");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
debug("Writing ctrl reg=%x", ctrl);
|
||||
if (write_register8(&dev->i2c_dev, BMP280_REG_CTRL, ctrl)) {
|
||||
debug("Failed controlling sensor");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bmp280_force_measurement(bmp280_t *dev)
|
||||
{
|
||||
uint8_t ctrl;
|
||||
if (read_data(&dev->i2c_dev, BMP280_REG_CTRL, &ctrl, 1))
|
||||
return false;
|
||||
ctrl &= ~0b11; // clear two lower bits
|
||||
ctrl |= BMP280_MODE_FORCED;
|
||||
debug("Writing ctrl reg=%x", ctrl);
|
||||
if (write_register8(&dev->i2c_dev, BMP280_REG_CTRL, ctrl)) {
|
||||
debug("Failed starting forced mode");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bmp280_is_measuring(bmp280_t *dev)
|
||||
{
|
||||
uint8_t status;
|
||||
if (read_data(&dev->i2c_dev, BMP280_REG_STATUS, &status, 1))
|
||||
return false;
|
||||
if (status & (1 << 3)) {
|
||||
debug("Status: measuring");
|
||||
return true;
|
||||
}
|
||||
debug("Status: idle");
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compensation algorithm is taken from BMP280 datasheet.
|
||||
*
|
||||
* Return value is in degrees Celsius.
|
||||
*/
|
||||
static inline int32_t compensate_temperature(bmp280_t *dev,
|
||||
int32_t adc_temp, int32_t *fine_temp)
|
||||
{
|
||||
int32_t var1, var2;
|
||||
|
||||
var1 = ((((adc_temp >> 3) - ((int32_t)dev->dig_T1 << 1)))
|
||||
* (int32_t)dev->dig_T2) >> 11;
|
||||
var2 = (((((adc_temp >> 4) - (int32_t)dev->dig_T1)
|
||||
* ((adc_temp >> 4) - (int32_t)dev->dig_T1)) >> 12)
|
||||
* (int32_t)dev->dig_T3) >> 14;
|
||||
|
||||
*fine_temp = var1 + var2;
|
||||
return (*fine_temp * 5 + 128) >> 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compensation algorithm is taken from BMP280 datasheet.
|
||||
*
|
||||
* Return value is in Pa, 24 integer bits and 8 fractional bits.
|
||||
*/
|
||||
static inline uint32_t compensate_pressure(bmp280_t *dev,
|
||||
int32_t adc_press, int32_t fine_temp)
|
||||
{
|
||||
int64_t var1, var2, p;
|
||||
|
||||
var1 = (int64_t)fine_temp - 128000;
|
||||
var2 = var1 * var1 * (int64_t)dev->dig_P6;
|
||||
var2 = var2 + ((var1 * (int64_t)dev->dig_P5) << 17);
|
||||
var2 = var2 + (((int64_t)dev->dig_P4) << 35);
|
||||
var1 = ((var1 * var1 * (int64_t)dev->dig_P3) >> 8) +
|
||||
((var1 * (int64_t)dev->dig_P2) << 12);
|
||||
var1 = (((int64_t)1 << 47) + var1) * ((int64_t)dev->dig_P1) >> 33;
|
||||
|
||||
if (var1 == 0) {
|
||||
return 0; // avoid exception caused by division by zero
|
||||
}
|
||||
|
||||
p = 1048576 - adc_press;
|
||||
p = (((p << 31) - var2) * 3125) / var1;
|
||||
var1 = ((int64_t)dev->dig_P9 * (p >> 13) * (p >> 13)) >> 25;
|
||||
var2 = ((int64_t)dev->dig_P8 * p) >> 19;
|
||||
|
||||
p = ((p + var1 + var2) >> 8) + ((int64_t)dev->dig_P7 << 4);
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compensation algorithm is taken from BME280 datasheet.
|
||||
*
|
||||
* Return value is in Pa, 24 integer bits and 8 fractional bits.
|
||||
*/
|
||||
static inline uint32_t compensate_humidity(bmp280_t *dev,
|
||||
int32_t adc_hum, int32_t fine_temp)
|
||||
{
|
||||
int32_t v_x1_u32r;
|
||||
|
||||
v_x1_u32r = fine_temp - (int32_t)76800;
|
||||
v_x1_u32r = ((((adc_hum << 14) - ((int32_t)dev->dig_H4 << 20) -
|
||||
((int32_t)dev->dig_H5 * v_x1_u32r)) +
|
||||
(int32_t)16384) >> 15) *
|
||||
(((((((v_x1_u32r * (int32_t)dev->dig_H6) >> 10) *
|
||||
(((v_x1_u32r * (int32_t)dev->dig_H3) >> 11) +
|
||||
(int32_t)32768)) >> 10) + (int32_t)2097152) *
|
||||
(int32_t)dev->dig_H2 + 8192) >> 14);
|
||||
v_x1_u32r = v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) *
|
||||
(int32_t)dev->dig_H1) >> 4);
|
||||
v_x1_u32r = v_x1_u32r < 0 ? 0 : v_x1_u32r;
|
||||
v_x1_u32r = v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r;
|
||||
return v_x1_u32r >> 12;
|
||||
}
|
||||
|
||||
bool bmp280_read_fixed(bmp280_t *dev, int32_t *temperature,
|
||||
uint32_t *pressure, uint32_t *humidity)
|
||||
{
|
||||
int32_t adc_pressure;
|
||||
int32_t adc_temp;
|
||||
uint8_t data[8];
|
||||
|
||||
// Only the BME280 supports reading the humidity.
|
||||
if (dev->id != BME280_CHIP_ID) {
|
||||
if (humidity)
|
||||
*humidity = 0;
|
||||
humidity = NULL;
|
||||
}
|
||||
|
||||
// Need to read in one sequence to ensure they match.
|
||||
size_t size = humidity ? 8 : 6;
|
||||
if (read_data(&dev->i2c_dev, 0xf7, data, size)) {
|
||||
debug("Failed reading");
|
||||
return false;
|
||||
}
|
||||
|
||||
adc_pressure = data[0] << 12 | data[1] << 4 | data[2] >> 4;
|
||||
adc_temp = data[3] << 12 | data[4] << 4 | data[5] >> 4;
|
||||
debug("ADC temperature: %d", adc_temp);
|
||||
debug("ADC pressure: %d", adc_pressure);
|
||||
|
||||
int32_t fine_temp;
|
||||
*temperature = compensate_temperature(dev, adc_temp, &fine_temp);
|
||||
*pressure = compensate_pressure(dev, adc_pressure, fine_temp);
|
||||
|
||||
if (humidity) {
|
||||
int32_t adc_humidity = data[6] << 8 | data[7];
|
||||
debug("ADC humidity: %d", adc_humidity);
|
||||
*humidity = compensate_humidity(dev, adc_humidity, fine_temp);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bmp280_read_float(bmp280_t *dev, float *temperature,
|
||||
float *pressure, float *humidity)
|
||||
{
|
||||
int32_t fixed_temperature;
|
||||
uint32_t fixed_pressure;
|
||||
uint32_t fixed_humidity;
|
||||
if (bmp280_read_fixed(dev, &fixed_temperature, &fixed_pressure,
|
||||
humidity ? &fixed_humidity : NULL)) {
|
||||
*temperature = (float)fixed_temperature/100;
|
||||
*pressure = (float)fixed_pressure/256;
|
||||
if (humidity)
|
||||
*humidity = (float)fixed_humidity/1024;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2016 sheinz (https://github.com/sheinz)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef __BMP280_H__
|
||||
#define __BMP280_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "i2c/i2c.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Uncomment to enable debug output.
|
||||
*/
|
||||
// #define BMP280_DEBUG
|
||||
|
||||
/**
|
||||
* BMP280 or BME280 address is 0x77 if SDO pin is high, and is 0x76 if
|
||||
* SDO pin is low.
|
||||
*/
|
||||
#define BMP280_I2C_ADDRESS_0 0x76
|
||||
#define BMP280_I2C_ADDRESS_1 0x77
|
||||
|
||||
#define BMP280_CHIP_ID 0x58 /* BMP280 has chip-id 0x58 */
|
||||
#define BME280_CHIP_ID 0x60 /* BME280 has chip-id 0x60 */
|
||||
|
||||
/**
|
||||
* Mode of BMP280 module operation.
|
||||
* Forced - Measurement is initiated by user.
|
||||
* Normal - Continues measurement.
|
||||
*/
|
||||
typedef enum {
|
||||
BMP280_MODE_SLEEP = 0,
|
||||
BMP280_MODE_FORCED = 1,
|
||||
BMP280_MODE_NORMAL = 3
|
||||
} BMP280_Mode;
|
||||
|
||||
typedef enum {
|
||||
BMP280_FILTER_OFF = 0,
|
||||
BMP280_FILTER_2 = 1,
|
||||
BMP280_FILTER_4 = 2,
|
||||
BMP280_FILTER_8 = 3,
|
||||
BMP280_FILTER_16 = 4
|
||||
} BMP280_Filter;
|
||||
|
||||
/**
|
||||
* Pressure oversampling settings
|
||||
*/
|
||||
typedef enum {
|
||||
BMP280_SKIPPED = 0, /* no measurement */
|
||||
BMP280_ULTRA_LOW_POWER = 1, /* oversampling x1 */
|
||||
BMP280_LOW_POWER = 2, /* oversampling x2 */
|
||||
BMP280_STANDARD = 3, /* oversampling x4 */
|
||||
BMP280_HIGH_RES = 4, /* oversampling x8 */
|
||||
BMP280_ULTRA_HIGH_RES = 5 /* oversampling x16 */
|
||||
} BMP280_Oversampling;
|
||||
|
||||
/**
|
||||
* Stand by time between measurements in normal mode
|
||||
*/
|
||||
typedef enum {
|
||||
BMP280_STANDBY_05 = 0, /* stand by time 0.5ms */
|
||||
BMP280_STANDBY_62 = 1, /* stand by time 62.5ms */
|
||||
BMP280_STANDBY_125 = 2, /* stand by time 125ms */
|
||||
BMP280_STANDBY_250 = 3, /* stand by time 250ms */
|
||||
BMP280_STANDBY_500 = 4, /* stand by time 500ms */
|
||||
BMP280_STANDBY_1000 = 5, /* stand by time 1s */
|
||||
BMP280_STANDBY_2000 = 6, /* stand by time 2s BMP280, 10ms BME280 */
|
||||
BMP280_STANDBY_4000 = 7, /* stand by time 4s BMP280, 20ms BME280 */
|
||||
} BMP280_StandbyTime;
|
||||
|
||||
/**
|
||||
* Configuration parameters for BMP280 module.
|
||||
* Use function bmp280_init_default_params to use default configuration.
|
||||
*/
|
||||
typedef struct {
|
||||
BMP280_Mode mode;
|
||||
BMP280_Filter filter;
|
||||
BMP280_Oversampling oversampling_pressure;
|
||||
BMP280_Oversampling oversampling_temperature;
|
||||
BMP280_Oversampling oversampling_humidity;
|
||||
BMP280_StandbyTime standby;
|
||||
} bmp280_params_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint16_t dig_T1;
|
||||
int16_t dig_T2;
|
||||
int16_t dig_T3;
|
||||
uint16_t dig_P1;
|
||||
int16_t dig_P2;
|
||||
int16_t dig_P3;
|
||||
int16_t dig_P4;
|
||||
int16_t dig_P5;
|
||||
int16_t dig_P6;
|
||||
int16_t dig_P7;
|
||||
int16_t dig_P8;
|
||||
int16_t dig_P9;
|
||||
|
||||
/* Humidity compensation for BME280 */
|
||||
uint8_t dig_H1;
|
||||
int16_t dig_H2;
|
||||
uint8_t dig_H3;
|
||||
int16_t dig_H4;
|
||||
int16_t dig_H5;
|
||||
int8_t dig_H6;
|
||||
|
||||
i2c_dev_t i2c_dev; /* I2C dev setting. */
|
||||
uint8_t id; /* Chip ID */
|
||||
} bmp280_t;
|
||||
|
||||
/**
|
||||
* Initialize default parameters.
|
||||
* Default configuration:
|
||||
* mode: NORAML
|
||||
* filter: OFF
|
||||
* oversampling: x4
|
||||
* standby time: 250ms
|
||||
*/
|
||||
void bmp280_init_default_params(bmp280_params_t *params);
|
||||
|
||||
/**
|
||||
* Initialize BMP280 module, probes for the device, soft resets the device,
|
||||
* reads the calibration constants, and configures the device using the supplied
|
||||
* parameters. Returns true on success otherwise false.
|
||||
*
|
||||
* The I2C address is assumed to have been initialized in the dev, and
|
||||
* may be either BMP280_I2C_ADDRESS_0 or BMP280_I2C_ADDRESS_1. If the I2C
|
||||
* address is unknown then try initializing each in turn.
|
||||
*
|
||||
* This may be called again to soft reset the device and initialize it again.
|
||||
*/
|
||||
bool bmp280_init(bmp280_t *dev, bmp280_params_t *params);
|
||||
|
||||
/**
|
||||
* Start measurement in forced mode.
|
||||
* The module remains in forced mode after this call.
|
||||
* Do not call this method in normal mode.
|
||||
*/
|
||||
bool bmp280_force_measurement(bmp280_t *dev);
|
||||
|
||||
/**
|
||||
* Check if BMP280 is busy with measuring temperature/pressure.
|
||||
* Return true if BMP280 is busy.
|
||||
*/
|
||||
bool bmp280_is_measuring(bmp280_t *dev);
|
||||
|
||||
/**
|
||||
* Read compensated temperature and pressure data:
|
||||
*
|
||||
* Temperature in degrees Celsius times 100.
|
||||
*
|
||||
* Pressure in Pascals in fixed point 24 bit integer 8 bit fraction format.
|
||||
*
|
||||
* Humidity is optional and only read for the BME280, in percent relative
|
||||
* humidity as a fixed point 22 bit interger and 10 bit fraction format.
|
||||
*/
|
||||
bool bmp280_read_fixed(bmp280_t *dev, int32_t *temperature,
|
||||
uint32_t *pressure, uint32_t *humidity);
|
||||
|
||||
/**
|
||||
* Read compensated temperature and pressure data:
|
||||
* Temperature in degrees Celsius.
|
||||
* Pressure in Pascals.
|
||||
* Humidity is optional and only read for the BME280, in percent relative
|
||||
* humidity.
|
||||
*/
|
||||
bool bmp280_read_float(bmp280_t *dev, float *temperature,
|
||||
float *pressure, float *humidity);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __BMP280_H__
|
||||
@@ -0,0 +1,10 @@
|
||||
#
|
||||
# Component Makefile
|
||||
#
|
||||
# This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default,
|
||||
# this will take the sources in the src/ directory, compile them and link them into
|
||||
# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
|
||||
# please read the SDK documents if you need to do this.
|
||||
#
|
||||
|
||||
COMPONENT_ADD_INCLUDEDIRS := .
|
||||
@@ -0,0 +1,5 @@
|
||||
set(COMPONENT_ADD_INCLUDEDIRS .)
|
||||
|
||||
set(COMPONENT_SRCS "sht3x.c")
|
||||
|
||||
register_component()
|
||||
@@ -0,0 +1,389 @@
|
||||
# Driver for **SHT3x** digital **temperature and humidity sensor**
|
||||
|
||||
The driver is for the usage with the ESP8266 and [esp-open-rtos](https://github.com/SuperHouse/esp-open-rtos).
|
||||
|
||||
It is also working with ESP32 and [ESP-IDF](https://github.com/espressif/esp-idf.git) using a wrapper component for ESP8266 functions, see folder ```components/esp8266_wrapper```, as well as Linux based systems using a wrapper library.
|
||||
|
||||
## About the sensor
|
||||
|
||||
SHT3x is a digital temperature and humidity sensor that uses an I2C interface with up to 1 MHz communication speed. It can operate with **three levels of repeatability** (low, medium and high) and in two different modes, the **single shot data acquisition mode** (or short **single shot mode**) and the **periodic data acquisition mode** (or short **periodic mode**).
|
||||
|
||||
## Measurement process
|
||||
|
||||
Once the SHT3x sensor is initialized, it can be used for measurements.
|
||||
|
||||
### Single shot mode
|
||||
|
||||
In **single shot mode**, a measurement command triggers the acquisition of **exactly one data pair**. Each data pair consists of temperature and humidity as 16-bit decimal values.
|
||||
|
||||
Due to the measurement duration of up to 15 ms, the measurement process is separated into steps to avoid blocking the user task during measurements:
|
||||
|
||||
1. Trigger the sensor with function ```sht3x_start_measurement``` to perform exactly one single measurement.
|
||||
|
||||
2. Wait the measurement duration using function ```vTaskDelay``` until the results are available . Use either a constant duration of at least 30 ms or the duration in RTOS ticks returned from function ```sht3x_get_measurement_duration``` to wait.
|
||||
|
||||
3. Fetch the results as floating point sensor values with function ```sht3x_get_results``` or as raw data with function ```sht3x_get_raw_data```.
|
||||
|
||||
In the *single shot mode*, the user task has to perform all steps every time new sensor values are needed.
|
||||
|
||||
For convenience a high level function ```sht3x_measure``` that comprises all three steps above in only one function to perform a measurement. This function is the easiest way to use the sensor. It is most suitable for users that don't want to have the control on sensor details.
|
||||
|
||||
The advantage of this mode is that the sensor can switch between successive measurements into the sleep mode, which is more energy-efficient. This is particularly useful when the measurement rate is less than 1 measurement per second.
|
||||
|
||||
### Periodic mode
|
||||
|
||||
In this mode, one issued measurement command yields a stream of data pairs. Each data pair consists again of temperature and humidity as 16-bit decimal values. As soon as the measurement command has been sent to the sensor, it automatically performs measurements **periodically at a rate of 0.5, 1, 2, 4 or 10 measurements per second (mps)**. The data pairs can be fetched with the same rate or a lower rate.
|
||||
|
||||
As in *single shot mode*, the measurement process is separated into the following steps:
|
||||
|
||||
1. Trigger the sensor with function ```sht3x_start_measurement``` with a given rate to start periodic measurements.
|
||||
|
||||
2. Wait the measurement duration using function ```vTaskDelay``` until the first results are available . Use either a constant duration of at least 30 ms or the duration in RTOS ticks returned from function ```sht3x_get_measurement_duration``` to wait.
|
||||
|
||||
3. Fetch the results as floating point sensor values with function ```sht3x_get_results``` or as raw data with function ```sht3x_get_raw_data```.
|
||||
|
||||
However, in contrast to the *single shot mode*, steps 1 and 2 have to be executed only once. Once the measurement is started, the user task hast can simply fetch data periodically.
|
||||
|
||||
**Please note:** The rate of fetching the measurement results must not be greater than the rate of periodic measurements of the sensor. Even more, it *should be less* to avoid conflicts caused by the timing tolerance of the sensor.
|
||||
|
||||
## Measurement results
|
||||
|
||||
Once new measurement results are available, either function ```sht3x_get_raw_data``` or function ```sht3x_get_results``` can be used to fetch the results.
|
||||
|
||||
Function ```_sht3x_get_raw_data``` fetches only raw sensor data in 16-decimal format, checks the CRC checksums and stores them in an byte array of type ```sht3x_raw_data_t```. The user task then can use them directly or to call function ```sht3x_compute_values``` to compute floating point sensor values from them.
|
||||
|
||||
Function ```sht3x_get_results``` combines function ```sht3x_read_raw_data``` and function
|
||||
```sht3x_compute_values``` to get the sensor values. This is the preferred approach to get sensor values by the user task.
|
||||
|
||||
## Error Handling
|
||||
|
||||
Most driver functions return a simple boolean value to indicate whether its execution was successful or an error happened. In the latter case, the member ```error_code``` of the sensor device data structure is set which indicates what error happened.
|
||||
|
||||
There are two different error levels that are ORed into one single ```error_code```, errors in the I2C communication and errors with the SHT3x sensor itself. To test for a certain error you can AND the *error_code* with one of the error masks, ```SHT3x_I2C_ERROR_MASK``` for I2C errors and ```SHT3x_DRV_ERROR_MASK``` for other errors and then test for a certain error code.
|
||||
|
||||
|
||||
## Repeatability
|
||||
|
||||
The SHT3x sensor supports **three levels of repeatability** (low, medium and high). Repeatability is the variation in measurement results taken by the sensor under the same conditions, and in a short period of time. It is a measure for the noise on the physical sensor output. The higher the repeatability the smaller are changes in the output of subsequent measurements.
|
||||
|
||||
The repeatability settings influences the measurement duration as well as the power consumption of the sensor. The measurement takes 3 ms with low repeatability, 5 ms with medium repeatability and 13.5 ms with high repeatability. That is, the measurement produces a noticeable delay in execution.
|
||||
|
||||
While the sensor measures at the lowest repeatability, the average current consumption is 800 μA. That is, the higher the repeatability level, the longer the measurement takes and the higher the power consumption. The sensor consumes only 0.2 μA in standby mode.
|
||||
|
||||
The repeatability used for a measurement is specified as parameter of function ```sht3x_start_measurement```.
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
Before using the SHT3x driver, function ```i2c_init``` needs to be called for each I2C interface to setup them.
|
||||
|
||||
```
|
||||
#include "sht3x/sht3x.h"
|
||||
...
|
||||
#define I2C_BUS 0
|
||||
#define I2C_SCL_PIN 14
|
||||
#define I2C_SDA_PIN 13
|
||||
|
||||
...
|
||||
i2c_init(I2C_BUS, I2C_SCL_PIN, I2C_SDA_PIN, I2C_FREQ_100K)
|
||||
...
|
||||
```
|
||||
|
||||
Once I2C interfaces to be used are initialized, function ```sht3x_init_sensor``` has to be called for each SHT3x sensor to initialize the sensor and to check its availability as well as its error state. The parameters specify the I2C bus to which it is connected and its I2C slave address.
|
||||
|
||||
```
|
||||
static sht3x_sensor_t* sensor; // pointer to sensor device data structure
|
||||
...
|
||||
if ((sensor = sht3x_init_sensor (I2C_BUS, SHT3x_ADDR_2)))
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Function ```sht3x_init_sensor``` returns a pointer to the sensor device data structure or NULL in case of error.
|
||||
|
||||
Finally, a user task that uses the sensor has to be created.
|
||||
|
||||
```
|
||||
xTaskCreate(user_task, "user_task", 256, NULL, 2, 0);
|
||||
```
|
||||
|
||||
**Please note:** To avoid concurrency situations when driver functions are used to access the sensor, for example to read data, the user task must not be created until the sensor configuration is completed.
|
||||
|
||||
In **periodic mode**, the user task has to start the periodic measurement only once at the beginning of the task. After that, it has only to wait for the results of the first measurement. In the task loop itself, it simply fetches the next measurement results in each cycle.
|
||||
|
||||
Thus, in this mode the user task could look like the following:
|
||||
|
||||
```
|
||||
void user_task (void *pvParameters)
|
||||
{
|
||||
float temperature;
|
||||
float humidity;
|
||||
|
||||
// Start periodic measurements with 1 measurement per second.
|
||||
sht3x_start_measurement (sensor, sht3x_periodic_1mps, sht3x_high);
|
||||
|
||||
// Wait until first measurement is ready (constant time of at least 30 ms
|
||||
// or the duration returned from *sht3x_get_measurement_duration*).
|
||||
vTaskDelay (sht3x_get_measurement_duration(sht3x_high));
|
||||
|
||||
TickType_t last_wakeup = xTaskGetTickCount();
|
||||
|
||||
while (1)
|
||||
{
|
||||
// Get the values and do something with them.
|
||||
if (sht3x_get_results (sensor, &temperature, &humidity))
|
||||
printf("%.3f SHT3x Sensor: %.2f °C, %.2f %%\n",
|
||||
(double)sdk_system_get_time()*1e-3, temperature, humidity);
|
||||
|
||||
// Wait until 2 seconds (cycle time) are over.
|
||||
vTaskDelayUntil(&last_wakeup, 2000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
At the beginning of the task, the periodic measurement is started with function ```sht3x_start_measurement``` at high repeatability level and a rate of 1 measurement per second. The task is then delayed with function ```vTaskDelay``` to wait for first measurement results. The duration can be either a constant time of at least 30 ms or the duration returned by ```sht3x_get_measurement_duration```, as in the example. Inside the task loop, simply the measurement results are fetched periodically using function ```sht3x_get_results``` every 2 seconds.
|
||||
|
||||
**Please note:** The rate of fetching the measurement results must be not greater than the rate of periodic measurements of the sensor, however, it *should be less* to avoid conflicts caused by the timing tolerance of the sensor.
|
||||
|
||||
In **single shot mode**, the measurement has to be triggered
|
||||
in each cycle. Also the waiting for measurement results is required in each cylce, before the results can be fetched.
|
||||
|
||||
Thus the user task could look like the following:
|
||||
|
||||
```
|
||||
void user_task (void *pvParameters)
|
||||
{
|
||||
float temperature;
|
||||
float humidity;
|
||||
|
||||
TickType_t last_wakeup = xTaskGetTickCount();
|
||||
|
||||
uint8_t duration = sht3x_get_measurement_duration(sht3x_high);
|
||||
|
||||
while (1)
|
||||
{
|
||||
// Trigger one measurement in single shot mode with high repeatability.
|
||||
sht3x_start_measurement (sensor, sht3x_single_shot, sht3x_high);
|
||||
|
||||
// Wait until measurement is ready (constant time of at least 30 ms
|
||||
// or the duration returned from *sht3x_get_measurement_duration*).
|
||||
vTaskDelay (duration);
|
||||
|
||||
// retrieve the values and do something with them
|
||||
if (sht3x_get_results (sensor, &temperature, &humidity))
|
||||
printf("%.3f SHT3x Sensor: %.2f °C, %.2f %%\n",
|
||||
(double)sdk_system_get_time()*1e-3, temperature, humidity);
|
||||
|
||||
// wait until 5 seconds are over
|
||||
vTaskDelayUntil(&last_wakeup, 5000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In contrast to the *periodic mode*, the function ```sht3x_start_measurement``` is called inside the task loop to start exactly one measurement in each cycle. The task is then also delayed every time using function ```vTaskDelay``` before the results are fetched with function ```sht3x_get_results```.
|
||||
|
||||
Alternatively, user task can use the high level function ```sht3x_measure``` that comprises these steps in only one function. This would simplify the user task that would then look like the following:
|
||||
|
||||
```
|
||||
void user_task (void *pvParameters)
|
||||
{
|
||||
float temperature;
|
||||
float humidity;
|
||||
|
||||
TickType_t last_wakeup = xTaskGetTickCount();
|
||||
|
||||
while (1)
|
||||
{
|
||||
// perform one measurement and do something with the results
|
||||
if (sht3x_measure (sensor, &temperature, &humidity))
|
||||
printf("%.3f SHT3x Sensor: %.2f °C, %.2f %%\n",
|
||||
(double)sdk_system_get_time()*1e-3, temperature, humidity);
|
||||
|
||||
// wait until 5 seconds are over
|
||||
vTaskDelayUntil(&last_wakeup, 5000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
The code could be extended by an error handling. In the event of an error, most driver functions set the ```error_code``` element of the sensor device data structure. This indicates which error has occurred. Error codes are a combination of I2C communication error codes and SHT3x sensor error codes. To test for a particular error, the *error code* has to be ANDed with one of the error masks ```SHT3x_I2C_ERROR_MASK``` or ```SHT3x_DRV_ERROR_MASK``` and then tested for a certain value.
|
||||
|
||||
For example, error handling for ```sht3x_get_results``` could look like:
|
||||
```
|
||||
|
||||
if (!sht3x_get_results (sensor, &values))
|
||||
{
|
||||
// error happened
|
||||
|
||||
switch (sensor->error_code & SHT3x_I2C_ERROR_MASK)
|
||||
{
|
||||
case SHT3x_I2C_BUSY: ...
|
||||
case SHT3x_I2C_READ_FAILED: ...
|
||||
...
|
||||
}
|
||||
switch (sensor->error_code & SHT3x_DRV_ERROR_MASK)
|
||||
{
|
||||
case SHT3x_MEAS_NOT_RUNNING: ...
|
||||
case SHT3x_READ_RAW_DATA_FAILED: ...
|
||||
case SHT3x_WRONG_CRC_TEMPERATURE: ...
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Full Example
|
||||
|
||||
```
|
||||
/* -- use following constants to define the example mode ----------- */
|
||||
|
||||
// #define SINGLE_SHOT_LOW_LEVEL
|
||||
// #define SINGLE_SHOT_HIGH_LEVEL
|
||||
|
||||
/* -- includes ----------------------------------------------------- */
|
||||
|
||||
#include "sht3x.h"
|
||||
|
||||
/* -- platform dependent definitions ------------------------------- */
|
||||
|
||||
#ifdef ESP_PLATFORM // ESP32 (ESP-IDF)
|
||||
|
||||
// user task stack depth for ESP32
|
||||
#define TASK_STACK_DEPTH 2048
|
||||
|
||||
#else // ESP8266 (esp-open-rtos)
|
||||
|
||||
// user task stack depth for ESP8266
|
||||
#define TASK_STACK_DEPTH 256
|
||||
|
||||
#endif // ESP_PLATFORM
|
||||
|
||||
// I2C interface defintions for ESP32 and ESP8266
|
||||
#define I2C_BUS 0
|
||||
#define I2C_SCL_PIN 14
|
||||
#define I2C_SDA_PIN 13
|
||||
#define I2C_FREQ I2C_FREQ_100K
|
||||
|
||||
/* -- user tasks --------------------------------------------------- */
|
||||
|
||||
static sht3x_sensor_t* sensor; // sensor device data structure
|
||||
|
||||
#if defined(SINGLE_SHOT_HIGH_LEVEL)
|
||||
/*
|
||||
* User task that triggers a measurement every 5 seconds. Due to power
|
||||
* efficiency reasons it uses *single shot* mode. In this example it uses the
|
||||
* high level function *sht3x_measure* to perform one measurement in each cycle.
|
||||
*/
|
||||
void user_task (void *pvParameters)
|
||||
{
|
||||
float temperature;
|
||||
float humidity;
|
||||
|
||||
TickType_t last_wakeup = xTaskGetTickCount();
|
||||
|
||||
while (1)
|
||||
{
|
||||
// perform one measurement and do something with the results
|
||||
if (sht3x_measure (sensor, &temperature, &humidity))
|
||||
printf("%.3f SHT3x Sensor: %.2f °C, %.2f %%\n",
|
||||
(double)sdk_system_get_time()*1e-3, temperature, humidity);
|
||||
|
||||
// wait until 5 seconds are over
|
||||
vTaskDelayUntil(&last_wakeup, 5000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(SINGLE_SHOT_LOW_LEVEL)
|
||||
/*
|
||||
* User task that triggers a measurement every 5 seconds. Due to power
|
||||
* efficiency reasons it uses *single shot* mode. In this example it starts the
|
||||
* measurement, waits for the results and fetches the results using separate
|
||||
* functions
|
||||
*/
|
||||
void user_task (void *pvParameters)
|
||||
{
|
||||
float temperature;
|
||||
float humidity;
|
||||
|
||||
TickType_t last_wakeup = xTaskGetTickCount();
|
||||
|
||||
// get the measurement duration for high repeatability;
|
||||
uint8_t duration = sht3x_get_measurement_duration(sht3x_high);
|
||||
|
||||
while (1)
|
||||
{
|
||||
// Trigger one measurement in single shot mode with high repeatability.
|
||||
sht3x_start_measurement (sensor, sht3x_single_shot, sht3x_high);
|
||||
|
||||
// Wait until measurement is ready (constant time of at least 30 ms
|
||||
// or the duration returned from *sht3x_get_measurement_duration*).
|
||||
vTaskDelay (duration);
|
||||
|
||||
// retrieve the values and do something with them
|
||||
if (sht3x_get_results (sensor, &temperature, &humidity))
|
||||
printf("%.3f SHT3x Sensor: %.2f °C, %.2f %%\n",
|
||||
(double)sdk_system_get_time()*1e-3, temperature, humidity);
|
||||
|
||||
// wait until 5 seconds are over
|
||||
vTaskDelayUntil(&last_wakeup, 5000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
#else // PERIODIC MODE
|
||||
/*
|
||||
* User task that fetches latest measurement results of sensor every 2
|
||||
* seconds. It starts the SHT3x in periodic mode with 1 measurements per
|
||||
* second (*sht3x_periodic_1mps*).
|
||||
*/
|
||||
void user_task (void *pvParameters)
|
||||
{
|
||||
float temperature;
|
||||
float humidity;
|
||||
|
||||
// Start periodic measurements with 1 measurement per second.
|
||||
sht3x_start_measurement (sensor, sht3x_periodic_1mps, sht3x_high);
|
||||
|
||||
// Wait until first measurement is ready (constant time of at least 30 ms
|
||||
// or the duration returned from *sht3x_get_measurement_duration*).
|
||||
vTaskDelay (sht3x_get_measurement_duration(sht3x_high));
|
||||
|
||||
TickType_t last_wakeup = xTaskGetTickCount();
|
||||
|
||||
while (1)
|
||||
{
|
||||
// Get the values and do something with them.
|
||||
if (sht3x_get_results (sensor, &temperature, &humidity))
|
||||
printf("%.3f SHT3x Sensor: %.2f °C, %.2f %%\n",
|
||||
(double)sdk_system_get_time()*1e-3, temperature, humidity);
|
||||
|
||||
// Wait until 2 seconds (cycle time) are over.
|
||||
vTaskDelayUntil(&last_wakeup, 2000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* -- main program ------------------------------------------------- */
|
||||
|
||||
void user_init(void)
|
||||
{
|
||||
// Set UART Parameter.
|
||||
uart_set_baud(0, 115200);
|
||||
// Give the UART some time to settle
|
||||
vTaskDelay(1);
|
||||
|
||||
// Init I2C bus interfaces at which SHT3x sensors are connected
|
||||
// (different busses are possible).
|
||||
i2c_init(I2C_BUS, I2C_SCL_PIN, I2C_SDA_PIN, I2C_FREQ);
|
||||
|
||||
// Create the sensors, multiple sensors are possible.
|
||||
if ((sensor = sht3x_init_sensor (I2C_BUS, SHT3x_ADDR_2)))
|
||||
{
|
||||
// Create a user task that uses the sensors.
|
||||
xTaskCreate(user_task, "user_task", TASK_STACK_DEPTH, NULL, 2, 0);
|
||||
}
|
||||
|
||||
// That's it.
|
||||
}
|
||||
```
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
#
|
||||
# Component Makefile
|
||||
#
|
||||
# This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default,
|
||||
# this will take the sources in the src/ directory, compile them and link them into
|
||||
# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
|
||||
# please read the SDK documents if you need to do this.
|
||||
#
|
||||
|
||||
COMPONENT_ADD_INCLUDEDIRS := .
|
||||
@@ -0,0 +1,423 @@
|
||||
/*
|
||||
* Driver for Sensirion SHT3x digital temperature and humidity sensor
|
||||
* connected to I2C
|
||||
*
|
||||
* This driver is for the usage with the ESP8266 and FreeRTOS (esp-open-rtos)
|
||||
* [https://github.com/SuperHouse/esp-open-rtos]. It is also working with ESP32
|
||||
* and ESP-IDF [https://github.com/espressif/esp-idf.git] as well as Linux
|
||||
* based systems using a wrapper library for ESP8266 functions.
|
||||
*
|
||||
* ----------------------------------------------------------------
|
||||
*
|
||||
* The BSD License (3-clause license)
|
||||
*
|
||||
* Copyright (c) 2017 Gunar Schorcht (https://github.com/gschorcht)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "sht3x.h"
|
||||
|
||||
#define SHT3x_STATUS_CMD 0xF32D
|
||||
#define SHT3x_CLEAR_STATUS_CMD 0x3041
|
||||
#define SHT3x_RESET_CMD 0x30A2
|
||||
#define SHT3x_FETCH_DATA_CMD 0xE000
|
||||
#define SHT3x_HEATER_OFF_CMD 0x3066
|
||||
|
||||
const uint16_t SHT3x_MEASURE_CMD[6][3] = {
|
||||
{0x2400,0x240b,0x2416}, // [SINGLE_SHOT][H,M,L] without clock stretching
|
||||
{0x2032,0x2024,0x202f}, // [PERIODIC_05][H,M,L]
|
||||
{0x2130,0x2126,0x212d}, // [PERIODIC_1 ][H,M,L]
|
||||
{0x2236,0x2220,0x222b}, // [PERIODIC_2 ][H,M,L]
|
||||
{0x2234,0x2322,0x2329}, // [PERIODIC_4 ][H,M,L]
|
||||
{0x2737,0x2721,0x272a} }; // [PERIODIC_10][H,M,L]
|
||||
|
||||
// due to the fact that ticks can be smaller than portTICK_PERIOD_MS, one and
|
||||
// a half tick period added to the duration to be sure that waiting time for
|
||||
// the results is long enough
|
||||
#define TIME_TO_TICKS(ms) (1 + ((ms) + (portTICK_PERIOD_MS-1) + portTICK_PERIOD_MS/2 ) / portTICK_PERIOD_MS)
|
||||
|
||||
#define SHT3x_MEAS_DURATION_REP_HIGH 15
|
||||
#define SHT3x_MEAS_DURATION_REP_MEDIUM 6
|
||||
#define SHT3x_MEAS_DURATION_REP_LOW 4
|
||||
|
||||
// measurement durations in us
|
||||
const uint16_t SHT3x_MEAS_DURATION_US[3] = { SHT3x_MEAS_DURATION_REP_HIGH * 1000,
|
||||
SHT3x_MEAS_DURATION_REP_MEDIUM * 1000,
|
||||
SHT3x_MEAS_DURATION_REP_LOW * 1000 };
|
||||
|
||||
// measurement durations in RTOS ticks
|
||||
const uint8_t SHT3x_MEAS_DURATION_TICKS[3] = { TIME_TO_TICKS(SHT3x_MEAS_DURATION_REP_HIGH),
|
||||
TIME_TO_TICKS(SHT3x_MEAS_DURATION_REP_MEDIUM),
|
||||
TIME_TO_TICKS(SHT3x_MEAS_DURATION_REP_LOW) };
|
||||
|
||||
#if defined(SHT3x_DEBUG_LEVEL_2)
|
||||
#define debug(s, f, ...) printf("%s %s: " s "\n", "SHT3x", f, ## __VA_ARGS__)
|
||||
#define debug_dev(s, f, d, ...) printf("%s %s: bus %d, addr %02x - " s "\n", "SHT3x", f, d->bus, d->addr, ## __VA_ARGS__)
|
||||
#else
|
||||
#define debug(s, f, ...)
|
||||
#define debug_dev(s, f, d, ...)
|
||||
#endif
|
||||
|
||||
#if defined(SHT3x_DEBUG_LEVEL_1) || defined(SHT3x_DEBUG_LEVEL_2)
|
||||
#define error(s, f, ...) printf("%s %s: " s "\n", "SHT3x", f, ## __VA_ARGS__)
|
||||
#define error_dev(s, f, d, ...) printf("%s %s: bus %d, addr %02x - " s "\n", "SHT3x", f, d->bus, d->addr, ## __VA_ARGS__)
|
||||
#else
|
||||
#define error(s, f, ...)
|
||||
#define error_dev(s, f, d, ...)
|
||||
#endif
|
||||
|
||||
/** Forward declaration of function for internal use */
|
||||
|
||||
static bool sht3x_is_measuring (sht3x_sensor_t*);
|
||||
static bool sht3x_send_command (sht3x_sensor_t*, uint16_t);
|
||||
static bool sht3x_read_data (sht3x_sensor_t*, uint8_t*, uint32_t);
|
||||
static bool sht3x_get_status (sht3x_sensor_t*, uint16_t*);
|
||||
static bool sht3x_reset (sht3x_sensor_t*);
|
||||
|
||||
static uint8_t crc8 (uint8_t data[], int len);
|
||||
|
||||
/** ------------------------------------------------ */
|
||||
|
||||
bool sht3x_init_driver()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
sht3x_sensor_t* sht3x_init_sensor(uint8_t bus, uint8_t addr)
|
||||
{
|
||||
sht3x_sensor_t* dev;
|
||||
|
||||
if ((dev = malloc (sizeof(sht3x_sensor_t))) == NULL)
|
||||
return NULL;
|
||||
|
||||
// inititalize sensor data structure
|
||||
dev->bus = bus;
|
||||
dev->addr = addr;
|
||||
dev->mode = sht3x_single_shot;
|
||||
dev->meas_start_time = 0;
|
||||
dev->meas_started = false;
|
||||
dev->meas_first = false;
|
||||
|
||||
uint16_t status;
|
||||
|
||||
// try to reset the sensor
|
||||
if (!sht3x_reset(dev))
|
||||
{
|
||||
debug_dev ("could not reset the sensor", __FUNCTION__, dev);
|
||||
}
|
||||
|
||||
// check again the status after clear status command
|
||||
if (!sht3x_get_status(dev, &status))
|
||||
{
|
||||
error_dev ("could not get sensor status", __FUNCTION__, dev);
|
||||
free(dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
debug_dev ("sensor initialized", __FUNCTION__, dev);
|
||||
return dev;
|
||||
}
|
||||
|
||||
|
||||
bool sht3x_measure (sht3x_sensor_t* dev, float* temperature, float* humidity)
|
||||
{
|
||||
if (!dev || (!temperature && !humidity)) return false;
|
||||
|
||||
if (!sht3x_start_measurement (dev, sht3x_single_shot, sht3x_high))
|
||||
return false;
|
||||
|
||||
vTaskDelay (SHT3x_MEAS_DURATION_TICKS[sht3x_high]);
|
||||
|
||||
sht3x_raw_data_t raw_data;
|
||||
|
||||
if (!sht3x_get_raw_data (dev, raw_data))
|
||||
return false;
|
||||
|
||||
return sht3x_compute_values (raw_data, temperature, humidity);
|
||||
}
|
||||
|
||||
|
||||
bool sht3x_start_measurement (sht3x_sensor_t* dev, sht3x_mode_t mode, sht3x_repeat_t repeat)
|
||||
{
|
||||
if (!dev) return false;
|
||||
|
||||
dev->error_code = SHT3x_OK;
|
||||
dev->mode = mode;
|
||||
dev->repeatability = repeat;
|
||||
|
||||
// start measurement according to selected mode and return an duration estimate
|
||||
if (!sht3x_send_command(dev, SHT3x_MEASURE_CMD[mode][repeat]))
|
||||
{
|
||||
error_dev ("could not send start measurment command", __FUNCTION__, dev);
|
||||
dev->error_code |= SHT3x_SEND_MEAS_CMD_FAILED;
|
||||
return false;
|
||||
}
|
||||
|
||||
dev->meas_start_time = sdk_system_get_time ();
|
||||
dev->meas_started = true;
|
||||
dev->meas_first = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
uint8_t sht3x_get_measurement_duration (sht3x_repeat_t repeat)
|
||||
{
|
||||
return SHT3x_MEAS_DURATION_TICKS[repeat]; // in RTOS ticks
|
||||
}
|
||||
|
||||
|
||||
bool sht3x_get_raw_data(sht3x_sensor_t* dev, sht3x_raw_data_t raw_data)
|
||||
{
|
||||
if (!dev || !raw_data) return false;
|
||||
|
||||
dev->error_code = SHT3x_OK;
|
||||
|
||||
if (!dev->meas_started)
|
||||
{
|
||||
debug_dev ("measurement is not started", __FUNCTION__, dev);
|
||||
dev->error_code = SHT3x_MEAS_NOT_STARTED;
|
||||
return sht3x_is_measuring (dev);
|
||||
}
|
||||
|
||||
if (sht3x_is_measuring(dev))
|
||||
{
|
||||
error_dev ("measurement is still running", __FUNCTION__, dev);
|
||||
dev->error_code = SHT3x_MEAS_STILL_RUNNING;
|
||||
return false;
|
||||
}
|
||||
|
||||
// send fetch command in any periodic mode (mode > 0) before read raw data
|
||||
if (dev->mode && !sht3x_send_command(dev, SHT3x_FETCH_DATA_CMD))
|
||||
{
|
||||
debug_dev ("send fetch command failed", __FUNCTION__, dev);
|
||||
dev->error_code |= SHT3x_SEND_FETCH_CMD_FAILED;
|
||||
return false;
|
||||
}
|
||||
|
||||
// read raw data
|
||||
if (!sht3x_read_data(dev, raw_data, sizeof(sht3x_raw_data_t)))
|
||||
{
|
||||
error_dev ("read raw data failed", __FUNCTION__, dev);
|
||||
dev->error_code |= SHT3x_READ_RAW_DATA_FAILED;
|
||||
return false;
|
||||
}
|
||||
|
||||
// reset first measurement flag
|
||||
dev->meas_first = false;
|
||||
|
||||
// reset measurement started flag in single shot mode
|
||||
if (dev->mode == sht3x_single_shot)
|
||||
dev->meas_started = false;
|
||||
|
||||
// check temperature crc
|
||||
if (crc8(raw_data,2) != raw_data[2])
|
||||
{
|
||||
error_dev ("CRC check for temperature data failed", __FUNCTION__, dev);
|
||||
dev->error_code |= SHT3x_WRONG_CRC_TEMPERATURE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// check humidity crc
|
||||
if (crc8(raw_data+3,2) != raw_data[5])
|
||||
{
|
||||
error_dev ("CRC check for humidity data failed", __FUNCTION__, dev);
|
||||
dev->error_code |= SHT3x_WRONG_CRC_HUMIDITY;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool sht3x_compute_values (sht3x_raw_data_t raw_data, float* temperature, float* humidity)
|
||||
{
|
||||
if (!raw_data) return false;
|
||||
|
||||
if (temperature)
|
||||
*temperature = ((((raw_data[0] * 256.0) + raw_data[1]) * 175) / 65535.0) - 45;
|
||||
|
||||
if (humidity)
|
||||
*humidity = ((((raw_data[3] * 256.0) + raw_data[4]) * 100) / 65535.0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool sht3x_get_results (sht3x_sensor_t* dev, float* temperature, float* humidity)
|
||||
{
|
||||
if (!dev || (!temperature && !humidity)) return false;
|
||||
|
||||
sht3x_raw_data_t raw_data;
|
||||
|
||||
if (!sht3x_get_raw_data (dev, raw_data))
|
||||
return false;
|
||||
|
||||
return sht3x_compute_values (raw_data, temperature, humidity);
|
||||
}
|
||||
|
||||
/* Functions for internal use only */
|
||||
|
||||
static bool sht3x_is_measuring (sht3x_sensor_t* dev)
|
||||
{
|
||||
if (!dev) return false;
|
||||
|
||||
dev->error_code = SHT3x_OK;
|
||||
|
||||
// not running if measurement is not started at all or
|
||||
// it is not the first measurement in periodic mode
|
||||
if (!dev->meas_started || !dev->meas_first)
|
||||
return false;
|
||||
|
||||
// not running if time elapsed is greater than duration
|
||||
uint32_t elapsed = sdk_system_get_time() - dev->meas_start_time;
|
||||
|
||||
return elapsed < SHT3x_MEAS_DURATION_US[dev->repeatability];
|
||||
}
|
||||
|
||||
|
||||
static bool sht3x_send_command(sht3x_sensor_t* dev, uint16_t cmd)
|
||||
{
|
||||
if (!dev) return false;
|
||||
|
||||
uint8_t data[2] = { cmd >> 8, cmd & 0xff };
|
||||
|
||||
debug_dev ("send command MSB=%02x LSB=%02x", __FUNCTION__, dev, data[0], data[1]);
|
||||
|
||||
int err = i2c_slave_write(dev->bus, dev->addr, 0, data, 2);
|
||||
|
||||
if (err)
|
||||
{
|
||||
dev->error_code |= (err == -EBUSY) ? SHT3x_I2C_BUSY : SHT3x_I2C_SEND_CMD_FAILED;
|
||||
error_dev ("i2c error %d on write command %02x", __FUNCTION__, dev, err, cmd);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sht3x_read_data(sht3x_sensor_t* dev, uint8_t *data, uint32_t len)
|
||||
{
|
||||
if (!dev) return false;
|
||||
int err = i2c_slave_read(dev->bus, dev->addr, 0, data, len);
|
||||
|
||||
if (err)
|
||||
{
|
||||
dev->error_code |= (err == -EBUSY) ? SHT3x_I2C_BUSY : SHT3x_I2C_READ_FAILED;
|
||||
error_dev ("error %d on read %d byte", __FUNCTION__, dev, err, len);
|
||||
return false;
|
||||
}
|
||||
|
||||
# ifdef SHT3x_DEBUG_LEVEL_2
|
||||
printf("SHT3x %s: bus %d, addr %02x - read following bytes: ",
|
||||
__FUNCTION__, dev->bus, dev->addr);
|
||||
for (int i=0; i < len; i++)
|
||||
printf("%02x ", data[i]);
|
||||
printf("\n");
|
||||
# endif // ifdef SHT3x_DEBUG_LEVEL_2
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool sht3x_reset (sht3x_sensor_t* dev)
|
||||
{
|
||||
if (!dev) return false;
|
||||
|
||||
debug_dev ("soft-reset triggered", __FUNCTION__, dev);
|
||||
|
||||
dev->error_code = SHT3x_OK;
|
||||
|
||||
// send reset command
|
||||
if (!sht3x_send_command(dev, SHT3x_RESET_CMD))
|
||||
{
|
||||
dev->error_code |= SHT3x_SEND_RESET_CMD_FAILED;
|
||||
return false;
|
||||
}
|
||||
// wait for small amount of time needed (according to datasheet 0.5ms)
|
||||
vTaskDelay (100 / portTICK_PERIOD_MS);
|
||||
|
||||
uint16_t status;
|
||||
|
||||
// check the status after reset
|
||||
if (!sht3x_get_status(dev, &status))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool sht3x_get_status (sht3x_sensor_t* dev, uint16_t* status)
|
||||
{
|
||||
if (!dev || !status) return false;
|
||||
|
||||
dev->error_code = SHT3x_OK;
|
||||
|
||||
uint8_t data[3];
|
||||
|
||||
if (!sht3x_send_command(dev, SHT3x_STATUS_CMD) || !sht3x_read_data(dev, data, 3))
|
||||
{
|
||||
dev->error_code |= SHT3x_SEND_STATUS_CMD_FAILED;
|
||||
return false;
|
||||
}
|
||||
|
||||
*status = data[0] << 8 | data[1];
|
||||
debug_dev ("status=%02x", __FUNCTION__, dev, *status);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
const uint8_t g_polynom = 0x31;
|
||||
|
||||
static uint8_t crc8 (uint8_t data[], int len)
|
||||
{
|
||||
// initialization value
|
||||
uint8_t crc = 0xff;
|
||||
|
||||
// iterate over all bytes
|
||||
for (int i=0; i < len; i++)
|
||||
{
|
||||
crc ^= data[i];
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
bool xor = crc & 0x80;
|
||||
crc = crc << 1;
|
||||
crc = xor ? crc ^ g_polynom : crc;
|
||||
}
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* Driver for Sensirion SHT3x digital temperature and humidity sensor
|
||||
* connected to I2C
|
||||
*
|
||||
* This driver is for the usage with the ESP8266 and FreeRTOS (esp-open-rtos)
|
||||
* [https://github.com/SuperHouse/esp-open-rtos]. It is also working with ESP32
|
||||
* and ESP-IDF [https://github.com/espressif/esp-idf.git] as well as Linux
|
||||
* based systems using a wrapper library for ESP8266 functions.
|
||||
*
|
||||
* ----------------------------------------------------------------
|
||||
*
|
||||
* The BSD License (3-clause license)
|
||||
*
|
||||
* Copyright (c) 2017 Gunar Schorcht (https://github.com/gschorcht)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __SHT3x_H__
|
||||
#define __SHT3x_H__
|
||||
|
||||
// Uncomment to enable debug output
|
||||
// #define SHT3x_DEBUG_LEVEL_1 // only error messages
|
||||
// #define SHT3x_DEBUG_LEVEL_2 // error and debug messages
|
||||
|
||||
#include "stdint.h"
|
||||
#include "stdbool.h"
|
||||
|
||||
#include "sht3x_platform.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// definition of possible I2C slave addresses
|
||||
#define SHT3x_ADDR_1 0x44 // ADDR pin connected to GND/VSS (default)
|
||||
#define SHT3x_ADDR_2 0x45 // ADDR pin connected to VDD
|
||||
|
||||
// definition of error codes
|
||||
#define SHT3x_OK 0
|
||||
#define SHT3x_NOK -1
|
||||
|
||||
#define SHT3x_I2C_ERROR_MASK 0x000f
|
||||
#define SHT3x_DRV_ERROR_MASK 0xfff0
|
||||
|
||||
// error codes for I2C interface ORed with SHT3x error codes
|
||||
#define SHT3x_I2C_READ_FAILED 1
|
||||
#define SHT3x_I2C_SEND_CMD_FAILED 2
|
||||
#define SHT3x_I2C_BUSY 3
|
||||
|
||||
// SHT3x driver error codes OR ed with error codes for I2C interface
|
||||
#define SHT3x_MEAS_NOT_STARTED (1 << 8)
|
||||
#define SHT3x_MEAS_ALREADY_RUNNING (2 << 8)
|
||||
#define SHT3x_MEAS_STILL_RUNNING (3 << 8)
|
||||
#define SHT3x_READ_RAW_DATA_FAILED (4 << 8)
|
||||
|
||||
#define SHT3x_SEND_MEAS_CMD_FAILED (5 << 8)
|
||||
#define SHT3x_SEND_RESET_CMD_FAILED (6 << 8)
|
||||
#define SHT3x_SEND_STATUS_CMD_FAILED (7 << 8)
|
||||
#define SHT3x_SEND_FETCH_CMD_FAILED (8 << 8)
|
||||
|
||||
#define SHT3x_WRONG_CRC_TEMPERATURE (9 << 8)
|
||||
#define SHT3x_WRONG_CRC_HUMIDITY (10 << 8)
|
||||
|
||||
#define SHT3x_RAW_DATA_SIZE 6
|
||||
|
||||
/**
|
||||
* @brief raw data type
|
||||
*/
|
||||
typedef uint8_t sht3x_raw_data_t [SHT3x_RAW_DATA_SIZE];
|
||||
|
||||
|
||||
/**
|
||||
* @brief possible measurement modes
|
||||
*/
|
||||
typedef enum {
|
||||
sht3x_single_shot = 0, // one single measurement
|
||||
sht3x_periodic_05mps, // periodic with 0.5 measurements per second (mps)
|
||||
sht3x_periodic_1mps, // periodic with 1 measurements per second (mps)
|
||||
sht3x_periodic_2mps, // periodic with 2 measurements per second (mps)
|
||||
sht3x_periodic_4mps, // periodic with 4 measurements per second (mps)
|
||||
sht3x_periodic_10mps // periodic with 10 measurements per second (mps)
|
||||
} sht3x_mode_t;
|
||||
|
||||
|
||||
/**
|
||||
* @brief possible repeatability modes
|
||||
*/
|
||||
typedef enum {
|
||||
sht3x_high = 0,
|
||||
sht3x_medium,
|
||||
sht3x_low
|
||||
} sht3x_repeat_t;
|
||||
|
||||
/**
|
||||
* @brief SHT3x sensor device data structure type
|
||||
*/
|
||||
typedef struct {
|
||||
|
||||
uint32_t error_code; // combined error codes
|
||||
|
||||
uint8_t bus; // I2C bus at which sensor is connected
|
||||
uint8_t addr; // I2C slave address of the sensor
|
||||
|
||||
sht3x_mode_t mode; // used measurement mode
|
||||
sht3x_repeat_t repeatability; // used repeatability
|
||||
|
||||
bool meas_started; // indicates whether measurement started
|
||||
uint32_t meas_start_time; // measurement start time in us
|
||||
bool meas_first; // first measurement in periodic mode
|
||||
|
||||
} sht3x_sensor_t;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize a SHT3x sensor
|
||||
*
|
||||
* The function creates a data structure describing the sensor and
|
||||
* initializes the sensor device.
|
||||
*
|
||||
* @param bus I2C bus at which the sensor is connected
|
||||
* @param addr I2C slave address of the sensor
|
||||
* @return pointer to sensor data structure, or NULL on error
|
||||
*/
|
||||
sht3x_sensor_t* sht3x_init_sensor (uint8_t bus, uint8_t addr);
|
||||
|
||||
|
||||
/**
|
||||
* @brief High level measurement function
|
||||
*
|
||||
* For convenience this function comprises all three steps to perform
|
||||
* one measurement in only one function:
|
||||
*
|
||||
* 1. Starts a measurement in single shot mode with high reliability
|
||||
* 2. Waits using *vTaskDelay* until measurement results are available
|
||||
* 3. Returns the results in kind of floating point sensor values
|
||||
*
|
||||
* This function is the easiest way to use the sensor. It is most suitable
|
||||
* for users that don't want to have the control on sensor details.
|
||||
*
|
||||
* Please note: The function delays the calling task up to 30 ms to wait for
|
||||
* the the measurement results. This might lead to problems when the function
|
||||
* is called from a software timer callback function.
|
||||
*
|
||||
* @param dev pointer to sensor device data structure
|
||||
* @param temperature returns temperature in degree Celsius
|
||||
* @param humidity returns humidity in percent
|
||||
* @return true on success, false on error
|
||||
*/
|
||||
bool sht3x_measure (sht3x_sensor_t* dev, float* temperature, float* humidity);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Start the measurement in single shot or periodic mode
|
||||
*
|
||||
* The function starts the measurement either in *single shot mode*
|
||||
* (exactly one measurement) or *periodic mode* (periodic measurements)
|
||||
* with given repeatabilty.
|
||||
*
|
||||
* In the *single shot mode*, this function has to be called for each
|
||||
* measurement. The measurement duration has to be waited every time
|
||||
* before the results can be fetched.
|
||||
*
|
||||
* In the *periodic mode*, this function has to be called only once. Also
|
||||
* the measurement duration has to be waited only once until the first
|
||||
* results are available. After this first measurement, the sensor then
|
||||
* automatically performs all subsequent measurements. The rate of periodic
|
||||
* measurements can be 10, 4, 2, 1 or 0.5 measurements per second (mps).
|
||||
*
|
||||
* Please note: Due to inaccuracies in timing of the sensor, the user task
|
||||
* should fetch the results at a lower rate. The rate of the periodic
|
||||
* measurements is defined by the parameter *mode*.
|
||||
*
|
||||
* @param dev pointer to sensor device data structure
|
||||
* @param mode measurement mode, see type *sht3x_mode_t*
|
||||
* @param repeat repeatability, see type *sht3x_repeat_t*
|
||||
* @return true on success, false on error
|
||||
*/
|
||||
bool sht3x_start_measurement (sht3x_sensor_t* dev, sht3x_mode_t mode,
|
||||
sht3x_repeat_t repeat);
|
||||
|
||||
/**
|
||||
* @brief Get the duration of a measurement in RTOS ticks.
|
||||
*
|
||||
* The function returns the duration in RTOS ticks required by the sensor to
|
||||
* perform a measurement for the given repeatability. Once a measurement is
|
||||
* started with function *sht3x_start_measurement* the user task can use this
|
||||
* duration in RTOS ticks directly to wait with function *vTaskDelay* until
|
||||
* the measurement results can be fetched.
|
||||
*
|
||||
* Please note: The duration only depends on repeatability level. Therefore,
|
||||
* it can be considered as constant for a repeatibility.
|
||||
*
|
||||
* @param repeat repeatability, see type *sht3x_repeat_t*
|
||||
* @return measurement duration given in RTOS ticks
|
||||
*/
|
||||
uint8_t sht3x_get_measurement_duration (sht3x_repeat_t repeat);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Read measurement results from sensor as raw data
|
||||
*
|
||||
* The function read measurement results from the sensor, checks the CRC
|
||||
* checksum and stores them in the byte array as following.
|
||||
*
|
||||
* data[0] = Temperature MSB
|
||||
* data[1] = Temperature LSB
|
||||
* data[2] = Temperature CRC
|
||||
* data[3] = Pressure MSB
|
||||
* data[4] = Pressure LSB
|
||||
* data[2] = Pressure CRC
|
||||
*
|
||||
* In case that there are no new data that can be read, the function fails.
|
||||
*
|
||||
* @param dev pointer to sensor device data structure
|
||||
* @param raw_data byte array in which raw data are stored
|
||||
* @return true on success, false on error
|
||||
*/
|
||||
bool sht3x_get_raw_data(sht3x_sensor_t* dev, sht3x_raw_data_t raw_data);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Computes sensor values from raw data
|
||||
*
|
||||
* @param raw_data byte array that contains raw data
|
||||
* @param temperature returns temperature in degree Celsius
|
||||
* @param humidity returns humidity in percent
|
||||
* @return true on success, false on error
|
||||
*/
|
||||
bool sht3x_compute_values (sht3x_raw_data_t raw_data,
|
||||
float* temperature, float* humidity);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get measurement results in form of sensor values
|
||||
*
|
||||
* The function combines function *sht3x_read_raw_data* and function
|
||||
* *sht3x_compute_values* to get the measurement results.
|
||||
*
|
||||
* In case that there are no results that can be read, the function fails.
|
||||
*
|
||||
* @param dev pointer to sensor device data structure
|
||||
* @param temperature returns temperature in degree Celsius
|
||||
* @param humidity returns humidity in percent
|
||||
* @return true on success, false on error
|
||||
*/
|
||||
bool sht3x_get_results (sht3x_sensor_t* dev,
|
||||
float* temperature, float* humidity);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __SHT3x_H__ */
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Driver for Sensirion SHT3x digital temperature and humidity sensor
|
||||
* connected to I2C
|
||||
*
|
||||
* This driver is for the usage with the ESP8266 and FreeRTOS (esp-open-rtos)
|
||||
* [https://github.com/SuperHouse/esp-open-rtos]. It is also working with ESP32
|
||||
* and ESP-IDF [https://github.com/espressif/esp-idf.git] as well as Linux
|
||||
* based systems using a wrapper library for ESP8266 functions.
|
||||
*
|
||||
* ----------------------------------------------------------------
|
||||
*
|
||||
* The BSD License (3-clause license)
|
||||
*
|
||||
* Copyright (c) 2017 Gunar Schorcht (https://github.com/gschorcht)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Platform file: platform specific definitions, includes and functions
|
||||
*/
|
||||
|
||||
#ifndef __SHT3x_PLATFORM_H__
|
||||
#define __SHT3x_PLATFORM_H__
|
||||
|
||||
#if !defined(ESP_OPEN_RTOS)
|
||||
#define ESP_OPEN_RTOS 1
|
||||
#endif
|
||||
|
||||
#ifdef ESP_OPEN_RTOS // ESP8266
|
||||
|
||||
// platform specific includes
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
|
||||
#include "espressif/esp_common.h"
|
||||
#include "espressif/sdk_private.h"
|
||||
|
||||
#include "esp/uart.h"
|
||||
#include "i2c/i2c.h"
|
||||
|
||||
#endif // ESP_OPEN_RTOS
|
||||
|
||||
#endif // __SHT3x_PLATFORM_H__
|
||||
Reference in New Issue
Block a user