Monitor the growing conditions in a hothouse with simple sensors.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

477 lines
15 KiB

/*
* bme280.c
*
* Created on: Feb 26, 2023
* Author: Daniel Peter Chokola
* License: Beerware - Use this code however you'd like. If you find it
* useful you can buy me a beer some time.
*/
/* Includes */
#include "bme280.h"
#include "spi.h"
#include "util.h"
/* Definitions */
/* BME280 registers */
#define BME280_REG_HUM_LSB (0xfe)
#define BME280_REG_HUM_MSB (0xfd)
#define BME280_REG_TEMP_XLSB (0xfc)
#define BME280_REG_TEMP_LSB (0xfb)
#define BME280_REG_TEMP_MSB (0xfa)
#define BME280_REG_PRESS_XLSB (0xf9)
#define BME280_REG_PRESS_LSB (0xf8)
#define BME280_REG_PRESS_MSB (0xf7)
#define BME280_REG_CONFIG (0xf5)
#define BME280_REG_CTRL_MEAS (0xf4)
#define BME280_REG_STATUS (0xf3)
#define BME280_REG_CTRL_HUM (0xf2)
#define BME280_REG_RESET (0xe0)
#define BME280_REG_ID (0xd0) /* should return 0x60 when read (BME280) */
#define BME280_REG_CALIB26 (0xe1)
#define BME280_REG_CALIB25 (0xa1)
#define BME280_REG_CALIB00 (0x88)
/* BME280 SPI Register Masks */
#define BME280_READ_MASK (0x80)
#define BME280_WRITE_MASK (0x7f)
/* BME280 Magic Register Values */
#define BME280_RESET (0xb6)
#define BME280_ID (0x60)
#define BMP280_ID_PROD (0x58)
#define BMP280_ID_SAMP1 (0x56)
#define BMP280_ID_SAMP2 (0x57)
enum H_OSR {
H_OSR_00 = 0, /* skip */
H_OSR_01,
H_OSR_02,
H_OSR_04,
H_OSR_08,
H_OSR_16
};
enum P_OSR {
P_OSR_00 = 0, /* skip */
P_OSR_01,
P_OSR_02,
P_OSR_04,
P_OSR_08,
P_OSR_16
};
enum T_OSR {
T_OSR_00 = 0, /* skip */
T_OSR_01,
T_OSR_02,
T_OSR_04,
T_OSR_08,
T_OSR_16
};
enum IIR_COEFF {
IIR_OFF = 0, /* no filtering */
IIR_02,
IIR_04,
IIR_08,
IIR_16,
};
enum MODE {
MODE_SLEEP = 0,
MODE_FORCED1,
MODE_FORCED2,
MODE_NORMAL
};
enum STBY_TIME {
STBY_00_5ms = 0,
STBY_62_5ms,
STBY_125ms,
STBY_250ms,
STBY_500ms,
STBY_1000ms,
STBY_10ms, /* 2000 ms on BMP280 */
STBY_20ms, /* 4000 ms on BMP280 */
};
/* Data Structures */
/* Function Prototypes */
static int32_t bme280_cal_get(bme280_t *bme280);
static int32_t bme280_writebyte(bme280_t *bme280, uint8_t reg, uint8_t byte);
static int32_t bme280_readbyte(bme280_t *bme280, uint8_t reg, uint8_t *byte);
static int32_t bme280_compensate_t(bme280_t *bme280, int32_t t);
static uint32_t bme280_compensate_p(bme280_t *bme280, int32_t p_comp);
static uint32_t bme280_compensate_h(bme280_t *bme280, int32_t h);
/* Function Definitions */
int32_t bme280_init(bme280_t *bme280, SPI_HandleTypeDef *hspi, GPIO_TypeDef *port, uint16_t pin)
{
uint8_t id = 0;
return_val_if_fail(bme280, -1);
return_val_if_fail(hspi, -1);
bme280->hspi = hspi;
bme280->port = port;
bme280->pin = pin;
/*
* toggle CS line and send a dummy byte to to put the chip in SPI mode and
* let the SPI lines settle */
return_val_if_fail(bme280_writebyte(bme280, BME280_REG_RESET, 0x00) == 0, -1);
/* reset */
return_val_if_fail(bme280_writebyte(bme280, BME280_REG_RESET, BME280_RESET) == 0, -1);
HAL_Delay(1); /* give the chip a millisecond to come out of reset */
/* query chip ID */
return_val_if_fail(bme280_readbyte(bme280, BME280_REG_ID, &id) == 0, -1);
switch(id)
{
case BME280_ID:
/* BME280 */
break;
case BMP280_ID_PROD:
case BMP280_ID_SAMP1:
case BMP280_ID_SAMP2:
/* maybe support these one day */
default:
return -1; /* not a BME280 */
}
/* fetch calibration constants */
return_val_if_fail(bme280_cal_get(bme280) == 0, -1);
/*
* FIXME: don't hardcode these
* Maybe leave the chip in sleep mode here and create new function(s) to
* adjust config?
*/
/* configure humidity acquisition options */
return_val_if_fail(bme280_writebyte(bme280, BME280_REG_CTRL_HUM, H_OSR_01) == 0, -1);
/* configure temperature and pressure acquisition options and acquisition mode */
return_val_if_fail(bme280_writebyte(bme280,
BME280_REG_CTRL_MEAS,
(T_OSR_01 << 5) | (P_OSR_16 << 2) | MODE_NORMAL) == 0,
-1);
/* configure acquisition rate, IIR filtering, and SPI options */
return_val_if_fail(bme280_writebyte(bme280,
BME280_REG_CONFIG,
(STBY_00_5ms << 5) | (IIR_16 << 2) | 0) == 0,
-1);
return 0;
}
int32_t bme280_temp_get(bme280_t *bme280)
{
uint8_t reg;
uint8_t rxdata[3];
int32_t t;
HAL_StatusTypeDef res;
/* read temperature; data must be read in one transaction to guarantee consistency */
reg = BME280_REG_TEMP_MSB;
bme280->port->BSRR = bme280->pin << 16;
res = HAL_SPI_Transmit(bme280->hspi, &reg, 1, 10);
res |= HAL_SPI_Receive(bme280->hspi, &(rxdata[0]), 3, 10);
bme280->port->BSRR = bme280->pin;
return_val_if_fail(res == HAL_OK, -1);
/* assemble raw temperature */
t = ((int32_t) rxdata[0] << 12) | ((uint32_t) rxdata[1] << 4) | ((uint32_t) rxdata[2] >> 4);
return bme280_compensate_t(bme280, t);
}
int32_t bme280_press_get(bme280_t *bme280)
{
uint8_t reg;
uint8_t rxdata[3];
int32_t p;
HAL_StatusTypeDef res;
/* read pressure; data must be read in one transaction to guarantee consistency */
reg = BME280_REG_PRESS_MSB;
bme280->port->BSRR = bme280->pin << 16;
res = HAL_SPI_Transmit(bme280->hspi, &reg, 1, 10);
res |= HAL_SPI_Receive(bme280->hspi, &(rxdata[0]), 3, 10);
bme280->port->BSRR = bme280->pin;
return_val_if_fail(res == HAL_OK, -1);
/* assemble raw temperature */
p = ((int32_t) rxdata[0] << 12) | ((uint32_t) rxdata[1] << 4) | ((uint32_t) rxdata[2] >> 4);
return bme280_compensate_p(bme280, p);
}
int32_t bme280_hum_get(bme280_t *bme280)
{
uint8_t reg;
uint8_t rxdata[2];
int32_t h;
HAL_StatusTypeDef res;
/* read pressure; data must be read in one transaction to guarantee consistency */
reg = BME280_REG_HUM_MSB;
bme280->port->BSRR = bme280->pin << 16;
res = HAL_SPI_Transmit(bme280->hspi, &reg, 1, 10);
res |= HAL_SPI_Receive(bme280->hspi, &(rxdata[0]), 2, 10);
bme280->port->BSRR = bme280->pin;
return_val_if_fail(res == HAL_OK, -1);
/* assemble raw temperature */
h = ((int32_t) rxdata[0] << 8) | rxdata[1];
return bme280_compensate_h(bme280, h);
}
static int32_t bme280_cal_get(bme280_t *bme280)
{
uint8_t reg;
uint8_t calh[7]; /* humidity cal which needs some massaging */
int32_t i;
HAL_StatusTypeDef res;
for(i = 0; i < BME280_CAL_LEN; i++)
{
bme280->cal.byte[i] = 0;
}
reg = BME280_REG_CALIB00;
bme280->port->BSRR = bme280->pin << 16;
res = HAL_SPI_Transmit(bme280->hspi, &reg, 1, 10);
res |= HAL_SPI_Receive(bme280->hspi, &(bme280->cal.byte[ 0]), 24, 10);
bme280->port->BSRR = bme280->pin;
return_val_if_fail(res == HAL_OK, -1);
reg = BME280_REG_CALIB25;
bme280->port->BSRR = bme280->pin << 16;
res = HAL_SPI_Transmit(bme280->hspi, &reg, 1, 10);
res |= HAL_SPI_Receive(bme280->hspi, &(bme280->cal.byte[24]), 1, 10);
bme280->port->BSRR = bme280->pin;
return_val_if_fail(res == HAL_OK, -1);
reg = BME280_REG_CALIB26;
bme280->port->BSRR = bme280->pin << 16;
res = HAL_SPI_Transmit(bme280->hspi, &reg, 1, 10);
res |= HAL_SPI_Receive(bme280->hspi, &(calh[0]), 7, 10);
bme280->port->BSRR = bme280->pin;
return_val_if_fail(res == HAL_OK, -1);
/* massage the humidity cal into its struct */
bme280->cal.field.cal_h2 = ((int16_t) calh[1] << 8) | calh[0];
bme280->cal.field.cal_h3 = calh[2];
bme280->cal.field.cal_h4 = ((int16_t) calh[3] << 4) | (calh[4] & 0x0f);
bme280->cal.field.cal_h5 = ((int16_t) calh[5] << 4) | ((calh[4] >> 4) & 0x0f);
bme280->cal.field.cal_h6 = (int8_t) calh[6];
return 0;
}
static int32_t bme280_writebyte(bme280_t *bme280, uint8_t reg, uint8_t byte)
{
uint8_t txdata[2] = { reg & BME280_WRITE_MASK, byte };
HAL_StatusTypeDef res;
/* assert chip select */
bme280->port->BSRR = bme280->pin << 16;
res = HAL_SPI_Transmit(bme280->hspi, &(txdata[0]), 2, 10);
/* deassert chip select */
bme280->port->BSRR = bme280->pin;
return_val_if_fail(res == HAL_OK, -1);
return 0;
}
static int32_t bme280_readbyte(bme280_t *bme280, uint8_t reg, uint8_t *byte)
{
uint8_t txdata[2] = { reg | BME280_READ_MASK, 0x00 };
uint8_t rxdata[2];
HAL_StatusTypeDef res;
/* assert chip select */
bme280->port->BSRR = bme280->pin << 16;
res = HAL_SPI_TransmitReceive(bme280->hspi, &(txdata[0]), &(rxdata[0]), 2, 10);
/* deassert chip select */
bme280->port->BSRR = bme280->pin;
return_val_if_fail(res == HAL_OK, -1);
*byte = rxdata[1];
return 0;
}
/*
* Returns temperature in DegC, resolution is 0.01 DegC. Output value of
* "5123" equals 51.23 DegC.
*/
static int32_t bme280_compensate_t(bme280_t *bme280, int32_t t)
{
int32_t var1;
int32_t var2;
int32_t t_comp;
var1 = ((((t >> 3) - ((int32_t) bme280->cal.field.cal_t1 << 1))) * ((int32_t) bme280->cal.field.cal_t2)) >> 11;
var2 = (((((t >> 4) - ((int32_t) bme280->cal.field.cal_t1)) * ((t >> 4) - ((int32_t) bme280->cal.field.cal_t1))) >> 12) * ((int32_t) bme280->cal.field.cal_t3)) >> 14;
bme280->t_fine = var1 + var2;
t_comp = (bme280->t_fine * 5 + 128) >> 8;
return t_comp;
}
/*
* Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24
* integer bits and 8 fractional bits).
* Output value of "24674867" represents 24674867/256 = 96386.2 Pa =
* 963.862 hPa
*/
static uint32_t bme280_compensate_p(bme280_t *bme280, int32_t p)
{
int64_t var1;
int64_t var2;
int64_t p_comp;
var1 = ((int64_t) bme280->t_fine) - 128000;
var2 = var1 * var1 * (int64_t) bme280->cal.field.cal_p6;
var2 = var2 + ((var1 * (int64_t) bme280->cal.field.cal_p5) << 17);
var2 = var2 + (((int64_t) bme280->cal.field.cal_p4) << 35);
var1 = ((var1 * var1 * (int64_t) bme280->cal.field.cal_p3) >> 8) + ((var1 * (int64_t) bme280->cal.field.cal_p2) << 12);
var1 = (((((int64_t) 1) << 47) + var1)) * ((int64_t) bme280->cal.field.cal_p1) >> 33;
if(var1 == 0)
{
/* division by zero */
return 0;
}
p_comp = 1048576 - p;
p_comp = (((p_comp << 31) - var2) * 3125) / var1;
var1 = (((int64_t) bme280->cal.field.cal_p9) * (p_comp >> 13) * (p_comp >> 13)) >> 25;
var2 = (((int64_t) bme280->cal.field.cal_p8) * p_comp) >> 19;
p_comp = ((p_comp + var1 + var2) >> 8) + (((int64_t) bme280->cal.field.cal_p7) << 4);
return (uint32_t) p_comp;
}
/*
* Returns humidity in %RH as unsigned 32 bit integer in Q22.10 format (22
* integer and 10 fractional bits).
* Output value of “47445” represents 47445/1024 = 46.333 %RH
*/
static uint32_t bme280_compensate_h(bme280_t *bme280, int32_t h)
{
int32_t h_comp;
h_comp = (bme280->t_fine - ((int32_t) 76800));
h_comp = (((((h << 14) - (((int32_t) bme280->cal.field.cal_h4) << 20) - (((int32_t) bme280->cal.field.cal_h5) *
h_comp)) + ((int32_t) 16384)) >> 15) * (((((((h_comp *
((int32_t) bme280->cal.field.cal_h6)) >> 10) * (((h_comp * ((int32_t) bme280->cal.field.cal_h3)) >> 11) +
((int32_t) 32768))) >> 10) + ((int32_t) 2097152)) * ((int32_t) bme280->cal.field.cal_h2) +
8192) >> 14));
h_comp = (h_comp - (((((h_comp >> 15) * (h_comp >> 15)) >> 7) *
((int32_t) bme280->cal.field.cal_h1)) >> 4));
h_comp = (h_comp < 0 ? 0 : h_comp);
h_comp = (h_comp > 419430400 ? 419430400 : h_comp);
return (uint32_t) (h_comp >> 12);
}
#if (0) /* crusty old code */
/* Global Variables */
/* Specify BME280 configuration */
uint8_t Posr = P_OSR_16;
uint8_t Tosr = T_OSR_02;
uint8_t Mode = normal;
uint8_t IIRFilter = BW0_042ODR;
uint8_t SBy = t_62_5ms; // set pressure and temperature output data rate
/* t_fine carries fine temperature as global value for BME280 */
int32_t t_fine;
/* BME280 compensation parameters */
uint16_t dig_T1, dig_P1;
int16_t dig_T2, dig_T3, dig_P2, dig_P3, dig_P4, dig_P5, dig_P6, dig_P7, dig_P8, dig_P9;
double Temperature, Pressure; // stores BME280 pressures sensor pressure and temperature
int32_t rawPress, rawTemp; // pressure and temperature raw count output for BME280
/* Function Definitions */
int32_t readBME280Temperature()
{
uint8_t rawData[3]; // 20-bit pressure register data stored here
HAL_I2C_Mem_Read(&hi2c1, BME280_ADDRESS, BME280_TEMP_MSB, 3, &rawData[0], 3, 10);
return (int32_t) (((int32_t) rawData[0] << 16 | (int32_t) rawData[1] << 8 | rawData[2]) >> 4);
}
int32_t readBME280Pressure()
{
uint8_t rawData[3]; // 20-bit pressure register data stored here
HAL_I2C_Mem_Read(&hi2c1, BME280_ADDRESS, BME280_PRESS_MSB, 3, &rawData[0], 3, 10);
return (int32_t) (((int32_t) rawData[0] << 16 | (int32_t) rawData[1] << 8 | rawData[2]) >> 4);
}
void BME280Init()
{
uint8_t TPosrMode = Tosr << 5 | Posr << 2 | Mode;
uint8_t SByTimeIntervalModeAndBandwidth = SBy << 5 | IIRFilter << 2;
uint8_t calib[24];
/* Configure the BME280 */
/* Set T and P oversampling rates and sensor mode */
HAL_I2C_Mem_Write(&hi2c1, BME280_ADDRESS, BME280_CTRL_MEAS, 1, &TPosrMode, 1, 10);
/* Set standby time interval in normal mode and bandwidth */
HAL_I2C_Mem_Write(&hi2c1, BME280_ADDRESS, BME280_CONFIG, 1, &SByTimeIntervalModeAndBandwidth, 1, 10);
/* Read and store calibration data */
HAL_I2C_Mem_Read(&hi2c1, BME280_ADDRESS, BME280_CALIB00, 24, &calib[0], 24, 10);
dig_T1 = (uint16_t) (((uint16_t) calib[ 1] << 8) | calib[ 0]);
dig_T2 = ( int16_t) ((( int16_t) calib[ 3] << 8) | calib[ 2]);
dig_T3 = ( int16_t) ((( int16_t) calib[ 5] << 8) | calib[ 4]);
dig_P1 = (uint16_t) (((uint16_t) calib[ 7] << 8) | calib[ 6]);
dig_P2 = ( int16_t) ((( int16_t) calib[ 9] << 8) | calib[ 8]);
dig_P3 = ( int16_t) ((( int16_t) calib[11] << 8) | calib[10]);
dig_P4 = ( int16_t) ((( int16_t) calib[13] << 8) | calib[12]);
dig_P5 = ( int16_t) ((( int16_t) calib[15] << 8) | calib[14]);
dig_P6 = ( int16_t) ((( int16_t) calib[17] << 8) | calib[16]);
dig_P7 = ( int16_t) ((( int16_t) calib[19] << 8) | calib[18]);
dig_P8 = ( int16_t) ((( int16_t) calib[21] << 8) | calib[20]);
dig_P9 = ( int16_t) ((( int16_t) calib[23] << 8) | calib[22]);
}
/*
* Returns temperature in DegC, resolution is 0.01 DegC. Output value of
* "5123" equals 51.23 DegC.
*/
int32_t bmp280_compensate_T(int32_t adc_T)
{
int32_t var1, var2, T;
var1 = ((((adc_T >> 3) - ((int32_t) dig_T1 << 1))) * ((int32_t) dig_T2)) >> 11;
var2 = (((((adc_T >> 4) - ((int32_t) dig_T1)) * ((adc_T >> 4) - ((int32_t) dig_T1))) >> 12) * ((int32_t) dig_T3)) >> 14;
t_fine = var1 + var2;
T = (t_fine * 5 + 128) >> 8;
return T;
}
/*
* Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24
* integer bits and 8 fractional bits).
* Output value of "24674867" represents 24674867/256 = 96386.2 Pa =
* 963.862 hPa
*/
uint32_t bmp280_compensate_P(int32_t adc_P)
{
uint64_t var1, var2, p;
var1 = ((uint64_t) t_fine) - 128000;
var2 = var1 * var1 * (uint64_t) dig_P6;
var2 = var2 + ((var1 * (uint64_t) dig_P5) << 17);
var2 = var2 + (((uint64_t) dig_P4) << 35);
var1 = ((var1 * var1 * (uint64_t) dig_P3) >> 8) + ((var1 * (uint64_t) dig_P2) << 12);
var1 = (((((uint64_t) 1) << 47) + var1)) * ((uint64_t) dig_P1) >> 33;
if(var1 == 0)
{
/* division by zero */
return 0;
}
p = 1048576 - adc_P;
p = (((p << 31) - var2) * 3125) / var1;
var1 = (((uint64_t) dig_P9) * (p >> 13) * (p >> 13)) >> 25;
var2 = (((uint64_t) dig_P8) * p)>> 19;
p = ((p + var1 + var2) >> 8) + (((uint64_t) dig_P7) << 4);
return (uint32_t) p;
}
#endif /* crusty old code */