# BME680 module
| Since  | Origin / Contributor  | Maintainer  | Source  |
| :----- | :-------------------- | :---------- | :------ |
| 2017-10-28 | [vsky279](https://github.com/vsky279) | [vsky279](https://github.com/vsky279) | [bme680.c](../../app/modules/bme680.c)|

This module provides a simple interface to [BME680](https://www.bosch-sensortec.com/bst/products/all_products/bme680) temperature/air presssure/humidity sensors/air quality sensor (Bosch Sensortec). Compared to the BME280 module the sensor does not support automatic mode which means that it can be setup to perform regular measurements. Every measurement has to be triggered manually.

In order to measure the air quality the sensor needs to be heated first. In the example provided by the manufacturer the sensor is heated to 300 degrees centigrade for a period of 200 ms and then the measurement is taken. These values are taken as default values in this implementation. I have not tested the impact of different temperatures and heating times on the measurement.

This module is able to measure the gas resistance (see Bosch's datasheet). The gas resistance is not the IAQ (Indoor Air Quality) Index. But apparently it can be used as some proxy. The value still should somehow reflect the air quality. It seems that the higher value the air quality is better.

The algorithm for IAQ calculation from the gas restistances (probably measured at different temperatures) is not publicly available. Bosch says that at this point of time the calculations for the Indoor Air Quality index are offered only as a pre-compiled library (see discussion here: [BoschSensortec/BME680_driver#6](https://github.com/BoschSensortec/BME680_driver/issues/6)). It is available as the [BSEC Library](https://www.bosch-sensortec.com/bst/products/all_products/bsec). 
The algorithm is implemented in the library `bsec/algo/bin/ESP8266/libalgobsec.a`. Unfortunately I did not even manage to run the Bosch BSEC example on ESP8266 using this library.

## bme680.altitude()

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
`bme680.altitude(P, QNH)`

#### Parameters
- `P` measured pressure
- `QNH` current sea level pressure

#### Returns  
altitude in meters of measurement point

## bme680.dewpoint()

For given temperature and relative humidity returns the dew point in Celsius as an integer multiplied with 100.

#### Syntax
`bme680.dewpoint(H, T)`

#### Parameters
- `H` relative humidity in percent multiplied by 1000.
- `T` temperate in Celsius multiplied by 100.

#### Returns  
dew point in Celsius

## bme680.qfe2qnh()

For given altitude converts the air pressure to sea level air pressure.

#### Syntax
`bme680.qfe2qnh(P, altitude)`

#### Parameters
- `P` measured pressure
- `altitude` altitude in meters of measurement point

#### Returns  
sea level pressure


## bme680.read()

Reads the sensor and returns the temperature, the air pressure, the air relative humidity and 

#### Syntax
`bme680.read([altitude])`

#### Parameters
- (optional) `altitude`- altitude in meters of measurement point. If provided also the air pressure converted to sea level air pressure is returned.

#### Returns  
- `T` temperature in Celsius as an integer multiplied with 100
- `P` air pressure in hectopascals multiplied by 100
- `H` relative humidity in percent multiplied by 1000
- `G` gas resistance
- `QNH` air pressure in hectopascals multiplied by 100 converted to sea level

Any of these variables is `nil` if the readout of given measure was not successful.

The measured values can be read only once. Following attempts to read values will return nil. A new `startreadout()` needs to be called first before next `read()`.

## bme680.startreadout()
Starts readout (turns the sensor into forced mode). After the readout the sensor turns to sleep mode.

#### Syntax
`bme680.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 calculated by the [formula provided by Bosch](https://github.com/BoschSensortec/BME680_driver/blob/2a51b9c0c1899f28e561e6701caa22cb23201cfc/bme680.c#L586). Apparently for certain combinations of oversamplings setup the the delay returned by the formula is not sufficient and the readout is not ready (make sure you are not reading the previous measurement). For default parameters (2x, 16x, 1x) the calculated delay is 121 ms while in reality 150 ms are needed to get the result.
- `callback` if provided it will be invoked after given `delay`. The sensor reading should be finalized by then so.

#### Returns  
`nil`

## bme680.setup()

Initializes module. Initialization is mandatory before read values.

#### Syntax

`bme680.setup([temp_oss, press_oss, humi_oss, heater_temp, heater_duration, IIR_filter, cold_start])`

#### Parameters
- (optional) `temp_oss` - Controls oversampling of temperature data. Default oversampling is 2x.
- (optional) `press_oss` - Controls oversampling of pressure data. Default oversampling is 16x.
- (optional) `humi_oss` - Controls oversampling of humidity data. Default oversampling is 1x
- (optional) `heater_temp` - 
- (optional) `heater_duration` - 
- (optional) `IIR_filter` - Controls the time constant of the IIR filter. Default filter coefficient is 31.
- (optional) `cold_start` - If 0 then the bme680 chip is not initialised. Useful in a battery operated setup when the ESP deep sleeps and on wakeup needs to initialise the driver (the module) but not the chip itself. The chip was kept powered (sleeping too) and is holding the latest reading that should be fetched quickly before another reading starts (`bme680.startreadout()`). By default the chip is initialised.

|`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|

|`IIR_filter`|Filter coefficient |
|-----|-----------------|
|0|Filter off|
|1|1|
|2|3|
|3|7|
|4|15|
|5|31|
|6|63|
|7|127|

#### Returns
`nil` if initialization has failed (no sensor connected?)

#### Example

```lua
alt=320 -- altitude of the measurement place

sda, scl = 3, 4
i2c.setup(0, sda, scl, i2c.SLOW) -- call i2c.setup() only once

bme680.setup()

-- delay calculated by formula provided by Bosch: 121 ms, minimum working (empirical): 150 ms
bme680.startreadout(150, function ()
    T, P, H, G, QNH = bme680.read(alt)
    if T then
        local Tsgn = (T < 0 and -1 or 1); T = Tsgn*T
        print(string.format("T=%s%d.%02d", Tsgn<0 and "-" or "", T/100, T%100))
        print(string.format("QFE=%d.%03d", P/100, P%100))
        print(string.format("QNH=%d.%03d", QNH/100, QNH%100))
        print(string.format("humidity=%d.%03d%%", H/1000, H%1000))
        print(string.format("gas resistance=%d", G))
        D = bme680.dewpoint(H, T)
        local Dsgn = (D < 0 and -1 or 1); D = Dsgn*D
        print(string.format("dew_point=%s%d.%02d", Dsgn<0 and "-" or "", D/100, D%100))
    end
end)
```
