2016-12-30 05:42:00 +08:00
|
|
|
package i2c
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/binary"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"gobot.io/x/gobot"
|
|
|
|
)
|
|
|
|
|
|
|
|
const bmp180Address = 0x77
|
|
|
|
|
|
|
|
const bmp180RegisterAC1MSB = 0xAA
|
|
|
|
|
|
|
|
const bmp180RegisterCtl = 0xF4
|
|
|
|
const bmp180CmdTemp = 0x2E
|
|
|
|
const bmp180RegisterTempMSB = 0xF6
|
|
|
|
const bmp180CmdPressure = 0x34
|
|
|
|
const bmp180RegisterPressureMSB = 0xF6
|
|
|
|
|
|
|
|
// BMP180Driver is the gobot driver for the Bosch pressure sensor BMP180.
|
|
|
|
// Device datasheet: https://cdn-shop.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf
|
|
|
|
type BMP180Driver struct {
|
2017-02-09 18:23:36 +08:00
|
|
|
name string
|
|
|
|
connector I2cConnector
|
|
|
|
connection I2cConnection
|
2017-02-09 21:35:48 +08:00
|
|
|
I2cConfig
|
2016-12-30 05:42:00 +08:00
|
|
|
calibrationCoefficients *calibrationCoefficients
|
|
|
|
}
|
|
|
|
|
|
|
|
// BMP180OversamplingMode is the oversampling ratio of the pressure measurement.
|
|
|
|
type BMP180OversamplingMode uint
|
|
|
|
|
|
|
|
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
|
2016-12-30 05:44:05 +08:00
|
|
|
)
|
2016-12-30 05:42:00 +08:00
|
|
|
|
|
|
|
type calibrationCoefficients struct {
|
|
|
|
ac1 int16
|
|
|
|
ac2 int16
|
|
|
|
ac3 int16
|
|
|
|
ac4 uint16
|
|
|
|
ac5 uint16
|
|
|
|
ac6 uint16
|
|
|
|
b1 int16
|
|
|
|
b2 int16
|
|
|
|
mb int16
|
|
|
|
mc int16
|
|
|
|
md int16
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewBMP180Driver creates a new driver with the i2c interface for the BMP180 device.
|
2017-02-09 21:35:48 +08:00
|
|
|
func NewBMP180Driver(c I2cConnector, options ...func(I2cConfig)) *BMP180Driver {
|
2017-02-09 18:23:36 +08:00
|
|
|
b := &BMP180Driver{
|
2017-02-02 22:46:00 +08:00
|
|
|
name: gobot.DefaultName("BMP180"),
|
2017-02-06 07:19:42 +08:00
|
|
|
connector: c,
|
2017-02-09 21:35:48 +08:00
|
|
|
I2cConfig: NewI2cConfig(),
|
2016-12-30 05:42:00 +08:00
|
|
|
calibrationCoefficients: &calibrationCoefficients{},
|
|
|
|
}
|
2017-02-09 18:23:36 +08:00
|
|
|
|
|
|
|
for _, option := range options {
|
|
|
|
option(b)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: expose commands to API
|
|
|
|
return b
|
2016-12-30 05:42:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Name returns the name of the device.
|
|
|
|
func (d *BMP180Driver) Name() string {
|
|
|
|
return d.name
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetName sets the name of the device.
|
|
|
|
func (d *BMP180Driver) SetName(n string) {
|
|
|
|
d.name = n
|
|
|
|
}
|
|
|
|
|
|
|
|
// Connection returns the connection of the device.
|
|
|
|
func (d *BMP180Driver) Connection() gobot.Connection {
|
2017-02-06 07:19:42 +08:00
|
|
|
return d.connector.(gobot.Connection)
|
2016-12-30 05:42:00 +08:00
|
|
|
}
|
|
|
|
|
2016-12-31 01:17:49 +08:00
|
|
|
// Start initializes the BMP180 and loads the calibration coefficients.
|
2016-12-30 05:42:00 +08:00
|
|
|
func (d *BMP180Driver) Start() (err error) {
|
2017-02-09 21:35:48 +08:00
|
|
|
bus := d.GetBus(d.connector.I2cGetDefaultBus())
|
|
|
|
address := d.GetAddress(bmp180Address)
|
2017-02-09 18:23:36 +08:00
|
|
|
|
2017-02-09 21:35:48 +08:00
|
|
|
if d.connection, err = d.connector.I2cGetConnection(address, bus); err != nil {
|
2017-02-06 07:19:42 +08:00
|
|
|
return err
|
|
|
|
}
|
2016-12-30 05:42:00 +08:00
|
|
|
if err := d.initialization(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-12-31 01:17:49 +08:00
|
|
|
return nil
|
2016-12-30 05:42:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (d *BMP180Driver) initialization() (err error) {
|
|
|
|
var coefficients []byte
|
|
|
|
// read the 11 calibration coefficients.
|
|
|
|
if coefficients, err = d.read(bmp180RegisterAC1MSB, 22); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
buf := bytes.NewBuffer(coefficients)
|
|
|
|
binary.Read(buf, binary.BigEndian, &d.calibrationCoefficients.ac1)
|
|
|
|
binary.Read(buf, binary.BigEndian, &d.calibrationCoefficients.ac2)
|
|
|
|
binary.Read(buf, binary.BigEndian, &d.calibrationCoefficients.ac3)
|
|
|
|
binary.Read(buf, binary.BigEndian, &d.calibrationCoefficients.ac4)
|
|
|
|
binary.Read(buf, binary.BigEndian, &d.calibrationCoefficients.ac5)
|
2016-12-30 05:44:05 +08:00
|
|
|
binary.Read(buf, binary.BigEndian, &d.calibrationCoefficients.ac6)
|
2016-12-30 05:42:00 +08:00
|
|
|
binary.Read(buf, binary.BigEndian, &d.calibrationCoefficients.b1)
|
|
|
|
binary.Read(buf, binary.BigEndian, &d.calibrationCoefficients.b2)
|
|
|
|
binary.Read(buf, binary.BigEndian, &d.calibrationCoefficients.mb)
|
|
|
|
binary.Read(buf, binary.BigEndian, &d.calibrationCoefficients.mc)
|
|
|
|
binary.Read(buf, binary.BigEndian, &d.calibrationCoefficients.md)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-12-31 01:17:49 +08:00
|
|
|
// Halt halts the device.
|
|
|
|
func (d *BMP180Driver) Halt() (err error) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Temperature returns the current temperature, in celsius degrees.
|
|
|
|
func (d *BMP180Driver) Temperature() (temp float32, err error) {
|
|
|
|
var rawTemp int16
|
|
|
|
if rawTemp, err = d.rawTemp(); err != nil {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
return d.calculateTemp(rawTemp), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pressure returns the current pressure, in pascals.
|
|
|
|
func (d *BMP180Driver) Pressure(mode BMP180OversamplingMode) (pressure float32, err error) {
|
|
|
|
var rawTemp int16
|
|
|
|
var rawPressure int32
|
|
|
|
if rawTemp, err = d.rawTemp(); err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
if rawPressure, err = d.rawPressure(mode); err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
return d.calculatePressure(rawTemp, rawPressure, mode), nil
|
|
|
|
}
|
|
|
|
|
2016-12-30 05:42:00 +08:00
|
|
|
func (d *BMP180Driver) rawTemp() (int16, error) {
|
2017-02-06 07:19:42 +08:00
|
|
|
if _, err := d.connection.Write([]byte{bmp180RegisterCtl, bmp180CmdTemp}); err != nil {
|
2016-12-30 05:42:00 +08:00
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
time.Sleep(5 * time.Millisecond)
|
|
|
|
ret, err := d.read(bmp180RegisterTempMSB, 2)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
buf := bytes.NewBuffer(ret)
|
|
|
|
var rawTemp int16
|
|
|
|
binary.Read(buf, binary.BigEndian, &rawTemp)
|
|
|
|
return rawTemp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *BMP180Driver) read(address byte, n int) ([]byte, error) {
|
2017-02-06 07:19:42 +08:00
|
|
|
if _, err := d.connection.Write([]byte{address}); err != nil {
|
2016-12-30 05:42:00 +08:00
|
|
|
return nil, err
|
|
|
|
}
|
2017-02-06 07:19:42 +08:00
|
|
|
buf := make([]byte, n)
|
|
|
|
bytesRead, err := d.connection.Read(buf)
|
|
|
|
if bytesRead != n || err != nil {
|
2016-12-30 05:42:00 +08:00
|
|
|
return nil, err
|
|
|
|
}
|
2017-02-06 07:19:42 +08:00
|
|
|
return buf, nil
|
2016-12-30 05:42:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2016-12-30 05:44:05 +08:00
|
|
|
x1 := (int32(rawTemp) - int32(d.calibrationCoefficients.ac6)) * int32(d.calibrationCoefficients.ac5) >> 15
|
2016-12-30 05:42:00 +08:00
|
|
|
x2 := int32(d.calibrationCoefficients.mc) << 11 / (x1 + int32(d.calibrationCoefficients.md))
|
|
|
|
return x1 + x2
|
|
|
|
}
|
|
|
|
|
2016-12-31 01:17:49 +08:00
|
|
|
func (d *BMP180Driver) rawPressure(mode BMP180OversamplingMode) (rawPressure int32, err error) {
|
2017-02-06 07:19:42 +08:00
|
|
|
if _, err := d.connection.Write([]byte{bmp180RegisterCtl, bmp180CmdPressure + byte(mode<<6)}); err != nil {
|
2016-12-30 05:42:00 +08:00
|
|
|
return 0, err
|
|
|
|
}
|
2016-12-31 01:17:49 +08:00
|
|
|
switch mode {
|
2016-12-30 05:42:00 +08:00
|
|
|
case BMP180UltraLowPower:
|
|
|
|
time.Sleep(5 * time.Millisecond)
|
|
|
|
case BMP180Standard:
|
|
|
|
time.Sleep(8 * time.Millisecond)
|
|
|
|
case BMP180HighResolution:
|
|
|
|
time.Sleep(14 * time.Millisecond)
|
|
|
|
case BMP180UltraHighResolution:
|
|
|
|
time.Sleep(26 * time.Millisecond)
|
|
|
|
}
|
|
|
|
var ret []byte
|
|
|
|
if ret, err = d.read(bmp180RegisterPressureMSB, 3); err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
2016-12-31 01:17:49 +08:00
|
|
|
rawPressure = (int32(ret[0])<<16 + int32(ret[1])<<8 + int32(ret[2])) >> (8 - uint(mode))
|
2016-12-30 05:42:00 +08:00
|
|
|
return rawPressure, nil
|
|
|
|
}
|
|
|
|
|
2016-12-31 01:17:49 +08:00
|
|
|
func (d *BMP180Driver) calculatePressure(rawTemp int16, rawPressure int32, mode BMP180OversamplingMode) float32 {
|
2016-12-30 05:42:00 +08:00
|
|
|
b5 := d.calculateB5(rawTemp)
|
|
|
|
b6 := b5 - 4000
|
|
|
|
x1 := (int32(d.calibrationCoefficients.b2) * (b6 * b6 >> 12)) >> 11
|
|
|
|
x2 := (int32(d.calibrationCoefficients.ac2) * b6) >> 11
|
|
|
|
x3 := x1 + x2
|
2016-12-31 01:17:49 +08:00
|
|
|
b3 := (((int32(d.calibrationCoefficients.ac1)*4 + x3) << uint(mode)) + 2) >> 2
|
2016-12-30 05:42:00 +08:00
|
|
|
x1 = (int32(d.calibrationCoefficients.ac3) * b6) >> 13
|
2016-12-30 05:44:05 +08:00
|
|
|
x2 = (int32(d.calibrationCoefficients.b1) * ((b6 * b6) >> 12)) >> 16
|
|
|
|
x3 = ((x1 + x2) + 2) >> 2
|
|
|
|
b4 := (uint32(d.calibrationCoefficients.ac4) * uint32(x3+32768)) >> 15
|
2016-12-31 01:17:49 +08:00
|
|
|
b7 := (uint32(rawPressure-b3) * (50000 >> uint(mode)))
|
2016-12-30 05:42:00 +08:00
|
|
|
var p int32
|
2016-12-30 05:44:05 +08:00
|
|
|
if b7 < 0x80000000 {
|
2016-12-30 05:42:00 +08:00
|
|
|
p = int32((b7 << 1) / b4)
|
|
|
|
} else {
|
|
|
|
p = int32((b7 / b4) << 1)
|
|
|
|
}
|
|
|
|
x1 = (p >> 8) * (p >> 8)
|
2016-12-30 05:44:05 +08:00
|
|
|
x1 = (x1 * 3038) >> 16
|
|
|
|
x2 = (-7357 * p) >> 16
|
|
|
|
return float32(p + ((x1 + x2 + 3791) >> 4))
|
2016-12-30 05:42:00 +08:00
|
|
|
}
|