From 6bd0ec4480534531b83d5462cde8f59d597bdf56 Mon Sep 17 00:00:00 2001 From: vsky Date: Sun, 7 Feb 2016 14:26:43 +0100 Subject: [PATCH] BME280/BMP280 Digital Pressure Sensor module --- app/include/user_modules.h | 1 + app/modules/bme280.c | 440 +++++++++++++++++++++++++++++++++++++ docs/en/modules/bme280.md | 225 +++++++++++++++++++ 3 files changed, 666 insertions(+) create mode 100644 app/modules/bme280.c create mode 100644 docs/en/modules/bme280.md diff --git a/app/include/user_modules.h b/app/include/user_modules.h index 27c4d4c1..14b529d8 100644 --- a/app/include/user_modules.h +++ b/app/include/user_modules.h @@ -17,6 +17,7 @@ //#define LUA_USE_MODULES_APA102 #define LUA_USE_MODULES_BIT //#define LUA_USE_MODULES_BMP085 +//#define LUA_USE_MODULES_BME280 #define LUA_USE_MODULES_CJSON #define LUA_USE_MODULES_COAP #define LUA_USE_MODULES_CRYPTO diff --git a/app/modules/bme280.c b/app/modules/bme280.c new file mode 100644 index 00000000..edd4f1a0 --- /dev/null +++ b/app/modules/bme280.c @@ -0,0 +1,440 @@ +// *************************************************************************** +// BMP280 module for ESP8266 with nodeMCU +// +// Written by Lukas Voborsky, @voborsky +// +// MIT license, http://opensource.org/licenses/MIT +// *************************************************************************** + +//#define NODE_DEBUG + +#include "module.h" +#include "lauxlib.h" +#include "platform.h" +#include "c_math.h" + +/****************************************************/ +/**\name registers definition */ +/***************************************************/ +#define BME280_REGISTER_CONTROL (0xF4) +#define BME280_REGISTER_CONTROL_HUM (0xF2) +#define BME280_REGISTER_CONFIG (0xF5) +#define BME280_REGISTER_CHIPID (0xD0) +#define BME280_REGISTER_VERSION (0xD1) +#define BME280_REGISTER_SOFTRESET (0xE0) +#define BME280_REGISTER_CAL26 (0xE1) +#define BME280_REGISTER_TEMP (0xFA) +#define BME280_REGISTER_PRESS (0xF7) +#define BME280_REGISTER_HUM (0xFD) + +#define BME280_REGISTER_DIG_T (0x88) +#define BME280_REGISTER_DIG_P (0x8E) +#define BME280_REGISTER_DIG_H1 (0xA1) +#define BME280_REGISTER_DIG_H2 (0xE1) +/****************************************************/ +/**\name I2C ADDRESS DEFINITIONS */ +/***************************************************/ +#define BME280_I2C_ADDRESS1 (0x76) +#define BME280_I2C_ADDRESS2 (0x77) +/****************************************************/ +/**\name POWER MODE DEFINITIONS */ +/***************************************************/ +/* Sensor Specific constants */ +#define BME280_SLEEP_MODE (0x00) +#define BME280_FORCED_MODE (0x01) +#define BME280_NORMAL_MODE (0x03) +#define BME280_SOFT_RESET_CODE (0xB6) +/****************************************************/ +/**\name OVER SAMPLING DEFINITIONS */ +/***************************************************/ +#define BME280_OVERSAMP_1X (0x01) +#define BME280_OVERSAMP_2X (0x02) +#define BME280_OVERSAMP_4X (0x03) +#define BME280_OVERSAMP_8X (0x04) +#define BME280_OVERSAMP_16X (0x05) +/****************************************************/ +/**\name STANDBY DEFINITIONS */ +/***************************************************/ +#define BME280_STANDBY_TIME_1_MS (0x00) +#define BME280_STANDBY_TIME_63_MS (0x01) +#define BME280_STANDBY_TIME_125_MS (0x02) +#define BME280_STANDBY_TIME_250_MS (0x03) +#define BME280_STANDBY_TIME_500_MS (0x04) +#define BME280_STANDBY_TIME_1000_MS (0x05) +#define BME280_STANDBY_TIME_10_MS (0x06) +#define BME280_STANDBY_TIME_20_MS (0x07) +/****************************************************/ +/**\name FILTER DEFINITIONS */ +/***************************************************/ +#define BME280_FILTER_COEFF_OFF (0x00) +#define BME280_FILTER_COEFF_2 (0x01) +#define BME280_FILTER_COEFF_4 (0x02) +#define BME280_FILTER_COEFF_8 (0x03) +#define BME280_FILTER_COEFF_16 (0x04) +/****************************************************/ +/**\data type definition */ +/***************************************************/ +#define BME280_S32_t int32_t +#define BME280_U32_t uint32_t +#define BME280_S64_t int64_t + +#define BME280_SAMPLING_DELAY 113 //maximum measurement time in ms for maximum oversampling for all measures = 1.25 + 2.3*16 + 2.3*16 + 0.575 + 2.3*16 + 0.575 ms + +#define r16s(reg) ((int16_t)r16u(reg)) +#define r16sLE(reg) ((int16_t)r16uLE(reg)) + +#define bme280_adc_T(void) r24u(BME280_REGISTER_TEMP) +#define bme280_adc_P(void) r24u(BME280_REGISTER_PRESS) +#define bme280_adc_H(void) r16u(BME280_REGISTER_HUM) + +static const uint32_t bme280_i2c_id = 0; + +static uint8_t bme280_i2c_addr = BME280_I2C_ADDRESS1; +static uint8_t bme280_isbme = 0; // 1 if the chip is BME280, 0 for BMP280 +static uint8_t bme280_mode = 0; // stores oversampling settings +os_timer_t bme280_timer; // timer for forced mode readout +int lua_connected_readout_ref; // callback when readout is ready + +static 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; + uint8_t dig_H1; + int16_t dig_H2; + uint8_t dig_H3; + int16_t dig_H4; + int16_t dig_H5; + int8_t dig_H6; +} bme280_data; + +static BME280_S32_t bme280_t_fine; +static uint32_t bme280_h = 0; +static double bme280_hc = 0.0; + +static uint8_t r8u(uint8_t reg) { + uint8_t ret; + + platform_i2c_send_start(bme280_i2c_id); + platform_i2c_send_address(bme280_i2c_id, bme280_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER); + platform_i2c_send_byte(bme280_i2c_id, reg); + platform_i2c_send_stop(bme280_i2c_id); + platform_i2c_send_start(bme280_i2c_id); + platform_i2c_send_address(bme280_i2c_id, bme280_i2c_addr, PLATFORM_I2C_DIRECTION_RECEIVER); + ret = platform_i2c_recv_byte(bme280_i2c_id, 0); + platform_i2c_send_stop(bme280_i2c_id); + //NODE_DBG("reg:%x, value:%x \n", reg, ret); + return ret; +} + +static uint8_t w8u(uint8_t reg, uint8_t val) { + platform_i2c_send_start(bme280_i2c_id); + platform_i2c_send_address(bme280_i2c_id, bme280_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER); + platform_i2c_send_byte(bme280_i2c_id, reg); + platform_i2c_send_byte(bme280_i2c_id, val); + platform_i2c_send_stop(bme280_i2c_id); +} + +static uint16_t r16u(uint8_t reg) { + uint8_t high = r8u(reg); + uint8_t low = r8u(++reg); + return (high << 8) | low; +} + +static uint16_t r16uLE(uint8_t reg) { + uint8_t low = r8u(reg); + uint8_t high = r8u(++reg); + return (high << 8) | low; +} + +static uint32_t r24u(uint8_t reg) { + uint8_t high = r8u(reg); + uint8_t mid = r8u(++reg); + uint8_t low = r8u(++reg); + return (uint32_t)(((high << 16) | (mid << 8) | low) >> 4); +} + +// Returns temperature in DegC, resolution is 0.01 DegC. Output value of “5123” equals 51.23 DegC. +// t_fine carries fine temperature as global value +static BME280_S32_t bme280_compensate_T(BME280_S32_t adc_T) { + BME280_S32_t var1, var2, T; + var1 = ((((adc_T>>3) - ((BME280_S32_t)bme280_data.dig_T1<<1))) * ((BME280_S32_t)bme280_data.dig_T2)) >> 11; + var2 = (((((adc_T>>4) - ((BME280_S32_t)bme280_data.dig_T1)) * ((adc_T>>4) - ((BME280_S32_t)bme280_data.dig_T1))) >> 12) * + ((BME280_S32_t)bme280_data.dig_T3)) >> 14; + bme280_t_fine = var1 + var2; + T = (bme280_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 +static BME280_U32_t bme280_compensate_P(BME280_S32_t adc_P) { + BME280_S64_t var1, var2, p; + var1 = ((BME280_S64_t)bme280_t_fine) - 128000; + var2 = var1 * var1 * (BME280_S64_t)bme280_data.dig_P6; + var2 = var2 + ((var1*(BME280_S64_t)bme280_data.dig_P5)<<17); + var2 = var2 + (((BME280_S64_t)bme280_data.dig_P4)<<35); + var1 = ((var1 * var1 * (BME280_S64_t)bme280_data.dig_P3)>>8) + ((var1 * (BME280_S64_t)bme280_data.dig_P2)<<12); + var1 = (((((BME280_S64_t)1)<<47)+var1))*((BME280_S64_t)bme280_data.dig_P1)>>33; + if (var1 == 0) { + return 0; // avoid exception caused by division by zero + } + p = 1048576-adc_P; + p = (((p<<31)-var2)*3125)/var1; + var1 = (((BME280_S64_t)bme280_data.dig_P9) * (p>>13) * (p>>13)) >> 25; + var2 = (((BME280_S64_t)bme280_data.dig_P8) * p) >> 19; + p = ((p + var1 + var2) >> 8) + (((BME280_S64_t)bme280_data.dig_P7)<<4); + p = (p * 10) >> 8; + return (BME280_U32_t)p; +} + +// 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 BME280_U32_t bme280_compensate_H(BME280_S32_t adc_H) { + BME280_S32_t v_x1_u32r; + + v_x1_u32r = (bme280_t_fine - ((BME280_S32_t)76800)); + v_x1_u32r = (((((adc_H << 14) - (((BME280_S32_t)bme280_data.dig_H4) << 20) - (((BME280_S32_t)bme280_data.dig_H5) * v_x1_u32r)) + + ((BME280_S32_t)16384)) >> 15) * (((((((v_x1_u32r * ((BME280_S32_t)bme280_data.dig_H6)) >> 10) * (((v_x1_u32r * + ((BME280_S32_t)bme280_data.dig_H3)) >> 11) + ((BME280_S32_t)32768))) >> 10) + ((BME280_S32_t)2097152)) * + ((BME280_S32_t)bme280_data.dig_H2) + 8192) >> 14)); + v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * ((BME280_S32_t)bme280_data.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); + v_x1_u32r = v_x1_u32r>>12; + return (BME280_U32_t)((v_x1_u32r * 1000)>>10); +} + +static int bme280_lua_init(lua_State* L) { + uint8_t sda; + uint8_t scl; + uint8_t ossh; + uint8_t config; + uint8_t ack; + + uint8_t const bit3 = 0b111; + uint8_t const bit2 = 0b11; + + if (!lua_isnumber(L, 1) || !lua_isnumber(L, 2)) { + return luaL_error(L, "wrong arg range"); + } + sda = luaL_checkinteger(L, 1); + scl = luaL_checkinteger(L, 2); + + bme280_mode = (!lua_isnumber(L, 6)?BME280_NORMAL_MODE:(luaL_checkinteger(L, 6)&bit2)) // 6-th parameter: power mode + | ((!lua_isnumber(L, 4)?BME280_OVERSAMP_16X:(luaL_checkinteger(L, 4)&bit3)) << 2) // 4-th parameter: pressure oversampling + | ((!lua_isnumber(L, 3)?BME280_OVERSAMP_16X:(luaL_checkinteger(L, 3)&bit3)) << 5); // 3-rd parameter: temperature oversampling + + ossh = (!lua_isnumber(L, 5))?BME280_OVERSAMP_16X:(luaL_checkinteger(L, 5)&bit3); // 5-th parameter: humidity oversampling + + config = ((!lua_isnumber(L, 7)?BME280_STANDBY_TIME_20_MS:(luaL_checkinteger(L, 7)&bit3))<< 4) // 7-th parameter: inactive duration in normal mode + | ((!lua_isnumber(L, 8)?BME280_FILTER_COEFF_16:(luaL_checkinteger(L, 8)&bit3)) << 1); // 8-th parameter: IIR filter + NODE_DBG("mode: %x\nhumidity oss: %x\nconfig: %x\n", bme280_mode, ossh, config); + + platform_i2c_setup(bme280_i2c_id, sda, scl, PLATFORM_I2C_SPEED_SLOW); + + bme280_i2c_addr = BME280_I2C_ADDRESS1; + platform_i2c_send_start(bme280_i2c_id); + ack = platform_i2c_send_address(bme280_i2c_id, bme280_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER); + platform_i2c_send_stop(bme280_i2c_id); + if (!ack) { + NODE_DBG("No ACK on address: %x\n", bme280_i2c_addr); + bme280_i2c_addr = BME280_I2C_ADDRESS2; + platform_i2c_send_start(bme280_i2c_id); + ack = platform_i2c_send_address(bme280_i2c_id, bme280_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER); + platform_i2c_send_stop(bme280_i2c_id); + if (!ack) { + NODE_DBG("No ACK on address: %x\n", bme280_i2c_addr); + return 0; + } + } + + uint8_t chipid = r8u(BME280_REGISTER_CHIPID); + NODE_DBG("chip_id: %x\n", chipid); + bme280_isbme = (chipid == 0x60); + + uint8_t reg = BME280_REGISTER_DIG_T; + bme280_data.dig_T1 = r16uLE(reg); reg+=2; + bme280_data.dig_T2 = r16sLE(reg); reg+=2; + bme280_data.dig_T3 = r16sLE(reg); + //NODE_DBG("dig_T: %d\t%d\t%d\n", bme280_data.dig_T1, bme280_data.dig_T2, bme280_data.dig_T3); + + reg = BME280_REGISTER_DIG_P; + bme280_data.dig_P1 = r16uLE(reg); reg+=2; + bme280_data.dig_P2 = r16sLE(reg); reg+=2; + bme280_data.dig_P3 = r16sLE(reg); reg+=2; + bme280_data.dig_P4 = r16sLE(reg); reg+=2; + bme280_data.dig_P5 = r16sLE(reg); reg+=2; + bme280_data.dig_P6 = r16sLE(reg); reg+=2; + bme280_data.dig_P7 = r16sLE(reg); reg+=2; + bme280_data.dig_P8 = r16sLE(reg); reg+=2; + bme280_data.dig_P9 = r16sLE(reg); + // NODE_DBG("dig_P: %d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", bme280_data.dig_P1, bme280_data.dig_P2, bme280_data.dig_P3, bme280_data.dig_P4, bme280_data.dig_P5, bme280_data.dig_P6, bme280_data.dig_P7, bme280_data.dig_P8, bme280_data.dig_P9); + + w8u(BME280_REGISTER_CONFIG, config); + if (bme280_isbme) { + reg = BME280_REGISTER_DIG_H1; + bme280_data.dig_H1 = r8u(reg); + reg = BME280_REGISTER_DIG_H2; + bme280_data.dig_H2 = r16sLE(reg); reg+=2; + bme280_data.dig_H3 = r8u(reg); reg++; + bme280_data.dig_H4 = ((int16_t)r8u(reg) << 4 | (r8u(reg+1) & 0xF)); reg+=2; + bme280_data.dig_H5 = ((int16_t)r8u(reg+1) << 4 | (r8u(reg) >> 4)); reg+=2; + bme280_data.dig_H6 = (int8_t)r8u(reg); + // NODE_DBG("dig_H: %d\t%d\t%d\t%d\t%d\t%d\n", bme280_data.dig_H1, bme280_data.dig_H2, bme280_data.dig_H3, bme280_data.dig_H4, bme280_data.dig_H5, bme280_data.dig_H6); + + w8u(BME280_REGISTER_CONTROL_HUM, ossh); + lua_pushinteger(L, 2); + } else { + lua_pushinteger(L, 1); + } + w8u(BME280_REGISTER_CONTROL, bme280_mode); + + return 1; +} + +static void bme280_readoutdone (void *arg) +{ + NODE_DBG("timer out\n"); + lua_State *L = arg; + lua_rawgeti (L, LUA_REGISTRYINDEX, lua_connected_readout_ref); + lua_call (L, 0, 0); + luaL_unref (L, LUA_REGISTRYINDEX, lua_connected_readout_ref); + os_timer_disarm (&bme280_timer); +} + +static int bme280_lua_startreadout(lua_State* L) { + uint32_t delay; + + if (lua_isnumber(L, 1)) { + delay = luaL_checkinteger(L, 1); + if (!delay) {delay = BME280_SAMPLING_DELAY;} // if delay is 0 then set the default delay + } + + if (!lua_isnoneornil(L, 2)) { + lua_pushvalue(L, 2); + lua_connected_readout_ref = luaL_ref(L, LUA_REGISTRYINDEX); + } else { + lua_connected_readout_ref = LUA_NOREF; + } + + w8u(BME280_REGISTER_CONTROL, (bme280_mode & 0xFC) | BME280_FORCED_MODE); + NODE_DBG("control old: %x, control: %x, delay: %d\n", bme280_mode, (bme280_mode & 0xFC) | BME280_FORCED_MODE, delay); + + if (lua_connected_readout_ref != LUA_NOREF) { + NODE_DBG("timer armed\n"); + os_timer_disarm (&bme280_timer); + os_timer_setfn (&bme280_timer, (os_timer_func_t *)bme280_readoutdone, L); + os_timer_arm (&bme280_timer, delay, 0); // trigger callback when readout is ready + } + return 0; +} + +static int bme280_lua_temp(lua_State* L) { + uint32_t adc_T = bme280_adc_T(); + if (adc_T ==0x80000) return 0; + lua_pushinteger(L, bme280_compensate_T(adc_T)); + lua_pushinteger(L, bme280_t_fine); + return 2; +} + +static int bme280_lua_baro(lua_State* L) { + uint32_t adc_T = bme280_adc_T(); + uint32_t T = bme280_compensate_T(adc_T); + uint32_t adc_P = bme280_adc_P(); + if (adc_P ==0x80000 || adc_T == 0x80000) + return 0; + lua_pushinteger(L, bme280_compensate_P(adc_P)); + lua_pushinteger(L, T); + return 2; +} + +static int bme280_lua_humi(lua_State* L) { + if (!bme280_isbme) return 0; + uint32_t adc_T = bme280_adc_T(); + uint32_t T = bme280_compensate_T(adc_T); + uint32_t adc_H = bme280_adc_H(); + if (adc_T == 0x80000) + return 0; + lua_pushinteger(L, bme280_compensate_H(adc_H)); + lua_pushinteger(L, T); + return 2; +} + +static int bme280_lua_qfe2qnh(lua_State* L) { + if (!lua_isnumber(L, 2)) { + return luaL_error(L, "wrong arg range"); + } + int32_t qfe = luaL_checkinteger(L, 1); + int32_t h = luaL_checkinteger(L, 2); + + double hc; + if (bme280_h == h) { + hc = bme280_hc; + } else { + hc = pow((double)(1.0 - 2.25577e-5 * h), (double)(-5.25588)); + bme280_hc = hc; bme280_h = h; + } + double qnh = (double)qfe * hc; + lua_pushinteger(L, (int32_t)(qnh + 0.5)); + return 1; +} + +static int bme280_lua_altitude(lua_State* L) { + if (!lua_isnumber(L, 2)) { + return luaL_error(L, "wrong arg range"); + } + int32_t P = luaL_checkinteger(L, 1); + int32_t qnh = luaL_checkinteger(L, 2); + + double h = (1.0 - pow((double)P/(double)qnh, 1.0/5.25588)) / 2.25577e-5 * 100.0; + lua_pushinteger(L, (int32_t)(h + (((h<0)?-1:(h>0)) * 0.5))); + return 1; +} + +static double ln(double x) { + double y = (x-1)/(x+1); + double y2 = y*y; + double r = 0; + for (int8_t i=33; i>0; i-=2) { //we've got the power + r = 1.0/(double)i + y2 * r; + } + return 2*y*r; +} + +static int bme280_lua_dewpoint(lua_State* L) { + const double c243 = 243.5; + const double c17 = 17.67; + if (!lua_isnumber(L, 2)) { + return luaL_error(L, "wrong arg range"); + } + double H = luaL_checkinteger(L, 1)/100000.0; + double T = luaL_checkinteger(L, 2)/100.0; + + double c = ln(H) + ((c17 * T) / (c243 + T)); + double d = (c243 * c)/(c17 - c) * 100.0; + lua_pushinteger(L, (int32_t)(d + (((d<0)?-1:(d>0)) * 0.5))); + return 1; +} + +static const LUA_REG_TYPE bme280_map[] = { + { LSTRKEY( "init" ), LFUNCVAL(bme280_lua_init)}, + { LSTRKEY( "temp" ), LFUNCVAL(bme280_lua_temp)}, + { LSTRKEY( "baro" ), LFUNCVAL(bme280_lua_baro)}, + { LSTRKEY( "humi" ), LFUNCVAL(bme280_lua_humi)}, + { LSTRKEY( "startreadout" ), LFUNCVAL(bme280_lua_startreadout)}, + { LSTRKEY( "qfe2qnh" ), LFUNCVAL(bme280_lua_qfe2qnh)}, + { LSTRKEY( "altitude" ), LFUNCVAL(bme280_lua_altitude)}, + { LSTRKEY( "dewpoint" ), LFUNCVAL(bme280_lua_dewpoint)}, + { LNILKEY, LNILVAL} +}; + +NODEMCU_MODULE(BME280, "bme280", bme280_map, NULL); diff --git a/docs/en/modules/bme280.md b/docs/en/modules/bme280.md new file mode 100644 index 00000000..9e81f7d5 --- /dev/null +++ b/docs/en/modules/bme280.md @@ -0,0 +1,225 @@ +# bme280 module + +The bme280 module provides simple interface to BME280/BMP280 temperature/air presssure/humidity sensor. + +|Method|Description| +|---------------|-------------| +|[init()](#init)|Initializes the module and sets the pin configuration.| +|[temp()](#temp)|Reads the sensor and returns the temperature in celsius as an integer multiplied with 100.| +|[baro()](#baro)|Reads the sensor and returns the air temperature in hectopascals as an integer multiplied with 1000.| +|[humi()](#humi)|Reads the sensor and returns the air relative humidity in percents as an integer multiplied with 100.| +|[startreadout()](#startreadout)|Starts readout (turns the sensor into forced mode).| +|[qfe2qnh()](#qfe2qnh)|For given altitude converts the air pressure to sea level air pressure.| +|[altitude()](#altitude)|For given air pressure and sea level air pressure returns the altitude in meters as an integer multiplied with 100.| +|[dewpoint()](#dewpoint)|For given temperature and relative humidity returns the dew point in celsius as an integer multiplied with 100.| + +## Methods + +###init() + +####Description +Initialize module. Initialization is mandatory before read values. + +####Syntax +`init(sda, scl)` + +`init(sda, scl, temp_oss, press_oss, humi_oss, power_mode, inactive_duration, IIR_filter)` + +####Parameters +* `sda` - SDA pin +* `scl` - SCL pin +* (optional) `temp_oss` - Controls oversampling of temperature data. Default oversampling is 16x. +* (optional) `press_oss` - Controls oversampling of pressure data. Default oversampling is 16x. +* (optional) `humi_oss` - Controls oversampling of humidity data. Default oversampling is 16x +* (optional) `sensor_mode` - Controls the sensor mode of the device. Default sensor more is normal. +* (optional) `inactive_duration` - Controls inactive duration in normal mode. Default inactive duration is 20ms. +* (optional) `IIR_filter` - Controls the time constant of the IIR filter. Default fitler coefficient is 16. + +|`temp_oss`, `press_oss`, `humi_oss`|Data oversampling| +|-----|-----------------| +|0|Skipped (output set to 0x80000)| +|1|oversampling ×1| +|2|oversampling ×2| +|3|oversampling ×4| +|4|oversampling ×8| +|**5**|**oversampling ×16**| + +|`sensor_mode`|Sensor mode| +|-----|-----------------| +|0|Sleep mode| +|1 and 2|Forced mode| +|**3**|**Normal mode**| + +Using forced mode is recommended for applications which require low sampling rate or hostbased synchronization. The sensor enters into sleep mode after a forced readout. Please refer to BME280 Final Datasheet for more details. + +|`inactive_duration`|t standby (ms)| +|-----|-----------------| +|0|0.5| +|1|62.5| +|2|125| +|3|250| +|4|500| +|5|1000| +|6|10| +|**7**|**20**| + +|`IIR_filter`|Filter coefficient | +|-----|-----------------| +|0|Filter off| +|1|2| +|2|4| +|3|8| +|**4**|**16**| + +####Returns +`nil` initialization has failed (no sensor connected?), `2` sensor is BME280, `1` sensor is BMP280 + +### Example +```lua +alt=320 -- altitude of the measurement place + +bme280.init(3, 4) + +P, T = bme280.baro() +print(string.format("QFE=%d.%03d", P/1000, P%1000)) + +-- convert measure air pressure to sea level pressure +QNH = bme280.qfe2qnh(P, alt) +print(string.format("QNH=%d.%03d", QNH/1000, QNH%1000)) + +H, T = bme280.humi() +print(string.format("T=%d.%02d", T/100, T%100)) +print(string.format("humidity=%d.%03d%%", H/1000, H%1000)) +D = bme280.dewpoint(H, T) +print(string.format("dew_point=%d.%02d", D/100, D%100)) + +-- altimeter function - calculate altitude based on current sea level pressure (QNH) and measure pressure +P = bme280.baro() +curAlt = bme280.altitude(P, QNH) +print(string.format("altitude=%d.%02d", curAlt/100, curAlt%100)) +``` +Use `bme280.init(sda, scl, 1, 3, 0, 3, 0, 4)` for "game mode" - Oversampling settings pressure ×4, temperature ×1, humidity ×0, sensor mode: normal mode, inactive duration = 0.5 ms, IIR filter settings filter coefficient 16. + +Example of readout in forced mode (asynchronous) +```lua +bme280.init(3, 4, nil, nil, nil, 0) -- initialize to sleep mode +bme280.startreadout(0, function () + T = bme280.temp() + print(string.format("T=%d.%02d", T/100, T%100)) +end) +``` + +**-** [Back to index](#index) + +###temp() +####Description +Reads the sensor and returns the temperature in celsius as an integer multiplied with 100. + +####Syntax +`temp()` + +####Parameters +none + +####Returns +* `T` - temperature in celsius as an integer multiplied with 100 or `nil` when readout is not successful. +* `t_fine` - temperature measure used in pressure and humidity compensation formulas (generally no need to use this value) + +**-** [Back to index](#index) + +###baro() +####Description +Reads the sensor and returns the air temperature in hectopascals as an integer multiplied with 1000 or `nil` when readout is not successful. +Current temperature is needed to calculate the air pressure so temperature reading is performed prior reading pressure data. Second returned variable is therefore current temperature. + +####Syntax +`baro()` + +####Parameters +none + +####Returns +* `P` - air pressure in hectopascals multiplied by 1000. +* `T` - temperature in celsius as an integer multiplied with 100. + +**-** [Back to index](#index) + +###humi() +####Description +Reads the sensor and returns the air relative humidity in percents as an integer multiplied with 100 or `nil` when readout is not successful. +Current temperature is needed to calculate the relative humidity so temperature reading is performed prior reading pressure data. Second returned variable is therefore current temperature. + +####Syntax +`humi()` + +####Parameters +none + +####Returns +* `H` - last relative humidity reading in % times 1000. +* `T` - temperature in celsius as an integer multiplied with 100. + +**-** [Back to index](#index) + +###startreadout() +Starts readout (turns the sensor into forced mode). After the readout the sensor turns to sleep mode. + +####Syntax +`startreadout(delay, callback)` + +####Parameters +* `delay` - sets sensor to forced mode and calls the `callback` (if provided) after given number of milliseconds. For 0 the default delay is set to 113ms (sufficient time to perform reading for oversampling settings 16x). For different oversampling setting please refer to BME280 Final Datasheet - Appendix B: Measurement time and current calculation. +* `callback` - if provided it will be invoked after given `delay`. The sensor reading should be finalized by then so. + +####Returns +`nil` + +**-** [Back to index](#index) + +###qfe2qnh() +Description +For given altitude converts the air pressure to sea level air pressure. + +####Syntax +`qfe2qnh(P, altitude)` + +####Parameters +* `P` - measured pressure +* `altitude` - altitude in meters of measurement point + +####Returns +* `QNH` - sea level pressure + +**-** [Back to index](#index) + +###altitude() +####Description +For given air pressure and sea level air pressure returns the altitude in meters as an integer multiplied with 100, i.e. altimeter function. + +####Syntax +`altitude(P, QNH)` + +####Parameters +* `P` - measured pressure +* `QNH` - current sea level pressure + +####Returns +* `altitude` - altitude in meters of measurement point + +**-** [Back to index](#index) + +###dewpoint() +####Description +For given temperature and relative humidity returns the dew point in celsius as an integer multiplied with 100. + +####Syntax +`dewpoint(H, T)` + +####Parameters +* `H` - relative humidity in percent multiplied by 1000. +* `T` - temperate in celsius multiplied by 100. + +####Returns +* `dewpoint` - dew point in celsisus. + +**-** [Back to index](#index)