243 lines
6.3 KiB
Go
243 lines
6.3 KiB
Go
package i2c
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"math"
|
|
|
|
"gobot.io/x/gobot"
|
|
)
|
|
|
|
const (
|
|
bmp280RegisterControl = 0xf4
|
|
bmp280RegisterConfig = 0xf5
|
|
bmp280RegisterPressureData = 0xf7
|
|
bmp280RegisterTempData = 0xfa
|
|
bmp280RegisterCalib00 = 0x88
|
|
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 {
|
|
name string
|
|
connector Connector
|
|
connection Connection
|
|
Config
|
|
|
|
tpc *bmp280CalibrationCoefficients
|
|
}
|
|
|
|
// NewBMP280Driver creates a new driver with specified i2c interface.
|
|
// 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 NewBMP280Driver(c Connector, options ...func(Config)) *BMP280Driver {
|
|
b := &BMP280Driver{
|
|
name: gobot.DefaultName("BMP280"),
|
|
connector: c,
|
|
Config: NewConfig(),
|
|
tpc: &bmp280CalibrationCoefficients{},
|
|
}
|
|
|
|
for _, option := range options {
|
|
option(b)
|
|
}
|
|
|
|
// TODO: expose commands to API
|
|
return b
|
|
}
|
|
|
|
// Name returns the name of the device.
|
|
func (d *BMP280Driver) Name() string {
|
|
return d.name
|
|
}
|
|
|
|
// SetName sets the name of the device.
|
|
func (d *BMP280Driver) SetName(n string) {
|
|
d.name = n
|
|
}
|
|
|
|
// Connection returns the connection of the device.
|
|
func (d *BMP280Driver) Connection() gobot.Connection {
|
|
return d.connector.(gobot.Connection)
|
|
}
|
|
|
|
// Start initializes the BMP280 and loads the calibration coefficients.
|
|
func (d *BMP280Driver) Start() (err error) {
|
|
bus := d.GetBusOrDefault(d.connector.GetDefaultBus())
|
|
address := d.GetAddressOrDefault(bmp180Address)
|
|
|
|
if d.connection, err = d.connector.GetConnection(address, bus); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := d.initialization(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Halt halts the device.
|
|
func (d *BMP280Driver) Halt() (err error) {
|
|
return nil
|
|
}
|
|
|
|
// Temperature returns the current temperature, in celsius degrees.
|
|
func (d *BMP280Driver) Temperature() (temp float32, err error) {
|
|
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) {
|
|
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() (err error) {
|
|
var coefficients []byte
|
|
if coefficients, err = d.read(bmp280RegisterCalib00, 24); err != nil {
|
|
return err
|
|
}
|
|
buf := bytes.NewBuffer(coefficients)
|
|
binary.Read(buf, binary.LittleEndian, &d.tpc.t1)
|
|
binary.Read(buf, binary.LittleEndian, &d.tpc.t2)
|
|
binary.Read(buf, binary.LittleEndian, &d.tpc.t3)
|
|
binary.Read(buf, binary.LittleEndian, &d.tpc.p1)
|
|
binary.Read(buf, binary.LittleEndian, &d.tpc.p2)
|
|
binary.Read(buf, binary.LittleEndian, &d.tpc.p3)
|
|
binary.Read(buf, binary.LittleEndian, &d.tpc.p4)
|
|
binary.Read(buf, binary.LittleEndian, &d.tpc.p5)
|
|
binary.Read(buf, binary.LittleEndian, &d.tpc.p6)
|
|
binary.Read(buf, binary.LittleEndian, &d.tpc.p7)
|
|
binary.Read(buf, binary.LittleEndian, &d.tpc.p8)
|
|
binary.Read(buf, binary.LittleEndian, &d.tpc.p9)
|
|
|
|
d.connection.WriteByteData(bmp280RegisterControl, 0x3F)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *BMP280Driver) rawTemp() (temp int32, err error) {
|
|
var data []byte
|
|
var tp0, tp1, tp2 byte
|
|
|
|
if data, err = d.read(bmp280RegisterTempData, 3); err != nil {
|
|
return 0, err
|
|
}
|
|
buf := bytes.NewBuffer(data)
|
|
binary.Read(buf, binary.LittleEndian, &tp0)
|
|
binary.Read(buf, binary.LittleEndian, &tp1)
|
|
binary.Read(buf, binary.LittleEndian, &tp2)
|
|
|
|
temp = ((int32(tp2) >> 4) | (int32(tp1) << 4) | (int32(tp0) << 12))
|
|
|
|
return
|
|
}
|
|
|
|
func (d *BMP280Driver) rawPressure() (press int32, err error) {
|
|
var data []byte
|
|
var tp0, tp1, tp2 byte
|
|
|
|
if data, err = d.read(bmp280RegisterPressureData, 3); err != nil {
|
|
return 0, err
|
|
}
|
|
buf := bytes.NewBuffer(data)
|
|
binary.Read(buf, binary.LittleEndian, &tp0)
|
|
binary.Read(buf, binary.LittleEndian, &tp1)
|
|
binary.Read(buf, binary.LittleEndian, &tp2)
|
|
|
|
press = ((int32(tp2) >> 4) | (int32(tp1) << 4) | (int32(tp0) << 12))
|
|
|
|
return
|
|
}
|
|
|
|
func (d *BMP280Driver) calculateTemp(rawTemp int32) (float32, int32) {
|
|
tcvar1 := ((float32(rawTemp) / 16384.0) - (float32(d.tpc.t1) / 1024.0)) * float32(d.tpc.t2)
|
|
tcvar2 := (((float32(rawTemp) / 131072.0) - (float32(d.tpc.t1) / 8192.0)) * ((float32(rawTemp) / 131072.0) - float32(d.tpc.t1)/8192.0)) * float32(d.tpc.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.tpc.p6)
|
|
var2 = var2 + ((var1 * int64(d.tpc.p5)) << 17)
|
|
var2 = var2 + (int64(d.tpc.p4) << 35)
|
|
var1 = (var1 * var1 * int64(d.tpc.p3) >> 8) +
|
|
((var1 * int64(d.tpc.p2)) << 12)
|
|
var1 = ((int64(1) << 47) + var1) * (int64(d.tpc.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.tpc.p9) * (p >> 13) * (p >> 13)) >> 25
|
|
var2 = (int64(d.tpc.p8) * p) >> 19
|
|
|
|
p = ((p + var1 + var2) >> 8) + (int64(d.tpc.p7) << 4)
|
|
return float32(p) / 256
|
|
}
|
|
|
|
func (d *BMP280Driver) read(address byte, n int) ([]byte, error) {
|
|
if _, err := d.connection.Write([]byte{address}); err != nil {
|
|
return nil, err
|
|
}
|
|
buf := make([]byte, n)
|
|
bytesRead, err := d.connection.Read(buf)
|
|
if bytesRead != n || err != nil {
|
|
return nil, err
|
|
}
|
|
return buf, nil
|
|
}
|