Browse Source

drivers: Add driver for BME280

pr/spi.typo
Kees Bakker 6 years ago
parent
commit
a0a76d66d2
  1. 5
      drivers/Makefile.dep
  2. 3
      drivers/Makefile.include
  3. 3
      drivers/bme280/Makefile
  4. 369
      drivers/bme280/bme280.c
  5. 74
      drivers/bme280/bme280_saul.c
  6. 88
      drivers/bme280/include/bme280_internals.h
  7. 92
      drivers/bme280/include/bme280_params.h
  8. 223
      drivers/include/bme280.h
  9. 4
      sys/auto_init/auto_init.c
  10. 75
      sys/auto_init/saul/auto_init_bme280.c
  11. 7
      tests/driver_bme280/Makefile
  12. 30
      tests/driver_bme280/Readme.md
  13. 101
      tests/driver_bme280/main.c

5
drivers/Makefile.dep

@ -29,6 +29,11 @@ ifneq (,$(filter bmp180,$(USEMODULE)))
USEMODULE += xtimer
endif
ifneq (,$(filter bme280,$(USEMODULE)))
FEATURES_REQUIRED += periph_i2c
USEMODULE += xtimer
endif
ifneq (,$(filter cc110x,$(USEMODULE)))
USEMODULE += ieee802154
USEMODULE += uuid

3
drivers/Makefile.include

