hybridgroup.gobot/drivers/i2c/bmp280_driver.go

330 lines
10 KiB
Go

package i2c
import (
"bytes"
"encoding/binary"
"log"
"math"
)
const bmp280Debug = true
// the default address is applicable for SDO to VDD, for SDO to GND it will be 0x76
// this is also true for bme280 (which using this address as well)
const bmp280DefaultAddress = 0x77
type BMP280PressureOversampling uint8
type BMP280TemperatureOversampling uint8
type BMP280IIRFilter uint8
const (
bmp280RegCalib00 = 0x88 // 12 x 16 bit calibration data (T1..T3, P1..P9)
bmp280RegCtrl = 0xF4 // data acquisition options (oversampling of temperature and pressure, power mode)
bmp280RegConf = 0xF5 // rate, IIR-filter and interface options (SPI)
bmp280RegPressureData = 0xF7
bmp280RegTempData = 0xFA
// bits 0, 1 of control register
bmp280CtrlPwrSleepMode = 0x00
bmp280CtrlPwrForcedMode = 0x01
bmp280CtrlPwrForcedMode2 = 0x02 // same function as 0x01
bmp280CtrlPwrNormalMode = 0x03
// bits 2, 3, 4 of control register (will be shifted on write)
BMP280CtrlPressNoMeasurement BMP280PressureOversampling = 0x00 // no measurement (value will be 0x08 0x00 0x00)
BMP280CtrlPressOversampling1 BMP280PressureOversampling = 0x01 // resolution 16 bit
BMP280CtrlPressOversampling2 BMP280PressureOversampling = 0x02 // resolution 17 bit
BMP280CtrlPressOversampling4 BMP280PressureOversampling = 0x03 // resolution 18 bit
BMP280CtrlPressOversampling8 BMP280PressureOversampling = 0x04 // resolution 19 bit
BMP280CtrlPressOversampling16 BMP280PressureOversampling = 0x05 // resolution 20 bit (same as 0x06, 0x07)
// bits 5, 6, 7 of control register (will be shifted on write)
BMP280CtrlTempNoMeasurement BMP280TemperatureOversampling = 0x00 // no measurement (value will be 0x08 0x00 0x00)
BMP280CtrlTempOversampling1 BMP280TemperatureOversampling = 0x01 // resolution 16 bit
BMP280CtrlTempOversampling2 BMP280TemperatureOversampling = 0x02 // resolution 17 bit
BMP280CtrlTempOversampling4 BMP280TemperatureOversampling = 0x03 // resolution 18 bit
BMP280CtrlTempOversampling8 BMP280TemperatureOversampling = 0x04 // resolution 19 bit
BMP280CtrlTempOversampling16 BMP280TemperatureOversampling = 0x05 // resolution 20 bit
// bit 0 of config register
bmp280ConfSPIBit = 0x01 // if set, SPI is used
// bits 2, 3, 4 of config register (bit 1 is unused, will be shifted on write)
bmp280ConfStandBy0005 = 0x00 // 0.5 ms
bmp280ConfStandBy0625 = 0x01 // 62.5 ms
bmp280ConfStandBy0125 = 0x02 // 125 ms
bmp280ConfStandBy0250 = 0x03 // 250 ms
bmp280ConfStandBy0500 = 0x04 // 500 ms
bmp280ConfStandBy1000 = 0x05 // 1000 ms
bmp280ConfStandBy2000 = 0x06 // 2000 ms
bmp280ConfStandBy4000 = 0x07 // 4000 ms
// bits 5, 6, 7 of config register
BMP280ConfFilterOff BMP280IIRFilter = 0x00
BMP280ConfFilter2 BMP280IIRFilter = 0x01
BMP280ConfFilter4 BMP280IIRFilter = 0x02
BMP280ConfFilter8 BMP280IIRFilter = 0x03
BMP280ConfFilter16 BMP280IIRFilter = 0x04
bmp280SeaLevelPressure = 1013.25
)
type bmp280CalibrationCoefficients 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
}
// BMP280Driver is a driver for the BMP280 temperature/pressure sensor
type BMP280Driver struct {
*Driver
calCoeffs *bmp280CalibrationCoefficients
ctrlPwrMode uint8
ctrlPressOversamp BMP280PressureOversampling
ctrlTempOversamp BMP280TemperatureOversampling
confFilter BMP280IIRFilter
}
// NewBMP280Driver creates a new driver with specified i2c interface.
// Params:
//
// c Connector - the Adaptor to use with this Driver
//
// Optional params:
//
// i2c.WithBus(int): bus to use with this driver
// i2c.WithAddress(int): address to use with this driver
func NewBMP280Driver(c Connector, options ...func(Config)) *BMP280Driver {
d := &BMP280Driver{
Driver: NewDriver(c, "BMP280", bmp280DefaultAddress),
calCoeffs: &bmp280CalibrationCoefficients{},
ctrlPwrMode: bmp280CtrlPwrNormalMode,
ctrlPressOversamp: BMP280CtrlPressOversampling16,
ctrlTempOversamp: BMP280CtrlTempOversampling1,
confFilter: BMP280ConfFilterOff,
}
d.afterStart = d.initialization
for _, option := range options {
option(d)
}
// TODO: expose commands to API
return d
}
// WithBMP280PressureOversampling option sets the oversampling for pressure.
// Valid settings are of type "BMP280PressureOversampling"
func WithBMP280PressureOversampling(val BMP280PressureOversampling) func(Config) {
return func(c Config) {
if d, ok := c.(*BMP280Driver); ok {
d.ctrlPressOversamp = val
} else if bmp280Debug {
log.Printf("Trying to set pressure oversampling for non-BMP280Driver %v", c)
}
}
}
// WithBMP280TemperatureOversampling option sets oversampling for temperature.
// Valid settings are of type "BMP280TemperatureOversampling"
func WithBMP280TemperatureOversampling(val BMP280TemperatureOversampling) func(Config) {
return func(c Config) {
if d, ok := c.(*BMP280Driver); ok {
d.ctrlTempOversamp = val
} else if bmp280Debug {
log.Printf("Trying to set temperature oversampling for non-BMP280Driver %v", c)
}
}
}
// WithBMP280IIRFilter option sets the count of IIR filter coefficients.
// Valid settings are of type "BMP280IIRFilter"
func WithBMP280IIRFilter(val BMP280IIRFilter) func(Config) {
return func(c Config) {
if d, ok := c.(*BMP280Driver); ok {
d.confFilter = val
} else if bmp280Debug {
log.Printf("Trying to set IIR filter for non-BMP280Driver %v", c)
}
}
}
// Temperature returns the current temperature, in celsius degrees.
func (d *BMP280Driver) Temperature() (temp float32, err error) {
d.mutex.Lock()
defer d.mutex.Unlock()
var rawT int32
if rawT, err = d.rawTemp(); err != nil {
return 0.0, err
}
temp, _ = d.calculateTemp(rawT)
return
}
// Pressure returns the current barometric pressure, in Pa
func (d *BMP280Driver) Pressure() (press float32, err error) {
d.mutex.Lock()
defer d.mutex.Unlock()
var rawT, rawP int32
if rawT, err = d.rawTemp(); err != nil {
return 0.0, err
}
if rawP, err = d.rawPressure(); err != nil {
return 0.0, err
}
_, tFine := d.calculateTemp(rawT)
return d.calculatePress(rawP, tFine), nil
}
// Altitude 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 *BMP280Driver) Altitude() (alt float32, err error) {
atmP, _ := d.Pressure()
atmP /= 100.0
alt = float32(44330.0 * (1.0 - math.Pow(float64(atmP/bmp280SeaLevelPressure), 0.1903)))
return
}
// initialization reads the calibration coefficients.
func (d *BMP280Driver) initialization() error {
coefficients := make([]byte, 24)
if err := d.connection.ReadBlockData(bmp280RegCalib00, coefficients); err != nil {
return err
}
buf := bytes.NewBuffer(coefficients)
if err := binary.Read(buf, binary.LittleEndian, &d.calCoeffs.t1); err != nil {
return err
}
if err := binary.Read(buf, binary.LittleEndian, &d.calCoeffs.t2); err != nil {
return err
}
if err := binary.Read(buf, binary.LittleEndian, &d.calCoeffs.t3); err != nil {
return err
}
if err := binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p1); err != nil {
return err
}
if err := binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p2); err != nil {
return err
}
if err := binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p3); err != nil {
return err
}
if err := binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p4); err != nil {
return err
}
if err := binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p5); err != nil {
return err
}
if err := binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p6); err != nil {
return err
}
if err := binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p7); err != nil {
return err
}
if err := binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p8); err != nil {
return err
}
if err := binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p9); err != nil {
return err
}
ctrlReg := uint8(d.ctrlPwrMode) | uint8(d.ctrlPressOversamp)<<2 | uint8(d.ctrlTempOversamp)<<5
if err := d.connection.WriteByteData(bmp280RegCtrl, ctrlReg); err != nil {
return err
}
confReg := uint8(bmp280ConfStandBy0005)<<2 | uint8(d.confFilter)<<5
return d.connection.WriteByteData(bmp280RegConf, confReg & ^uint8(bmp280ConfSPIBit))
}
func (d *BMP280Driver) rawTemp() (int32, error) {
var tp0, tp1, tp2 byte
data := make([]byte, 3)
if err := d.connection.ReadBlockData(bmp280RegTempData, data); err != nil {
return 0, err
}
buf := bytes.NewBuffer(data)
if err := binary.Read(buf, binary.LittleEndian, &tp0); err != nil {
return 0, err
}
if err := binary.Read(buf, binary.LittleEndian, &tp1); err != nil {
return 0, err
}
if err := binary.Read(buf, binary.LittleEndian, &tp2); err != nil {
return 0, err
}
return ((int32(tp2) >> 4) | (int32(tp1) << 4) | (int32(tp0) << 12)), nil
}
func (d *BMP280Driver) rawPressure() (int32, error) {
var tp0, tp1, tp2 byte
data := make([]byte, 3)
if err := d.connection.ReadBlockData(bmp280RegPressureData, data); err != nil {
return 0, err
}
buf := bytes.NewBuffer(data)
if err := binary.Read(buf, binary.LittleEndian, &tp0); err != nil {
return 0, err
}
if err := binary.Read(buf, binary.LittleEndian, &tp1); err != nil {
return 0, err
}
if err := binary.Read(buf, binary.LittleEndian, &tp2); err != nil {
return 0, err
}
return ((int32(tp2) >> 4) | (int32(tp1) << 4) | (int32(tp0) << 12)), nil
}
func (d *BMP280Driver) calculateTemp(rawTemp int32) (float32, int32) {
tcvar1 := ((float32(rawTemp) / 16384.0) - (float32(d.calCoeffs.t1) / 1024.0)) * float32(d.calCoeffs.t2)
tcvar2 := (((float32(rawTemp) / 131072.0) - (float32(d.calCoeffs.t1) / 8192.0)) * ((float32(rawTemp) / 131072.0) - float32(d.calCoeffs.t1)/8192.0)) * float32(d.calCoeffs.t3)
temperatureComp := (tcvar1 + tcvar2) / 5120.0
tFine := int32(tcvar1 + tcvar2)
return temperatureComp, tFine
}
func (d *BMP280Driver) calculatePress(rawPress int32, tFine int32) float32 {
var var1, var2, p int64
var1 = int64(tFine) - 128000
var2 = var1 * var1 * int64(d.calCoeffs.p6)
var2 = var2 + ((var1 * int64(d.calCoeffs.p5)) << 17)
var2 = var2 + (int64(d.calCoeffs.p4) << 35)
var1 = (var1 * var1 * int64(d.calCoeffs.p3) >> 8) +
((var1 * int64(d.calCoeffs.p2)) << 12)
var1 = ((int64(1) << 47) + var1) * (int64(d.calCoeffs.p1)) >> 33
if var1 == 0 {
return 0 // avoid exception caused by division by zero
}
p = 1048576 - int64(rawPress)
p = (((p << 31) - var2) * 3125) / var1
var1 = (int64(d.calCoeffs.p9) * (p >> 13) * (p >> 13)) >> 25
var2 = (int64(d.calCoeffs.p8) * p) >> 19
p = ((p + var1 + var2) >> 8) + (int64(d.calCoeffs.p7) << 4)
return float32(p) / 256
}