250 lines
7.0 KiB
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
|
|
}
|