|
|
|
@@ -0,0 +1,251 @@
|
|
|
|
|
package bme280
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"machine"
|
|
|
|
|
"math"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// calibrationCoefficients reads at startup and stores the calibration coefficients
|
|
|
|
|
type calibrationCoefficients struct {
|
|
|
|
|
t1 uint16
|
|
|
|
|
t2 int16
|
|
|
|
|
t3 int16
|
|
|
|
|
p1 uint16
|
|
|
|
|
p2 int16
|
|
|
|
|
p3 int16
|
|
|
|
|
p4 int16
|
|
|
|
|
p5 int16
|
|
|
|
|
p6 int16
|
|
|
|
|
p7 int16
|
|
|
|
|
p8 int16
|
|
|
|
|
p9 int16
|
|
|
|
|
h1 uint8
|
|
|
|
|
h2 int16
|
|
|
|
|
h3 uint8
|
|
|
|
|
h4 int16
|
|
|
|
|
h5 int16
|
|
|
|
|
h6 int8
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Device wraps an I2C connection to a BME280 device.
|
|
|
|
|
type Device struct {
|
|
|
|
|
bus machine.I2C
|
|
|
|
|
Address uint16
|
|
|
|
|
calibrationCoefficients calibrationCoefficients
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// New creates a new BME280 connection. The I2C bus must already be
|
|
|
|
|
// configured.
|
|
|
|
|
//
|
|
|
|
|
// This function only creates the Device object, it does not touch the device.
|
|
|
|
|
func New(bus machine.I2C) Device {
|
|
|
|
|
return Device{
|
|
|
|
|
bus: bus,
|
|
|
|
|
Address: Address,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Configure sets up the device for communication and
|
|
|
|
|
// read the calibration coefficientes.
|
|
|
|
|
func (d *Device) Configure() {
|
|
|
|
|
|
|
|
|
|
var data [24]byte
|
|
|
|
|
err := d.bus.ReadRegister(uint8(d.Address), REG_CALIBRATION, data[:])
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var h1 [1]byte
|
|
|
|
|
err = d.bus.ReadRegister(uint8(d.Address), REG_CALIBRATION_H1, h1[:])
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var h2lsb [7]byte
|
|
|
|
|
err = d.bus.ReadRegister(uint8(d.Address), REG_CALIBRATION_H2LSB, h2lsb[:])
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
d.calibrationCoefficients.t1 = readUintLE(data[0], data[1])
|
|
|
|
|
d.calibrationCoefficients.t2 = readIntLE(data[2], data[3])
|
|
|
|
|
d.calibrationCoefficients.t3 = readIntLE(data[4], data[5])
|
|
|
|
|
d.calibrationCoefficients.p1 = readUintLE(data[6], data[7])
|
|
|
|
|
d.calibrationCoefficients.p2 = readIntLE(data[8], data[9])
|
|
|
|
|
d.calibrationCoefficients.p3 = readIntLE(data[10], data[11])
|
|
|
|
|
d.calibrationCoefficients.p4 = readIntLE(data[12], data[13])
|
|
|
|
|
d.calibrationCoefficients.p5 = readIntLE(data[14], data[15])
|
|
|
|
|
d.calibrationCoefficients.p6 = readIntLE(data[16], data[17])
|
|
|
|
|
d.calibrationCoefficients.p7 = readIntLE(data[18], data[19])
|
|
|
|
|
d.calibrationCoefficients.p8 = readIntLE(data[20], data[21])
|
|
|
|
|
d.calibrationCoefficients.p9 = readIntLE(data[22], data[23])
|
|
|
|
|
|
|
|
|
|
d.calibrationCoefficients.h1 = h1[0]
|
|
|
|
|
d.calibrationCoefficients.h2 = readIntLE(h2lsb[0], h2lsb[1])
|
|
|
|
|
d.calibrationCoefficients.h3 = h2lsb[2]
|
|
|
|
|
d.calibrationCoefficients.h6 = int8(h2lsb[6])
|
|
|
|
|
d.calibrationCoefficients.h4 = 0 + (int16(h2lsb[3]) << 4) | (int16(h2lsb[4] & 0x0F))
|
|
|
|
|
d.calibrationCoefficients.h5 = 0 + (int16(h2lsb[5]) << 4) | (int16(h2lsb[4]) >> 4)
|
|
|
|
|
|
|
|
|
|
d.bus.WriteRegister(uint8(d.Address), CTRL_HUMIDITY_ADDR, []byte{0x3f})
|
|
|
|
|
d.bus.WriteRegister(uint8(d.Address), CTRL_MEAS_ADDR, []byte{0xB7})
|
|
|
|
|
d.bus.WriteRegister(uint8(d.Address), CTRL_CONFIG, []byte{0x00})
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Connected returns whether a BME280 has been found.
|
|
|
|
|
// It does a "who am I" request and checks the response.
|
|
|
|
|
func (d *Device) Connected() bool {
|
|
|
|
|
data := []byte{0}
|
|
|
|
|
d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data)
|
|
|
|
|
return data[0] == CHIP_ID
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Reset the device
|
|
|
|
|
func (d *Device) Reset() {
|
|
|
|
|
d.bus.WriteRegister(uint8(d.Address), CMD_RESET, []byte{0xB6})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ReadTemperature returns the temperature in celsius milli degrees (ºC/1000)
|
|
|
|
|
func (d *Device) ReadTemperature() (int32, error) {
|
|
|
|
|
data, err := d.readData()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return 0, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
temp, _ := d.calculateTemp(data)
|
|
|
|
|
return temp, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ReadPressure returns the pressure in milli pascals mPa
|
|
|
|
|
func (d *Device) ReadPressure() (int32, error) {
|
|
|
|
|
data, err := d.readData()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return 0, err
|
|
|
|
|
}
|
|
|
|
|
_, tFine := d.calculateTemp(data)
|
|
|
|
|
pressure := d.calculatePressure(data, tFine)
|
|
|
|
|
return pressure, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ReadHumidity returns the relative humidity in hundredths of a percent
|
|
|
|
|
func (d *Device) ReadHumidity() (int32, error) {
|
|
|
|
|
data, err := d.readData()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return 0, err
|
|
|
|
|
}
|
|
|
|
|
_, tFine := d.calculateTemp(data)
|
|
|
|
|
humidity := d.calculateHumidity(data, tFine)
|
|
|
|
|
return humidity, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ReadAltitude returns the current altitude in meters based on the
|
|
|
|
|
// current barometric pressure and estimated pressure at sea level.
|
|
|
|
|
// Calculation is based on code from Adafruit BME280 library
|
|
|
|
|
// https://github.com/adafruit/Adafruit_BME280_Library
|
|
|
|
|
func (d *Device) ReadAltitude() (alt int32, err error) {
|
|
|
|
|
mPa, _ := d.ReadPressure()
|
|
|
|
|
atmP := float32(mPa) / 100000
|
|
|
|
|
alt = int32(44330.0 * (1.0 - math.Pow(float64(atmP/SEALEVEL_PRESSURE), 0.1903)))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// convert2Bytes converts two bytes to int32
|
|
|
|
|
func convert2Bytes(msb byte, lsb byte) int32 {
|
|
|
|
|
return int32(readUint(msb, lsb))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// convert3Bytes converts three bytes to int32
|
|
|
|
|
func convert3Bytes(msb byte, b1 byte, lsb byte) int32 {
|
|
|
|
|
return int32(((((uint32(msb) << 8) | uint32(b1)) << 8) | uint32(lsb)) >> 4)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// readUint converts two bytes to uint16
|
|
|
|
|
func readUint(msb byte, lsb byte) uint16 {
|
|
|
|
|
return (uint16(msb) << 8) | uint16(lsb)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// readUintLE converts two little endian bytes to uint16
|
|
|
|
|
func readUintLE(msb byte, lsb byte) uint16 {
|
|
|
|
|
temp := readUint(msb, lsb)
|
|
|
|
|
return (temp >> 8) | (temp << 8)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// readIntLE converts two little endian bytes to int16
|
|
|
|
|
func readIntLE(msb byte, lsb byte) int16 {
|
|
|
|
|
return int16(readUintLE(msb, lsb))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// readData does a burst read from 0xF7 to 0xF0 according to the datasheet
|
|
|
|
|
// resulting in an slice with 8 bytes 0-2 = pressure / 3-5 = temperature / 6-7 = humidity
|
|
|
|
|
func (d *Device) readData() (data [8]byte, err error) {
|
|
|
|
|
err = d.bus.ReadRegister(uint8(d.Address), REG_PRESSURE, data[:])
|
|
|
|
|
if err != nil {
|
|
|
|
|
println(err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// calculateTemp uses the data slice and applies calibrations values on it to convert the value to milli degrees
|
|
|
|
|
// it also calculates the variable tFine which is used by the pressure and humidity calculation
|
|
|
|
|
func (d *Device) calculateTemp(data [8]byte) (int32, int32) {
|
|
|
|
|
|
|
|
|
|
rawTemp := convert3Bytes(data[3], data[4], data[5])
|
|
|
|
|
|
|
|
|
|
var1 := (((rawTemp >> 3) - (int32(d.calibrationCoefficients.t1) << 1)) * int32(d.calibrationCoefficients.t2)) >> 11
|
|
|
|
|
var2 := (((((rawTemp >> 4) - int32(d.calibrationCoefficients.t1)) * ((rawTemp >> 4) - int32(d.calibrationCoefficients.t1))) >> 12) * int32(d.calibrationCoefficients.t3)) >> 14
|
|
|
|
|
|
|
|
|
|
tFine := var1 + var2
|
|
|
|
|
T := (tFine*5 + 128) >> 8
|
|
|
|
|
return (10 * T), tFine
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// calculatePressure uses the data slice and applies calibrations values on it to convert the value to milli pascals mPa
|
|
|
|
|
func (d *Device) calculatePressure(data [8]byte, tFine int32) int32 {
|
|
|
|
|
|
|
|
|
|
rawPressure := convert3Bytes(data[0], data[1], data[2])
|
|
|
|
|
|
|
|
|
|
var1 := int64(tFine) - 128000
|
|
|
|
|
var2 := var1 * var1 * int64(d.calibrationCoefficients.p6)
|
|
|
|
|
var2 = var2 + ((var1 * int64(d.calibrationCoefficients.p5)) << 17)
|
|
|
|
|
var2 = var2 + (int64(d.calibrationCoefficients.p4) << 35)
|
|
|
|
|
var1 = ((var1 * var1 * int64(d.calibrationCoefficients.p3)) >> 8) + ((var1 * int64(d.calibrationCoefficients.p2)) << 12)
|
|
|
|
|
var1 = ((int64(1) << 47) + var1) * int64(d.calibrationCoefficients.p1) >> 33
|
|
|
|
|
|
|
|
|
|
if var1 == 0 {
|
|
|
|
|
return 0 // avoid exception caused by division by zero
|
|
|
|
|
}
|
|
|
|
|
p := int64(1048576 - rawPressure)
|
|
|
|
|
p = (((p << 31) - var2) * 3125) / var1
|
|
|
|
|
var1 = (int64(d.calibrationCoefficients.p9) * (p >> 13) * (p >> 13)) >> 25
|
|
|
|
|
var2 = (int64(d.calibrationCoefficients.p8) * p) >> 19
|
|
|
|
|
|
|
|
|
|
p = ((p + var1 + var2) >> 8) + (int64(d.calibrationCoefficients.p7) << 4)
|
|
|
|
|
p = (p / 256)
|
|
|
|
|
return int32(1000 * p)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// calculateHumidity uses the data slice and applies calibrations values on it to convert the value to relative humidity in hundredths of a percent
|
|
|
|
|
func (d *Device) calculateHumidity(data [8]byte, tFine int32) int32 {
|
|
|
|
|
|
|
|
|
|
rawHumidity := convert2Bytes(data[6], data[7])
|
|
|
|
|
|
|
|
|
|
h := float32(tFine) - 76800
|
|
|
|
|
|
|
|
|
|
if h == 0 {
|
|
|
|
|
println("invalid value")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var1 := float32(rawHumidity) - (float32(d.calibrationCoefficients.h4)*64.0 +
|
|
|
|
|
(float32(d.calibrationCoefficients.h5) / 16384.0 * h))
|
|
|
|
|
|
|
|
|
|
var2 := float32(d.calibrationCoefficients.h2) / 65536.0 *
|
|
|
|
|
(1.0 + float32(d.calibrationCoefficients.h6)/67108864.0*h*
|
|
|
|
|
(1.0+float32(d.calibrationCoefficients.h3)/67108864.0*h))
|
|
|
|
|
|
|
|
|
|
h = var1 * var2
|
|
|
|
|
h = h * (1 - float32(d.calibrationCoefficients.h1)*h/524288)
|
|
|
|
|
return int32(100 * h)
|
|
|
|
|
|
|
|
|
|
}
|