mirror of https://github.com/kidoman/embd
370 lines
8.6 KiB
Go
370 lines
8.6 KiB
Go
// Package bme280 allows interfacing with Bosch BME280 digital humidity, pressure
|
|
// and temperature sensor.
|
|
package bme280
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/kidoman/embd"
|
|
)
|
|
|
|
const (
|
|
CAL_T1_LSB_REG = 0x88
|
|
CAL_T1_MSB_REG = 0x89
|
|
CAL_T2_LSB_REG = 0x8A
|
|
CAL_T2_MSB_REG = 0x8B
|
|
CAL_T3_LSB_REG = 0x8C
|
|
CAL_T3_MSB_REG = 0x8D
|
|
|
|
CAL_P1_LSB_REG = 0x8E
|
|
CAL_P1_MSB_REG = 0x8F
|
|
CAL_P2_LSB_REG = 0x90
|
|
CAL_P2_MSB_REG = 0x91
|
|
CAL_P3_LSB_REG = 0x92
|
|
CAL_P3_MSB_REG = 0x93
|
|
CAL_P4_LSB_REG = 0x94
|
|
CAL_P4_MSB_REG = 0x95
|
|
CAL_P5_LSB_REG = 0x96
|
|
CAL_P5_MSB_REG = 0x97
|
|
CAL_P6_LSB_REG = 0x98
|
|
CAL_P6_MSB_REG = 0x99
|
|
CAL_P7_LSB_REG = 0x9A
|
|
CAL_P7_MSB_REG = 0x9B
|
|
CAL_P8_LSB_REG = 0x9C
|
|
CAL_P8_MSB_REG = 0x9D
|
|
CAL_P9_LSB_REG = 0x9E
|
|
CAL_P9_MSB_REG = 0x9F
|
|
|
|
CAL_H1_REG = 0xA1
|
|
CAL_H2_LSB_REG = 0xE1
|
|
CAL_H2_MSB_REG = 0xE2
|
|
CAL_H3_REG = 0xE3
|
|
CAL_H4_MSB_REG = 0xE4
|
|
CAL_H4_LSB_REG = 0xE5
|
|
CAL_H5_MSB_REG = 0xE6
|
|
CAL_H6_REG = 0xE7
|
|
|
|
MEASUREMENT_START_REG = 0xF7
|
|
PRESSURE_MSB_IDX = 0
|
|
PRESSURE_LSB_IDX = 1
|
|
PRESSURE_XLSB_IDX = 2
|
|
|
|
TMP_MSB_IDX = 3
|
|
TMP_LSB_IDX = 4
|
|
TMP_XLSB_IDX = 5
|
|
|
|
HUMIDITY_MSB_IDX = 6
|
|
HUMIDITY_LSB_IDX = 7
|
|
|
|
CTRL_MEAS_REG = 0xF4
|
|
CONFIG_REG = 0xF5
|
|
CTRL_HUMIDITY_REG = 0xF2
|
|
RESET_REG = 0xE0
|
|
|
|
//RunMode can be:
|
|
// 0, Sleep mode
|
|
// 1 or 2, Forced mode
|
|
// 3, Normal mode
|
|
RunMode = uint8(3)
|
|
|
|
//Standby can be:
|
|
// 0, 0.5ms
|
|
// 1, 62.5ms
|
|
// 2, 125ms
|
|
// 3, 250ms
|
|
// 4, 500ms
|
|
// 5, 1000ms
|
|
// 6, 10ms
|
|
// 7, 20ms
|
|
Standby = uint8(0)
|
|
|
|
//Filter can be off or number of FIR coefficients to use:
|
|
// 0, filter off
|
|
// 1, coefficients = 2
|
|
// 2, coefficients = 4
|
|
// 3, coefficients = 8
|
|
// 4, coefficients = 16
|
|
Filter = uint8(0)
|
|
|
|
//TempOverSample can be:
|
|
// 0, skipped
|
|
// 1 through 5, oversampling *1, *2, *4, *8, *16 respectivel
|
|
TempOverSample = uint8(1)
|
|
|
|
//PressOverSample can be:
|
|
// 0, skipped
|
|
// 1 through 5, oversampling *1, *2, *4, *8, *16 respectively
|
|
PressOverSample = uint8(1)
|
|
|
|
//HumidOverSample can be:
|
|
// 0, skipped
|
|
// 1 through 5, oversampling *1, *2, *4, *8, *16 respectively
|
|
HumidOverSample = uint8(1)
|
|
)
|
|
|
|
type Calibration struct {
|
|
T1 uint16
|
|
T2, T3 int16
|
|
|
|
P1, P2, P3, P4, P5, P6, P7, P8, P9 int64
|
|
H1, H2, H3, H4, H5, H6 float64
|
|
}
|
|
|
|
type BME280 struct {
|
|
Bus embd.I2CBus
|
|
Addr byte
|
|
|
|
Cal Calibration
|
|
}
|
|
|
|
func readUInt16(lsb byte, msb byte, bus embd.I2CBus, addr byte) (uint16, error) {
|
|
lsbv, err := bus.ReadByteFromReg(addr, lsb)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
msbv, err := bus.ReadByteFromReg(addr, msb)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return (uint16(msbv) << 8) | uint16(lsbv), nil
|
|
}
|
|
|
|
func readInt16(lsb byte, msb byte, bus embd.I2CBus, addr byte) (int16, error) {
|
|
lsbv, err := bus.ReadByteFromReg(addr, lsb)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
msbv, err := bus.ReadByteFromReg(addr, msb)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return (int16(msbv) << 8) | int16(lsbv), nil
|
|
}
|
|
|
|
// New creates and calibrates a connection to a BME280 sensor on the supplied i2c bus
|
|
// at the nominated i2c address.
|
|
func New(bus embd.I2CBus, addr byte) (*BME280, error) {
|
|
s := &BME280{
|
|
Bus: bus,
|
|
Addr: addr,
|
|
}
|
|
|
|
var err error
|
|
var msb, lsb byte
|
|
|
|
// Get calibrate information.
|
|
s.Cal.T1, err = readUInt16(CAL_T1_LSB_REG, CAL_T1_MSB_REG, bus, addr)
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
|
|
s.Cal.T2, err = readInt16(CAL_T2_LSB_REG, CAL_T2_MSB_REG, bus, addr)
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
|
|
s.Cal.T3, err = readInt16(CAL_T3_LSB_REG, CAL_T3_MSB_REG, bus, addr)
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
|
|
pu, err := readUInt16(CAL_P1_LSB_REG, CAL_P1_MSB_REG, bus, addr)
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
s.Cal.P1 = int64(pu)
|
|
|
|
p, err := readInt16(CAL_P2_LSB_REG, CAL_P2_MSB_REG, bus, addr)
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
s.Cal.P2 = int64(p)
|
|
|
|
p, err = readInt16(CAL_P3_LSB_REG, CAL_P3_MSB_REG, bus, addr)
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
s.Cal.P3 = int64(p)
|
|
|
|
p, err = readInt16(CAL_P4_LSB_REG, CAL_P4_MSB_REG, bus, addr)
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
s.Cal.P4 = int64(p)
|
|
|
|
p, err = readInt16(CAL_P5_LSB_REG, CAL_P5_MSB_REG, bus, addr)
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
s.Cal.P5 = int64(p)
|
|
|
|
p, err = readInt16(CAL_P6_LSB_REG, CAL_P6_MSB_REG, bus, addr)
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
s.Cal.P6 = int64(p)
|
|
|
|
p, err = readInt16(CAL_P7_LSB_REG, CAL_P7_MSB_REG, bus, addr)
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
s.Cal.P7 = int64(p)
|
|
|
|
p, err = readInt16(CAL_P8_LSB_REG, CAL_P8_MSB_REG, bus, addr)
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
s.Cal.P8 = int64(p)
|
|
|
|
p, err = readInt16(CAL_P9_LSB_REG, CAL_P9_MSB_REG, bus, addr)
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
s.Cal.P9 = int64(p)
|
|
|
|
msb, err = bus.ReadByteFromReg(addr, CAL_H1_REG)
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
s.Cal.H1 = float64(msb)
|
|
|
|
h2, err := readInt16(CAL_H2_LSB_REG, CAL_H2_MSB_REG, bus, addr)
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
s.Cal.H2 = float64(h2)
|
|
|
|
msb, err = bus.ReadByteFromReg(addr, CAL_H3_REG)
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
s.Cal.H3 = float64(msb)
|
|
|
|
// H4 and H5 share three registers.
|
|
msb, err = bus.ReadByteFromReg(addr, CAL_H4_MSB_REG)
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
lsb, err = bus.ReadByteFromReg(addr, CAL_H4_LSB_REG)
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
s.Cal.H4 = float64((int16(msb) << 4) | int16(lsb)&0x0F)
|
|
|
|
msb, err = bus.ReadByteFromReg(addr, CAL_H5_MSB_REG)
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
lsb, err = bus.ReadByteFromReg(addr, CAL_H4_LSB_REG)
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
s.Cal.H5 = float64((int16(msb) << 4) | (int16(lsb)>>4)&0x0F)
|
|
|
|
msb, err = bus.ReadByteFromReg(addr, CAL_H6_REG)
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
s.Cal.H6 = float64(msb)
|
|
|
|
// Put the sensor in sleep mode and configure.
|
|
err = bus.WriteByteToReg(addr, CTRL_MEAS_REG, 0x00)
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
|
|
// Set the config word.
|
|
dataToWrite := (Standby << 0x5) & 0xE0
|
|
dataToWrite = dataToWrite | ((Filter << 0x02) & 0x1C)
|
|
err = bus.WriteByteToReg(addr, CONFIG_REG, dataToWrite)
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
|
|
dataToWrite = HumidOverSample & 0x07
|
|
err = bus.WriteByteToReg(addr, CTRL_HUMIDITY_REG, dataToWrite)
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
|
|
dataToWrite = (TempOverSample << 0x5) & 0xE0
|
|
dataToWrite = dataToWrite | ((PressOverSample << 0x02) & 0x1C)
|
|
dataToWrite = dataToWrite | (RunMode & 0x03)
|
|
err = bus.WriteByteToReg(addr, CTRL_MEAS_REG, dataToWrite)
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
|
|
_, err = bus.ReadByteFromReg(s.Addr, 0xD0)
|
|
return s, err
|
|
}
|
|
|
|
func (s *BME280) fineT(d []byte) int32 {
|
|
adcT := int32((uint32(d[TMP_MSB_IDX])<<12)|(uint32(d[TMP_LSB_IDX])<<4)) | ((int32(d[TMP_XLSB_IDX]) >> 4) & 0x0F)
|
|
|
|
var1 := (((adcT >> 3) - (int32(s.Cal.T1) << 1)) * (int32(s.Cal.T2))) >> 11
|
|
var2 := (((((adcT >> 4) - (int32(s.Cal.T1))) * ((adcT >> 4) - (int32(s.Cal.T1)))) >> 12) * (int32(s.Cal.T3))) >> 14
|
|
|
|
return (var1 + var2)
|
|
}
|
|
|
|
// Reads all measurements from the sensor. Call Humidity, Pressure or Temperature to retrieve calibrated readings.
|
|
func (s *BME280) Measurements() ([]byte, error) {
|
|
buf := make([]byte, 8)
|
|
err := s.Bus.ReadFromReg(s.Addr, MEASUREMENT_START_REG, buf)
|
|
|
|
return buf, err
|
|
}
|
|
|
|
// Humdity returns the relative humidity from the supplied measurements. Output value of "46.332" represents 46.332 %rH.
|
|
func (s *BME280) Humidity(d []byte) float64 {
|
|
fineT := s.fineT(d)
|
|
adcH := (uint16(d[HUMIDITY_MSB_IDX]) << 8) | uint16(d[HUMIDITY_LSB_IDX])
|
|
|
|
varH := float64(fineT) - 76800.0
|
|
varH = (float64(adcH) - (s.Cal.H4*64.0 + s.Cal.H5/16384.0*varH)) *
|
|
(s.Cal.H2 / 65536.0 * (1.0 + s.Cal.H6/67108864.0*varH*(1.0+s.Cal.H3/67108864*varH)))
|
|
varH = varH * (1.0 - s.Cal.H1*varH/524288.0)
|
|
if varH > 100.0 {
|
|
varH = 100.0
|
|
} else if varH < 0.0 {
|
|
varH = 0.0
|
|
}
|
|
|
|
return varH
|
|
}
|
|
|
|
// Returns the pressure in Pascals from the supplied measurements. A value of "96386.2" equals 963.862 hPa.
|
|
func (s *BME280) Pressure(d []byte) float64 {
|
|
fineT := s.fineT(d)
|
|
|
|
adcP := int32((uint32(d[PRESSURE_MSB_IDX])<<12)|
|
|
(uint32(d[PRESSURE_LSB_IDX])<<4)) |
|
|
((int32(d[PRESSURE_XLSB_IDX]) >> 4) & 0x0F)
|
|
|
|
var1 := int64(fineT) - 128000
|
|
var2 := var1 * var1 * s.Cal.P6
|
|
var2 = var2 + (var1 * s.Cal.P5 << 17)
|
|
var2 = var2 + (s.Cal.P4 << 35)
|
|
var1 = (var1 * var1 * s.Cal.P3 >> 8) + (var1 * s.Cal.P2 << 12)
|
|
var1 = ((int64(1) << 47) + var1) * s.Cal.P1 >> 33
|
|
if var1 == 0 {
|
|
return 0 // avoid exception caused by division by zero
|
|
}
|
|
p_acc := 1048576 - int64(adcP)
|
|
p_acc = (((p_acc << 31) - var2) * 3125) / var1
|
|
var1 = (s.Cal.P9 * (p_acc >> 13) * (p_acc >> 13)) >> 25
|
|
var2 = (s.Cal.P8 * p_acc) >> 19
|
|
p_acc = ((p_acc + var1 + var2) >> 8) + (s.Cal.P7 << 4)
|
|
|
|
p_acc = p_acc >> 8 // /256
|
|
return float64(p_acc)
|
|
}
|
|
|
|
// Temperature returns the temperature in Degrees Celcius from the supplied measurements. Output value of "30.33" equals 30.33°C.
|
|
func (s *BME280) Temperature(d []byte) float64 {
|
|
fineT := s.fineT(d)
|
|
return (float64(fineT) / 5120.0)
|
|
}
|