hybridgroup.gobot/drivers/i2c/bmp180_driver.go

250 lines
7.0 KiB
Go

package i2c
import (
"bytes"
"encoding/binary"
"log"
"time"
)
const bmp180Debug = false
// the default address is applicable for SDO to VDD, for SDO to GND it will be 0x76
const bmp180DefaultAddress = 0x77
const (
bmp180RegisterAC1MSB = 0xAA // 11 x 16 bit calibration data (AC1..AC6, B1, B2, MB, MC, MD)
bmp180RegisterCtl = 0xF4 // control the value to read
bmp180RegisterDataMSB = 0xF6 // 16 bit data (temperature or pressure)
bmp180CtlTemp = 0x2E
bmp180CtlPressure = 0x34
)
const (
// BMP180UltraLowPower is the lowest oversampling mode of the pressure measurement.
BMP180UltraLowPower BMP180OversamplingMode = iota
// BMP180Standard is the standard oversampling mode of the pressure measurement.
BMP180Standard
// BMP180HighResolution is a high oversampling mode of the pressure measurement.
BMP180HighResolution
// BMP180UltraHighResolution is the highest oversampling mode of the pressure measurement.
BMP180UltraHighResolution
)
// BMP180OversamplingMode is the oversampling ratio of the pressure measurement.
type BMP180OversamplingMode uint
type bmp180CalibrationCoefficients struct {
ac1 int16
ac2 int16
ac3 int16
ac4 uint16
ac5 uint16
ac6 uint16
b1 int16
b2 int16
mb int16
mc int16
md int16
}
// BMP180Driver is the gobot driver for the Bosch pressure and temperature sensor BMP180.
// Device datasheet: https://cdn-shop.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf
type BMP180Driver struct {
*Driver
oversampling BMP180OversamplingMode
calCoeffs *bmp180CalibrationCoefficients
}
// NewBMP180Driver creates a new driver with the i2c interface for the BMP180 device.
// Params:
//
// conn 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 NewBMP180Driver(c Connector, options ...func(Config)) *BMP180Driver {
d := &BMP180Driver{
Driver: NewDriver(c, "BMP180", bmp180DefaultAddress),
oversampling: BMP180UltraLowPower,
calCoeffs: &bmp180CalibrationCoefficients{},
}
d.afterStart = d.initialization
for _, option := range options {
option(d)
}
// TODO: expose commands to API
return d
}
// WithBMP180oversampling option sets oversampling mode.
// Valid settings are of type "BMP180OversamplingMode"
func WithBMP180OversamplingMode(val BMP180OversamplingMode) func(Config) {
return func(c Config) {
if d, ok := c.(*BMP180Driver); ok {
d.oversampling = val
} else if bmp180Debug {
log.Printf("Trying to set oversampling mode for non-BMP180Driver %v", c)
}
}
}
// Temperature returns the current temperature, in celsius degrees.
func (d *BMP180Driver) Temperature() (float32, error) {
d.mutex.Lock()
defer d.mutex.Unlock()
rawTemp, err := d.rawTemp()
if err != nil {
return 0, err
}
return d.calculateTemp(rawTemp), nil
}
// Pressure returns the current pressure, in pascals.
func (d *BMP180Driver) Pressure() (float32, error) {
d.mutex.Lock()
defer d.mutex.Unlock()
rawTemp, err := d.rawTemp()
if err != nil {
return 0, err
}
rawPressure, err := d.rawPressure(d.oversampling)
if err != nil {
return 0, err
}
return d.calculatePressure(rawTemp, rawPressure, d.oversampling), nil
}
func (d *BMP180Driver) initialization() error {
// read the 11 calibration coefficients.
coefficients := make([]byte, 22)
if err := d.connection.ReadBlockData(bmp180RegisterAC1MSB, coefficients); err != nil {
return err
}
buf := bytes.NewBuffer(coefficients)
if err := binary.Read(buf, binary.BigEndian, &d.calCoeffs.ac1); err != nil {
return err
}
if err := binary.Read(buf, binary.BigEndian, &d.calCoeffs.ac2); err != nil {
return err
}
if err := binary.Read(buf, binary.BigEndian, &d.calCoeffs.ac3); err != nil {
return err
}
if err := binary.Read(buf, binary.BigEndian, &d.calCoeffs.ac4); err != nil {
return err
}
if err := binary.Read(buf, binary.BigEndian, &d.calCoeffs.ac5); err != nil {
return err
}
if err := binary.Read(buf, binary.BigEndian, &d.calCoeffs.ac6); err != nil {
return err
}
if err := binary.Read(buf, binary.BigEndian, &d.calCoeffs.b1); err != nil {
return err
}
if err := binary.Read(buf, binary.BigEndian, &d.calCoeffs.b2); err != nil {
return err
}
if err := binary.Read(buf, binary.BigEndian, &d.calCoeffs.mb); err != nil {
return err
}
if err := binary.Read(buf, binary.BigEndian, &d.calCoeffs.mc); err != nil {
return err
}
return binary.Read(buf, binary.BigEndian, &d.calCoeffs.md)
}
func (d *BMP180Driver) rawTemp() (int16, error) {
if _, err := d.connection.Write([]byte{bmp180RegisterCtl, bmp180CtlTemp}); err != nil {
return 0, err
}
time.Sleep(5 * time.Millisecond)
ret := make([]byte, 2)
err := d.connection.ReadBlockData(bmp180RegisterDataMSB, ret)
if err != nil {
return 0, err
}
buf := bytes.NewBuffer(ret)
var rawTemp int16
if err := binary.Read(buf, binary.BigEndian, &rawTemp); err != nil {
return 0, err
}
return rawTemp, nil
}
func (d *BMP180Driver) calculateTemp(rawTemp int16) float32 {
b5 := d.calculateB5(rawTemp)
t := (b5 + 8) >> 4
return float32(t) / 10
}
func (d *BMP180Driver) calculateB5(rawTemp int16) int32 {
x1 := (int32(rawTemp) - int32(d.calCoeffs.ac6)) * int32(d.calCoeffs.ac5) >> 15
x2 := int32(d.calCoeffs.mc) << 11 / (x1 + int32(d.calCoeffs.md))
return x1 + x2
}
func (d *BMP180Driver) rawPressure(oversampling BMP180OversamplingMode) (int32, error) {
if _, err := d.connection.Write([]byte{bmp180RegisterCtl, bmp180CtlPressure + byte(oversampling<<6)}); err != nil {
return 0, err
}
time.Sleep(bmp180PauseForReading(oversampling))
ret := make([]byte, 3)
if err := d.connection.ReadBlockData(bmp180RegisterDataMSB, ret); err != nil {
return 0, err
}
rawPressure := (int32(ret[0])<<16 + int32(ret[1])<<8 + int32(ret[2])) >> (8 - uint(oversampling))
return rawPressure, nil
}
func (d *BMP180Driver) calculatePressure(
rawTemp int16,
rawPressure int32,
oversampling BMP180OversamplingMode,
) float32 {
b5 := d.calculateB5(rawTemp)
b6 := b5 - 4000
x1 := (int32(d.calCoeffs.b2) * (b6 * b6 >> 12)) >> 11
x2 := (int32(d.calCoeffs.ac2) * b6) >> 11
x3 := x1 + x2
b3 := (((int32(d.calCoeffs.ac1)*4 + x3) << uint(oversampling)) + 2) >> 2
x1 = (int32(d.calCoeffs.ac3) * b6) >> 13
x2 = (int32(d.calCoeffs.b1) * ((b6 * b6) >> 12)) >> 16
x3 = ((x1 + x2) + 2) >> 2
b4 := (uint32(d.calCoeffs.ac4) * uint32(x3+32768)) >> 15 //nolint:gosec // TODO: fix later
b7 := (uint32(rawPressure-b3) * (50000 >> uint(oversampling))) //nolint:gosec // TODO: fix later
var p int32
if b7 < 0x80000000 {
p = int32((b7 << 1) / b4) //nolint:gosec // TODO: fix later
} else {
p = int32((b7 / b4) << 1) //nolint:gosec // TODO: fix later
}
x1 = (p >> 8) * (p >> 8)
x1 = (x1 * 3038) >> 16
x2 = (-7357 * p) >> 16
return float32(p + ((x1 + x2 + 3791) >> 4))
}
func bmp180PauseForReading(oversampling BMP180OversamplingMode) time.Duration {
var d time.Duration
switch oversampling {
case BMP180UltraLowPower:
d = 5 * time.Millisecond
case BMP180Standard:
d = 8 * time.Millisecond
case BMP180HighResolution:
d = 14 * time.Millisecond
case BMP180UltraHighResolution:
d = 26 * time.Millisecond
}
return d
}