@ -67,6 +67,9 @@ endif
ifneq (,$(filter jc42,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/jc42/include
endif
ifneq (,$(filter bme280,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/bme280/include
endif
ifneq (,$(filter cc2420,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/cc2420/include
endif

3
drivers/bme280/Makefile

@ -0,0 +1,3 @@
MODULE = bme280
include $(RIOTBASE)/Makefile.base

369
drivers/bme280/bme280.c

@ -0,0 +1,369 @@
/*
* Copyright (C) 2016 Kees Bakker, SODAQ
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup drivers_bme280
* @{
*
* @file
* @brief Device driver implementation for the BME280 temperature,
* pressure and humidity sensor.
*
* @author Kees Bakker <kees@sodaq.com>
*
* @}
*/
#include <string.h>
#include <math.h>
#include "log.h"
#include "bme280.h"
#include "bme280_internals.h"
#include "bme280_params.h"
#include "periph/i2c.h"
#include "xtimer.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
static int read_calibration_data(bme280_t* dev);
static int do_measurement(bme280_t* dev);
static uint8_t get_ctrl_meas(bme280_t* dev);
static uint8_t get_status(bme280_t* dev);
static uint8_t read_u8_reg(bme280_t* dev, uint8_t reg);
static void write_u8_reg(bme280_t* dev, uint8_t reg, uint8_t b);
static uint16_t get_uint16_le(const uint8_t *buffer, size_t offset);
static int16_t get_int16_le(const uint8_t *buffer, size_t offset);
#if ENABLE_DEBUG
static void dump_buffer(const char *txt, uint8_t *buffer, size_t size);
#define DUMP_BUFFER(txt, buffer, size) dump_buffer(txt, buffer, size)
#else
#define DUMP_BUFFER(txt, buffer, size)
#endif
/**
* @brief Fine resolution temperature value, also needed for pressure and humidity.
*/
static int32_t t_fine;
/**
* @brief The measurement registers, including temperature, pressure and humidity
*
* A temporary buffer for the memory map 0xF7..0xFE
* These are read all at once and then used to compute the three sensor values.
*/
static uint8_t measurement_regs[8];
/*---------------------------------------------------------------------------*
* BME280 Core API *
*---------------------------------------------------------------------------*/
int bme280_init(bme280_t* dev, const bme280_params_t* params)
{
uint8_t chip_id;
dev->params = *params;
/* Initialize I2C interface */
if (i2c_init_master(dev->params.i2c_dev, I2C_SPEED_NORMAL)) {
DEBUG("[Error] I2C device not enabled\n");
return BME280_ERR_I2C;
}
/* Read chip ID */
chip_id = read_u8_reg(dev, BME280_CHIP_ID_REG);
if (chip_id != BME280_CHIP_ID) {
DEBUG("[Error] Did not detect a BME280 at address %02x (%02X != %02X)\n",
dev->params.i2c_addr, chip_id, BME280_CHIP_ID);
return BME280_ERR_NODEV;
}
/* Read compensation data, 0x88..0x9F, 0xA1, 0xE1..0xE7 */
if (read_calibration_data(dev)) {
DEBUG("[Error] Could not read calibration data\n");
return BME280_ERR_NOCAL;
}
return BME280_OK;
}
/*
* Returns temperature in DegC, resolution is 0.01 DegC.
* t_fine carries fine temperature as global value
*/
int16_t bme280_read_temperature(bme280_t* dev)
{
if (do_measurement(dev) < 0) {
return INT16_MIN;
}
bme280_calibration_t *cal = &dev->calibration; /* helper variable */
/* Read the uncompensated temperature */
int32_t adc_T = (((uint32_t)measurement_regs[3 + 0]) << 12) |
(((uint32_t)measurement_regs[3 + 1]) << 4) |
((((uint32_t)measurement_regs[3 + 2]) >> 4) & 0x0F);
/*
* Compensate the temperature value.
* The following is code from Bosch's BME280_driver bme280_compensate_temperature_int32()
* The variable names and the many defines have been modified to make the code
* more readable.
*/
int32_t var1;
int32_t var2;
var1 = ((((adc_T >> 3) - ((int32_t)cal->dig_T1 << 1))) * ((int32_t)cal->dig_T2)) >> 11;
var2 = (((((adc_T >> 4) - ((int32_t)cal->dig_T1)) * ((adc_T >> 4) - ((int32_t)cal->dig_T1))) >> 12) *
((int32_t)cal->dig_T3)) >> 14;
/* calculate t_fine (used for pressure and humidity too) */
t_fine = var1 + var2;
return (t_fine * 5 + 128) >> 8;
}
/*
* Returns pressure in Pa
*/
uint32_t bme280_read_pressure(bme280_t *dev)
{
bme280_calibration_t *cal = &dev->calibration; /* helper variable */
/* Read the uncompensated pressure */
int32_t adc_P = (((uint32_t)measurement_regs[0 + 0]) << 12) |
(((uint32_t)measurement_regs[0 + 1]) << 4) |
((((uint32_t)measurement_regs[0 + 2]) >> 4) & 0x0F);
int64_t var1;
int64_t var2;
int64_t p_acc;
/*
* Compensate the pressure value.
* The following is code from Bosch's BME280_driver bme280_compensate_pressure_int64()
* The variable names and the many defines have been modified to make the code
* more readable.
*/
var1 = ((int64_t)t_fine) - 128000;
var2 = var1 * var1 * (int64_t)cal->dig_P6;
var2 = var2 + ((var1 * (int64_t)cal->dig_P5) << 17);
var2 = var2 + (((int64_t)cal->dig_P4) << 35);
var1 = ((var1 * var1 * (int64_t)cal->dig_P3) >> 8) + ((var1 * (int64_t)cal->dig_P2) << 12);
var1 = (((((int64_t)1) << 47) + var1)) * ((int64_t)cal->dig_P1) >> 33;
/* Avoid division by zero */
if (var1 == 0) {
return UINT32_MAX;
}
p_acc = 1048576 - adc_P;
p_acc = (((p_acc << 31) - var2) * 3125) / var1;
var1 = (((int64_t)cal->dig_P9) * (p_acc >> 13) * (p_acc >> 13)) >> 25;
var2 = (((int64_t)cal->dig_P8) * p_acc) >> 19;
p_acc = ((p_acc + var1 + var2) >> 8) + (((int64_t)cal->dig_P7) << 4);
return p_acc >> 8;
}
uint16_t bme280_read_humidity(bme280_t *dev)
{
bme280_calibration_t *cal = &dev->calibration; /* helper variable */
/* Read the uncompensated pressure */
int32_t adc_H = (((uint32_t)measurement_regs[6 + 0]) << 8) |
(((uint32_t)measurement_regs[6 + 1]));
/*
* Compensate the humidity value.
* The following is code from Bosch's BME280_driver bme280_compensate_humidity_int32()
* The variable names and the many defines have been modified to make the code
* more readable.
* The value is first computed as a value in %rH as unsigned 32bit integer
* in Q22.10 format(22 integer 10 fractional bits).
*/
int32_t var1;
/* calculate x1*/
var1 = (t_fine - ((int32_t)76800));
/* calculate x1*/
var1 = (((((adc_H << 14) - (((int32_t)cal->dig_H4) << 20) - (((int32_t)cal->dig_H5) * var1)) +
((int32_t)16384)) >> 15) *
(((((((var1 * ((int32_t)cal->dig_H6)) >> 10) *
(((var1 * ((int32_t)cal->dig_H3)) >> 11) + ((int32_t)32768))) >> 10) +
((int32_t)2097152)) * ((int32_t)cal->dig_H2) + 8192) >> 14));
var1 = (var1 - (((((var1 >> 15) * (var1 >> 15)) >> 7) * ((int32_t)cal->dig_H1)) >> 4));
var1 = (var1 < 0 ? 0 : var1);
var1 = (var1 > 419430400 ? 419430400 : var1);
/* First multiply to avoid losing the accuracy after the shift by ten */
return (100 * ((uint32_t)var1 >> 12)) >> 10;
}
/**
* Read compensation data, 0x88..0x9F, 0xA1, 0xE1..0xE7
*
* This function reads all calibration bytes at once. These are
* the registers DIG_T1_LSB (0x88) upto and including DIG_H6 (0xE7).
*/
static int read_calibration_data(bme280_t* dev)
{
uint8_t buffer[128]; /* 128 should be enough to read all calibration bytes */
int nr_bytes;
int nr_bytes_to_read = (BME280_DIG_H6_REG - BME280_DIG_T1_LSB_REG) + 1;
uint8_t offset = 0x88;
memset(buffer, 0, sizeof(buffer));
nr_bytes = i2c_read_regs(dev->params.i2c_dev, dev->params.i2c_addr, offset,
buffer, nr_bytes_to_read);
if (nr_bytes != nr_bytes_to_read) {
LOG_ERROR("Unable to read calibration data\n");
return -1;
}
DUMP_BUFFER("Raw Calibration Data", buffer, nr_bytes);
/* All little endian */
dev->calibration.dig_T1 = get_uint16_le(buffer, BME280_DIG_T1_LSB_REG - offset);
dev->calibration.dig_T2 = get_int16_le(buffer, BME280_DIG_T2_LSB_REG - offset);
dev->calibration.dig_T3 = get_int16_le(buffer, BME280_DIG_T3_LSB_REG - offset);
dev->calibration.dig_P1 = get_uint16_le(buffer, BME280_DIG_P1_LSB_REG - offset);
dev->calibration.dig_P2 = get_int16_le(buffer, BME280_DIG_P2_LSB_REG - offset);
dev->calibration.dig_P3 = get_int16_le(buffer, BME280_DIG_P3_LSB_REG - offset);
dev->calibration.dig_P4 = get_int16_le(buffer, BME280_DIG_P4_LSB_REG - offset);
dev->calibration.dig_P5 = get_int16_le(buffer, BME280_DIG_P5_LSB_REG - offset);
dev->calibration.dig_P6 = get_int16_le(buffer, BME280_DIG_P6_LSB_REG - offset);
dev->calibration.dig_P7 = get_int16_le(buffer, BME280_DIG_P7_LSB_REG - offset);
dev->calibration.dig_P8 = get_int16_le(buffer, BME280_DIG_P8_LSB_REG - offset);
dev->calibration.dig_P9 = get_int16_le(buffer, BME280_DIG_P9_LSB_REG - offset);
dev->calibration.dig_H1 = buffer[BME280_DIG_H1_REG - offset];
dev->calibration.dig_H2 = get_int16_le(buffer, BME280_DIG_H2_LSB_REG - offset);
dev->calibration.dig_H3 = buffer[BME280_DIG_H3_REG - offset];
dev->calibration.dig_H4 = (((int16_t)buffer[BME280_DIG_H4_MSB_REG - offset]) << 4) +
(buffer[BME280_DIG_H4_H5_REG - offset] & 0x0F);
dev->calibration.dig_H5 = (((int16_t)buffer[BME280_DIG_H5_MSB_REG - offset]) << 4) +
((buffer[BME280_DIG_H4_H5_REG - offset] & 0xF0) >> 4);
dev->calibration.dig_H6 = buffer[BME280_DIG_H6_REG - offset];
DEBUG("[INFO] Chip ID = 0x%02X\n", buffer[BME280_CHIP_ID_REG - offset]);
/* Config is only be writable in sleep mode */
(void)i2c_write_reg(dev->params.i2c_dev, dev->params.i2c_addr,
BME280_CTRL_MEAS_REG, 0);
uint8_t b;
/* Config Register */
/* spi3w_en unused */
b = ((dev->params.t_sb & 7) << 5) | ((dev->params.filter & 7) << 2);
write_u8_reg(dev, BME280_CONFIG_REG, b);
/*
* Note from the datasheet about ctrl_hum: "Changes to this register only become effective
* after a write operation to "ctrl_meas".
* So, set ctrl_hum first.
*/
b = dev->params.humid_oversample & 7;
write_u8_reg(dev, BME280_CTRL_HUMIDITY_REG, b);
b = ((dev->params.temp_oversample & 7) << 5) |
((dev->params.press_oversample & 7) << 2) |
(dev->params.run_mode & 3);
write_u8_reg(dev, BME280_CTRL_MEAS_REG, b);
return 0;
}
/**
* @brief Start a measurement and read the registers
*/
static int do_measurement(bme280_t* dev)
{
/*
* If settings has FORCED mode, then the device go to sleep after
* it finished the measurement. To read again we have to set the
* run_mode back to FORCED.
*/
uint8_t ctrl_meas = get_ctrl_meas(dev);
uint8_t run_mode = ctrl_meas & 3;
if (run_mode != dev->params.run_mode) {
/* Set the run_mode back to what we want. */
ctrl_meas &= ~3;
ctrl_meas |= dev->params.run_mode;
write_u8_reg(dev, BME280_CTRL_MEAS_REG, ctrl_meas);
/* Wait for measurement ready? */
size_t count = 0;
while (count < 10 && (get_status(dev) & 0x08) != 0) {
++count;
}
/* What to do when measuring is still on? */
}
int nr_bytes;
int nr_bytes_to_read = sizeof(measurement_regs);
uint8_t offset = BME280_PRESSURE_MSB_REG;
nr_bytes = i2c_read_regs(dev->params.i2c_dev, dev->params.i2c_addr,
offset, measurement_regs, nr_bytes_to_read);
if (nr_bytes != nr_bytes_to_read) {
LOG_ERROR("Unable to read temperature data\n");
return -1;
}
DUMP_BUFFER("Raw Sensor Data", measurement_regs, nr_bytes);
return 0;
}
static uint8_t get_ctrl_meas(bme280_t* dev)
{
return read_u8_reg(dev, BME280_CTRL_MEAS_REG);
}
static uint8_t get_status(bme280_t* dev)
{
return read_u8_reg(dev, BME280_STAT_REG);
}
static uint8_t read_u8_reg(bme280_t* dev, uint8_t reg)
{
uint8_t b;
/* Assuming device is correct, it should return 1 (nr bytes) */
(void)i2c_read_reg(dev->params.i2c_dev, dev->params.i2c_addr, reg, &b);
return b;
}
static void write_u8_reg(bme280_t* dev, uint8_t reg, uint8_t b)
{
/* Assuming device is correct, it should return 1 (nr bytes) */
(void)i2c_write_reg(dev->params.i2c_dev, dev->params.i2c_addr, reg, b);
}
static uint16_t get_uint16_le(const uint8_t *buffer, size_t offset)
{
return (((uint16_t)buffer[offset + 1]) << 8) + buffer[offset];
}
static int16_t get_int16_le(const uint8_t *buffer, size_t offset)
{
return (((int16_t)buffer[offset + 1]) << 8) + buffer[offset];
}
#if ENABLE_DEBUG
static void dump_buffer(const char *txt, uint8_t *buffer, size_t size)
{
size_t ix;
DEBUG("%s\n", txt);
for (ix = 0; ix < size; ix++) {
DEBUG("%02X", buffer[ix]);
if ((ix + 1) == size || (((ix + 1) % 16) == 0)) {
DEBUG("\n");
}
}
}
#endif

74
drivers/bme280/bme280_saul.c

@ -0,0 +1,74 @@
/*
* Copyright (C) 2016 Kees Bakker, SODAQ
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup driver_bme280
* @{
*
* @file
* @brief SAUL adoption for BME280 sensor.
*
* @author Kees Bakker <kees@sodaq.com>
*
* @}
*/
#include "saul.h"
#include "bme280.h"
static int read_temperature(void *dev, phydat_t *res)
{
bme280_t *d = (bme280_t *)dev;
res->val[0] = bme280_read_temperature(d);
res->unit = UNIT_TEMP_C;
res->scale = -2;
return 1;
}
static int read_relative_humidity(void *dev, phydat_t *res)
{
bme280_t *d = (bme280_t *)dev;
res->val[0] = bme280_read_humidity(d);
res->unit = UNIT_PERCENT;
res->scale = -2;
return 1;
}
static int read_pressure(void *dev, phydat_t *res)
{
bme280_t *d = (bme280_t *)dev;
res->val[0] = bme280_read_pressure(d) / 100;
res->unit = UNIT_PA;
res->scale = 2;
return 1;
}
const saul_driver_t bme280_temperature_saul_driver = {
.read = read_temperature,
.write = saul_notsup,
.type = SAUL_SENSE_TEMP,
};
const saul_driver_t bme280_relative_humidity_saul_driver = {
.read = read_relative_humidity,
.write = saul_notsup,
.type = SAUL_SENSE_HUM,
};
const saul_driver_t bme280_pressure_saul_driver = {
.read = read_pressure,
.write = saul_notsup,
.type = SAUL_SENSE_PRESS,
};

88
drivers/bme280/include/bme280_internals.h

@ -0,0 +1,88 @@
/*
* Copyright (C) 2016 Kees Bakker, SODAQ
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup drivers_bme280
* @brief Internal addresses, registers, constants for the BME280 sensor.
* @{
* @file
* @brief Internal addresses, registers, constants for the BME280 sensor.
*
* @author Kees Bakker <kees@sodaq.com>
*/
#ifndef BME280_INTERNALS_H
#define BME280_INTERNALS_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name BME280 registers
* @{
*/
#define BME280_DIG_T1_LSB_REG 0x88
#define BME280_DIG_T1_MSB_REG 0x89
#define BME280_DIG_T2_LSB_REG 0x8A
#define BME280_DIG_T2_MSB_REG 0x8B
#define BME280_DIG_T3_LSB_REG 0x8C
#define BME280_DIG_T3_MSB_REG 0x8D
#define BME280_DIG_P1_LSB_REG 0x8E
#define BME280_DIG_P1_MSB_REG 0x8F
#define BME280_DIG_P2_LSB_REG 0x90
#define BME280_DIG_P2_MSB_REG 0x91
#define BME280_DIG_P3_LSB_REG 0x92
#define BME280_DIG_P3_MSB_REG 0x93
#define BME280_DIG_P4_LSB_REG 0x94
#define BME280_DIG_P4_MSB_REG 0x95
#define BME280_DIG_P5_LSB_REG 0x96
#define BME280_DIG_P5_MSB_REG 0x97
#define BME280_DIG_P6_LSB_REG 0x98
#define BME280_DIG_P6_MSB_REG 0x99
#define BME280_DIG_P7_LSB_REG 0x9A
#define BME280_DIG_P7_MSB_REG 0x9B
#define BME280_DIG_P8_LSB_REG 0x9C
#define BME280_DIG_P8_MSB_REG 0x9D
#define BME280_DIG_P9_LSB_REG 0x9E
#define BME280_DIG_P9_MSB_REG 0x9F
#define BME280_DIG_H1_REG 0xA1
#define BME280_CHIP_ID 0x60 /* The identifier of the BME280 */
#define BME280_CHIP_ID_REG 0xD0
#define BME280_RST_REG 0xE0 /* Softreset Reg */
#define BME280_DIG_H2_LSB_REG 0xE1
#define BME280_DIG_H2_MSB_REG 0xE2
#define BME280_DIG_H3_REG 0xE3
#define BME280_DIG_H4_MSB_REG 0xE4 /* H4[11:4] */
#define BME280_DIG_H4_H5_REG 0xE5 /* H5[3:0] H4[3:0] */
#define BME280_DIG_H5_MSB_REG 0xE6 /* H5[11:4] */
#define BME280_DIG_H6_REG 0xE7
#define BME280_CTRL_HUMIDITY_REG 0xF2 /* Ctrl Humidity Reg */
#define BME280_STAT_REG 0xF3 /* Status Reg */
#define BME280_CTRL_MEAS_REG 0xF4 /* Ctrl Measure Reg */
#define BME280_CONFIG_REG 0xF5 /* Configuration Reg */
#define BME280_PRESSURE_MSB_REG 0xF7 /* Pressure MSB */
#define BME280_PRESSURE_LSB_REG 0xF8 /* Pressure LSB */
#define BME280_PRESSURE_XLSB_REG 0xF9 /* Pressure XLSB */
#define BME280_TEMPERATURE_MSB_REG 0xFA /* Temperature MSB */
#define BME280_TEMPERATURE_LSB_REG 0xFB /* Temperature LSB */
#define BME280_TEMPERATURE_XLSB_REG 0xFC /* Temperature XLSB */
#define BME280_HUMIDITY_MSB_REG 0xFD /* Humidity MSB */
#define BME280_HUMIDITY_LSB_REG 0xFE /* Humidity LSB */
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* BME280_INTERNALS_H */
/** @} */

92
drivers/bme280/include/bme280_params.h

@ -0,0 +1,92 @@
/*
* Copyright (C) 2016 Kees Bakker, SODAQ
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup drivers_bme280
*
* @{
* @file
* @brief Default configuration for BME280
*
* @author Kees Bakker <kees@sodaq.com>
*/
#ifndef BME280_PARAMS_H
#define BME280_PARAMS_H
#include "bme280.h"
#include "saul_reg.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Set default configuration parameters for the BME280
* @{
*/
#ifndef BME280_PARAM_I2C_DEV
#define BME280_PARAM_I2C_DEV I2C_DEV(0)
#endif
#ifndef BME280_PARAM_I2C_ADDR
#define BME280_PARAM_I2C_ADDR (0x77)
#endif
/* Defaults for Weather Monitoring */
#define BME280_PARAMS_DEFAULT \
{ \
.i2c_dev = BME280_PARAM_I2C_DEV, \
.i2c_addr = BME280_PARAM_I2C_ADDR, \
.t_sb = BME280_SB_0_5, \
.filter = BME280_FILTER_OFF, \
.run_mode = BME280_MODE_FORCED, \
.temp_oversample = BME280_OSRS_X1, \
.press_oversample = BME280_OSRS_X1, \
.humid_oversample = BME280_OSRS_X1, \
}
/**@}*/
/**
* @brief Configure BME280
*/
static const bme280_params_t bme280_params[] =
{
#ifdef BME280_PARAMS_BOARD
BME280_PARAMS_BOARD,
#else
BME280_PARAMS_DEFAULT,
#endif
};
/**
* @brief The number of configured sensors
*/
#define BME280_NUMOF (sizeof(bme280_params) / sizeof(bme280_params[0]))
/**
* @brief Configuration details of SAUL registry entries
*
* This two dimensional array contains static details of the sensors
* for each device. Please be awar that the indexes are used in
* auto_init_bme280, so make sure the indexes match.
*/
static const saul_reg_info_t bme280_saul_reg_info[BME280_NUMOF][3] =
{
{
{ .name = "bme280-temp" },
{ .name = "bme280-humidity" },
{ .name = "bme280-press" },
},
};
#ifdef __cplusplus
}
#endif
#endif /* BME280_PARAMS_H */
/** @} */

223
drivers/include/bme280.h

@ -0,0 +1,223 @@
/*
* Copyright (C) 2016 Kees Bakker, SODAQ
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup drivers_bme280 BME280
* @ingroup drivers_sensors
* @brief Device driver interface for the BME280 sensor
*
* @{
* @file
* @brief Device driver interface for the BME280 sensor.
*
* @details There are three sensor values that can be read: temperature,
* pressure and humidity. The BME280 device usually measures them
* all at once. It is possible to skip measuring either of the
* values by changing the oversampling settings. The driver is
* written in such a way that a measurement is only started from
* the function that reads the temperature. In other words, you
* always have to call bme280_read_temperature, and then optionally
* you can call the other two.
*
* @author Kees Bakker <kees@sodaq.com>
*/
#ifndef BME280_H
#define BME280_H
#include "saul.h"
#include "periph/i2c.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Calibration struct for the BME280 sensor
*
* This must be read from the device at startup.
*/
typedef struct {
uint16_t dig_T1; /**< T1 coefficient */
int16_t dig_T2; /**< T2 coefficient */
int16_t dig_T3; /**< T3 coefficient */
uint16_t dig_P1; /**< P1 coefficient */
int16_t dig_P2; /**< P2 coefficient */
int16_t dig_P3; /**< P3 coefficient */
int16_t dig_P4; /**< P4 coefficient */
int16_t dig_P5; /**< P5 coefficient */
int16_t dig_P6; /**< P6 coefficient */
int16_t dig_P7; /**< P7 coefficient */
int16_t dig_P8; /**< P8 coefficient */
int16_t dig_P9; /**< P9 coefficient */
uint8_t dig_H1; /**< H1 coefficient */
int16_t dig_H2; /**< H2 coefficient */
uint8_t dig_H3; /**< H3 coefficient */
int16_t dig_H4; /**< H4 coefficient */
int16_t dig_H5; /**< H5 coefficient */
int8_t dig_H6; /**< H6 coefficient */
} bme280_calibration_t;
/**
* @brief Values for t_sb field of the BME280 config register
*/
typedef enum {
BME280_SB_0_5 = 0,
BME280_SB_62_5 = 1,
BME280_SB_125 = 2,
BME280_SB_250 = 3,
BME280_SB_500 = 4,
BME280_SB_1000 = 5,
BME280_SB_10 = 6,
BME280_SB_20 = 7
} bme280_t_sb_t;
/**
* @brief Values for filter field of the BME280 config register
*/
typedef enum {
BME280_FILTER_OFF = 0,
BME280_FILTER_2 = 1,
BME280_FILTER_4 = 2,
BME280_FILTER_8 = 3,
BME280_FILTER_16 = 4,
} bme280_filter_t;
/**
* @brief Values for mode field of the BME280 ctrl_meas register
*/
typedef enum {
BME280_MODE_SLEEP = 0,
BME280_MODE_FORCED = 1,
BME280_MODE_FORCED2 = 2, /* Same as FORCED */
BME280_MODE_NORMAL = 3
} bme280_mode_t;
/**
* @brief Values for oversampling settings
*
* These values are used for:
* - osrs_h field of the BME280 ctrl_hum register
* - osrs_t field of the BME280 ctrl_meas register
* - osrs_p field of the BME280 ctrl_meas register
*/
typedef enum {
BME280_OSRS_SKIPPED = 0,
BME280_OSRS_X1 = 1,
BME280_OSRS_X2 = 2,
BME280_OSRS_X4 = 3,
BME280_OSRS_X8 = 4,
BME280_OSRS_X16 = 5,
} bme280_osrs_t;
/**
* @brief Parameters for the BME280 sensor
*
* These parameters are needed to configure the device at startup.
*/
typedef struct {
/* I2C details */
i2c_t i2c_dev; /**< I2C device which is used */
uint8_t i2c_addr; /**< I2C address */
/* Config Register */
bme280_t_sb_t t_sb; /**< standby */
bme280_filter_t filter; /**< filter coefficient */
uint8_t spi3w_en; /**< Enables 3-wire SPI interface */
/* ctrl_meas */
bme280_mode_t run_mode; /**< ctrl_meas mode */
bme280_osrs_t temp_oversample; /**< ctrl_meas osrs_t */
bme280_osrs_t press_oversample; /**< ctrl_meas osrs_p */
/* ctrl_hum */
bme280_osrs_t humid_oversample; /**< ctrl_hum osrs_h */
} bme280_params_t;
/**
* @brief Device descriptor for the BME280 sensor
*/
typedef struct {
bme280_params_t params; /**< Device Parameters */
bme280_calibration_t calibration; /**< Calibration Data */
} bme280_t;
/**
* @brief Status and error return codes
*/
enum {
BME280_OK = 0, /**< everything was fine */
BME280_ERR_I2C = -1, /**< error initializing the I2C bus */
BME280_ERR_NODEV = -2, /**< did not detect BME280 */
BME280_ERR_NOCAL = -3, /**< could not read calibration data */
};
/**
* @brief export SAUL endpoints
* @{
*/
extern const saul_driver_t bme280_temperature_saul_driver;
extern const saul_driver_t bme280_relative_humidity_saul_driver;
extern const saul_driver_t bme280_pressure_saul_driver;
/** @} */
/**
* @brief Initialize the given BME280 device
*
* @param[out] dev Initialized device descriptor of BME280 device
* @param[in] params The parameters for the BME280 device (sampling rate, etc)
*
* @return BME280_OK on success
* @return BME280_ERR_I2C
* @return BME280_ERR_NODEV
* @return BME280_ERR_NOCAL
*/
int bme280_init(bme280_t* dev, const bme280_params_t* params);
/**
* @brief Read temperature value from the given BME280 device, returned in centi °C
*
* @param[in] dev Device descriptor of BME280 device to read from
*
* @returns The temperature in centi Celsius. In case of an error
* it returns INT16_MIN.
*/
int16_t bme280_read_temperature(bme280_t* dev);
/**
* @brief Read humidity value from the given BME280 device, returned in centi %RH
*
* @details This function should only be called after doing bme280_read_temperature
* first.
*
* @param[in] dev Device descriptor of BME280 device to read from
*
* @returns Humidity in centi %RH (i.e. the percentage times 100)
*/
uint16_t bme280_read_humidity(bme280_t *dev);
/**
* @brief Read air pressure value from the given BME280 device, returned in PA
*
* @details This function should only be called after doing bme280_read_temperature
* first.
*
* @param[in] dev Device descriptor of BME280 device to read from
*
* @returns The air pressure in Pa
*/
uint32_t bme280_read_pressure(bme280_t *dev);
#ifdef __cplusplus
}
#endif
#endif /* BME280_H */
/** @} */

4
sys/auto_init/auto_init.c

@ -294,6 +294,10 @@ void auto_init(void)
extern void auto_init_bmp180(void);
auto_init_bmp180();
#endif
#ifdef MODULE_BME280
extern void auto_init_bme280(void);
auto_init_bme280();
#endif
#ifdef MODULE_JC42
extern void auto_init_jc42(void);
auto_init_jc42();

75
sys/auto_init/saul/auto_init_bme280.c

@ -0,0 +1,75 @@
/*
* Copyright (C) 2016 Kees Bakker, SODAQ
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup auto_init_saul
* @{
*
* @file
* @brief Auto initialization of BME280 driver.
*
* @author Kees Bakker <kees@sodaq.com>
*
* @}
*/
#ifdef MODULE_BME280
#include "log.h"
#include "saul_reg.h"
#include "bme280_params.h"
#include "bme280.h"
/**
* @brief Allocation of memory for device descriptors
*/
static bme280_t bme280_devs[BME280_NUMOF];
/**
* @brief Memory for the SAUL registry entries
*/
static saul_reg_t saul_entries[BME280_NUMOF * 3];
void auto_init_bme280(void)
{
size_t se_ix = 0;
for (size_t i = 0; i < BME280_NUMOF; i++) {
LOG_DEBUG("[auto_init_saul] initializing BME280 #%u\n", i);
int res = bme280_init(&bme280_devs[i], &bme280_params[i]);
if (res < 0) {
LOG_ERROR("[auto_init_saul] error initializing BME280 #%i\n", i);
}
else {
/* temperature */
saul_entries[se_ix].dev = &bme280_devs[i];
saul_entries[se_ix].name = bme280_saul_reg_info[i][0].name;
saul_entries[se_ix].driver = &bme280_temperature_saul_driver;
saul_reg_add(&saul_entries[se_ix]);
se_ix++;
/* relative humidity */
saul_entries[se_ix].dev = &bme280_devs[i];
saul_entries[se_ix].name = bme280_saul_reg_info[i][1].name;
saul_entries[se_ix].driver = &bme280_relative_humidity_saul_driver;
saul_reg_add(&saul_entries[se_ix]);
se_ix++;
/* pressure */
saul_entries[se_ix].dev = &bme280_devs[i];
saul_entries[se_ix].name = bme280_saul_reg_info[i][2].name;
saul_entries[se_ix].driver = &bme280_pressure_saul_driver;
saul_reg_add(&saul_entries[se_ix]);
se_ix++;
}
}
}
#else
typedef int dont_be_pedantic;
#endif /* MODULE_BME280 */

7
tests/driver_bme280/Makefile

@ -0,0 +1,7 @@
APPLICATION = driver_bme280
include ../Makefile.tests_common
USEMODULE += bme280
USEMODULE += xtimer
include $(RIOTBASE)/Makefile.include

30
tests/driver_bme280/Readme.md

@ -0,0 +1,30 @@
## About
This is a test application for the BME280 Pressure, Temperature and
Humidity sensor.
## Usage
The application will initialize the BME280 device and display its
calibration coefficients. More information can be found on the
[Bosch website][1]
And in this [BST-BME280_DS001-11 datasheet] [2]
Notice that it is necessary to first read the temperature even if only one
of the other values (humidity or pressure) is needed. This is described in
the above mentioned datasheet.
After initialization, every 2 seconds, the application:
* reads and displays the temperature (d°C);
* reads and displays the pressure (Pa);
* reads and displays the humidity (%rH);
## Overruling default parameters
If your device is at a different I2C address than the default (0x77) you
can build the test as follows:
export CFLAGS=-DBME280_PARAM_I2C_ADDR=0x76
make -C tests/driver_bme280 BOARD=sodaq-autonomo
[1]: http://www.bosch-sensortec.com/en/bst/products/all_products/bme280
[2]: https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME280_DS001-11.pdf

101
tests/driver_bme280/main.c

@ -0,0 +1,101 @@
/*
* Copyright (C) 2016 Kees Bakker, SODAQ
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup tests
* @{
*
* @file
* @brief Test application for the BME280 temperature, pressure
* and humidity sensor.
*
* @author Kees Bakker <kees@sodaq.com>
*
* @}
*/
#include <stdio.h>
#include <inttypes.h>
#include "bme280_params.h"
#include "bme280.h"
#include "xtimer.h"
#define MAINLOOP_DELAY (2 * 1000 * 1000u) /* 2 seconds delay between printf's */
int main(void)
{
bme280_t dev;
int result;
puts("BME280 test application\n");
printf("+------------Initializing------------+\n");
result = bme280_init(&dev, &bme280_params[0]);
if (result == -1) {
puts("[Error] The given i2c is not enabled");
return 1;
}
if (result == -2) {
printf("[Error] The sensor did not answer correctly at address 0x%02X\n", bme280_params[0].i2c_addr);
return 1;
}
printf("Initialization successful\n\n");
printf("+------------Calibration Data------------+\n");
printf("dig_T1: %u\n", dev.calibration.dig_T1);
printf("dig_T2: %i\n", dev.calibration.dig_T2);
printf("dig_T3: %i\n", dev.calibration.dig_T3);
printf("dig_P1: %u\n", dev.calibration.dig_P1);
printf("dig_P2: %i\n", dev.calibration.dig_P2);
printf("dig_P3: %i\n", dev.calibration.dig_P3);
printf("dig_P4: %i\n", dev.calibration.dig_P4);
printf("dig_P5: %i\n", dev.calibration.dig_P5);
printf("dig_P6: %i\n", dev.calibration.dig_P6);
printf("dig_P7: %i\n", dev.calibration.dig_P7);
printf("dig_P8: %i\n", dev.calibration.dig_P8);
printf("dig_P9: %i\n", dev.calibration.dig_P9);
printf("dig_H1: %u\n", dev.calibration.dig_H1);
printf("dig_H2: %i\n", dev.calibration.dig_H2);
printf("dig_H3: %i\n", dev.calibration.dig_H3);
printf("dig_H4: %i\n", dev.calibration.dig_H4);
printf("dig_H5: %i\n", dev.calibration.dig_H5);
printf("dig_H6: %i\n", dev.calibration.dig_H6);
printf("\n+--------Starting Measurements--------+\n");
while (1) {
int16_t temperature;
uint32_t pressure;
uint16_t humidity;
/* Get temperature in centi degrees Celsius */
temperature = bme280_read_temperature(&dev);
/* Get pressure in Pa */
pressure = bme280_read_pressure(&dev);
/* Get pressure in %rH */
humidity = bme280_read_humidity(&dev);
printf("Temperature [°C]: %d.%d\n"
"Pressure [Pa]: %lu\n"
"Humidity [%%rH]: %u.%02u\n"
"\n+-------------------------------------+\n",
temperature / 100, (temperature % 100) / 10,
(unsigned long)pressure,
(unsigned int)(humidity / 100), (unsigned int)(humidity % 100));
xtimer_usleep(MAINLOOP_DELAY);
}
return 0;
}
Loading…
Cancel
Save