mirror of
https://github.com/hybridgroup/gobot.git
synced 2025-04-24 13:48:49 +08:00
FEATURE: bmxy8z use ReadBlockData
This commit is contained in:
parent
be288943be
commit
6f970f7f6c
@ -4,12 +4,27 @@ import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"log"
|
||||
)
|
||||
|
||||
const bme280RegisterControlHumidity = 0xF2
|
||||
const bme280RegisterHumidityMSB = 0xFD
|
||||
const bme280RegisterCalibDigH1 = 0xa1
|
||||
const bme280RegisterCalibDigH2LSB = 0xe1
|
||||
const bme280Debug = true
|
||||
|
||||
type BME280HumidityOversampling uint8
|
||||
|
||||
const (
|
||||
bme280RegCalibDigH1 = 0xA1
|
||||
bme280RegCalibDigH2LSB = 0xE1
|
||||
bme280RegControlHumidity = 0xF2
|
||||
bme280RegHumidityMSB = 0xFD
|
||||
|
||||
// bits 0, 1, 3 of control humidity register
|
||||
BME280CtrlHumidityNoMeasurement BME280HumidityOversampling = 0x00 // no measurement (value will be 0x08 0x00 0x00)
|
||||
BME280CtrlHumidityOversampling1 BME280HumidityOversampling = 0x01
|
||||
BME280CtrlHumidityOversampling2 BME280HumidityOversampling = 0x02
|
||||
BME280CtrlHumidityOversampling4 BME280HumidityOversampling = 0x03
|
||||
BME280CtrlHumidityOversampling8 BME280HumidityOversampling = 0x04
|
||||
BME280CtrlHumidityOversampling16 BME280HumidityOversampling = 0x05 // same as 0x06, 0x07
|
||||
)
|
||||
|
||||
type bmeHumidityCalibrationCoefficients struct {
|
||||
h1 uint8
|
||||
@ -28,7 +43,8 @@ type bmeHumidityCalibrationCoefficients struct {
|
||||
//
|
||||
type BME280Driver struct {
|
||||
*BMP280Driver
|
||||
hc *bmeHumidityCalibrationCoefficients
|
||||
humCalCoeffs *bmeHumidityCalibrationCoefficients
|
||||
ctrlHumOversamp BME280HumidityOversampling
|
||||
}
|
||||
|
||||
// NewBME280Driver creates a new driver with specified i2c interface.
|
||||
@ -40,28 +56,89 @@ type BME280Driver struct {
|
||||
// i2c.WithAddress(int): address to use with this driver
|
||||
//
|
||||
func NewBME280Driver(c Connector, options ...func(Config)) *BME280Driver {
|
||||
b := &BME280Driver{
|
||||
BMP280Driver: NewBMP280Driver(c),
|
||||
hc: &bmeHumidityCalibrationCoefficients{},
|
||||
d := &BME280Driver{
|
||||
BMP280Driver: NewBMP280Driver(c),
|
||||
humCalCoeffs: &bmeHumidityCalibrationCoefficients{},
|
||||
ctrlHumOversamp: BME280CtrlHumidityOversampling16,
|
||||
}
|
||||
d.afterStart = d.initializationBME280
|
||||
|
||||
// this loop is for options of this class, all options of base class BMP280Driver
|
||||
// must be added in this class for usage
|
||||
for _, option := range options {
|
||||
option(b)
|
||||
option(d)
|
||||
}
|
||||
|
||||
// TODO: expose commands to API
|
||||
return b
|
||||
return d
|
||||
}
|
||||
|
||||
// Start initializes the BME280 and loads the calibration coefficients.
|
||||
func (d *BME280Driver) 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
|
||||
// WithBME280PressureOversampling option sets the oversampling for pressure.
|
||||
// Valid settings are of type "BMP280PressureOversampling"
|
||||
func WithBME280PressureOversampling(val BMP280PressureOversampling) func(Config) {
|
||||
return func(c Config) {
|
||||
if d, ok := c.(*BME280Driver); ok {
|
||||
d.ctrlPressOversamp = val
|
||||
} else if bme280Debug {
|
||||
log.Printf("Trying to set pressure oversampling for non-BME280Driver %v", c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithBME280TemperatureOversampling option sets oversampling for temperature.
|
||||
// Valid settings are of type "BMP280TemperatureOversampling"
|
||||
func WithBME280TemperatureOversampling(val BMP280TemperatureOversampling) func(Config) {
|
||||
return func(c Config) {
|
||||
if d, ok := c.(*BME280Driver); ok {
|
||||
d.ctrlTempOversamp = val
|
||||
} else if bme280Debug {
|
||||
log.Printf("Trying to set temperature oversampling for non-BME280Driver %v", c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithBME280IIRFilter option sets the count of IIR filter coefficients.
|
||||
// Valid settings are of type "BMP280IIRFilter"
|
||||
func WithBME280IIRFilter(val BMP280IIRFilter) func(Config) {
|
||||
return func(c Config) {
|
||||
if d, ok := c.(*BME280Driver); ok {
|
||||
d.confFilter = val
|
||||
} else if bme280Debug {
|
||||
log.Printf("Trying to set IIR filter for non-BME280Driver %v", c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithBME280HumidityOversampling option sets the oversampling for humidity.
|
||||
// Valid settings are of type "BME280HumidityOversampling"
|
||||
func WithBME280HumidityOversampling(val BME280HumidityOversampling) func(Config) {
|
||||
return func(c Config) {
|
||||
if d, ok := c.(*BME280Driver); ok {
|
||||
d.ctrlHumOversamp = val
|
||||
} else if bme280Debug {
|
||||
log.Printf("Trying to set humidity oversampling for non-BME280Driver %v", c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Humidity returns the current humidity in percentage of relative humidity
|
||||
func (d *BME280Driver) Humidity() (humidity float32, err error) {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
|
||||
var rawH uint32
|
||||
if rawH, err = d.rawHumidity(); err != nil {
|
||||
return 0.0, err
|
||||
}
|
||||
humidity = d.calculateHumidity(rawH)
|
||||
return
|
||||
}
|
||||
|
||||
func (d *BME280Driver) initializationBME280() (err error) {
|
||||
// call the initialization routine of base class BMP280Driver, which do:
|
||||
// * initializes temperature and pressure calibration coefficients
|
||||
// * set the control register
|
||||
// * set the configuration register
|
||||
if err := d.initialization(); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -73,26 +150,17 @@ func (d *BME280Driver) Start() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Humidity returns the current humidity in percentage of relative humidity
|
||||
func (d *BME280Driver) Humidity() (humidity float32, err error) {
|
||||
var rawH uint32
|
||||
if rawH, err = d.rawHumidity(); err != nil {
|
||||
return 0.0, err
|
||||
}
|
||||
humidity = d.calculateHumidity(rawH)
|
||||
return
|
||||
}
|
||||
|
||||
// read the humidity calibration coefficients.
|
||||
func (d *BME280Driver) initHumidity() (err error) {
|
||||
var coefficients []byte
|
||||
if coefficients, err = d.read(bme280RegisterCalibDigH1, 1); err != nil {
|
||||
var hch1 byte
|
||||
if hch1, err = d.connection.ReadByteData(bme280RegCalibDigH1); err != nil {
|
||||
return err
|
||||
}
|
||||
buf := bytes.NewBuffer(coefficients)
|
||||
binary.Read(buf, binary.BigEndian, &d.hc.h1)
|
||||
buf := bytes.NewBuffer([]byte{hch1})
|
||||
binary.Read(buf, binary.BigEndian, &d.humCalCoeffs.h1)
|
||||
|
||||
if coefficients, err = d.read(bme280RegisterCalibDigH2LSB, 7); err != nil {
|
||||
coefficients := make([]byte, 7)
|
||||
if err = d.connection.ReadBlockData(bme280RegCalibDigH2LSB, coefficients); err != nil {
|
||||
return err
|
||||
}
|
||||
buf = bytes.NewBuffer(coefficients)
|
||||
@ -102,32 +170,32 @@ func (d *BME280Driver) initHumidity() (err error) {
|
||||
var addrE5 byte
|
||||
var addrE6 byte
|
||||
|
||||
binary.Read(buf, binary.LittleEndian, &d.hc.h2) // E1 ...
|
||||
binary.Read(buf, binary.BigEndian, &d.hc.h3) // E3
|
||||
binary.Read(buf, binary.BigEndian, &addrE4) // E4
|
||||
binary.Read(buf, binary.BigEndian, &addrE5) // E5
|
||||
binary.Read(buf, binary.BigEndian, &addrE6) // E6
|
||||
binary.Read(buf, binary.BigEndian, &d.hc.h6) // ... E7
|
||||
binary.Read(buf, binary.LittleEndian, &d.humCalCoeffs.h2) // E1 ...
|
||||
binary.Read(buf, binary.BigEndian, &d.humCalCoeffs.h3) // E3
|
||||
binary.Read(buf, binary.BigEndian, &addrE4) // E4
|
||||
binary.Read(buf, binary.BigEndian, &addrE5) // E5
|
||||
binary.Read(buf, binary.BigEndian, &addrE6) // E6
|
||||
binary.Read(buf, binary.BigEndian, &d.humCalCoeffs.h6) // ... E7
|
||||
|
||||
d.hc.h4 = 0 + (int16(addrE4) << 4) | (int16(addrE5 & 0x0F))
|
||||
d.hc.h5 = 0 + (int16(addrE6) << 4) | (int16(addrE5) >> 4)
|
||||
d.humCalCoeffs.h4 = 0 + (int16(addrE4) << 4) | (int16(addrE5 & 0x0F))
|
||||
d.humCalCoeffs.h5 = 0 + (int16(addrE6) << 4) | (int16(addrE5) >> 4)
|
||||
|
||||
d.connection.WriteByteData(bme280RegisterControlHumidity, 0x3F)
|
||||
|
||||
// The 'ctrl_hum' register sets the humidity data acquisition options of
|
||||
// The 'ctrl_hum' register (0xF2) sets the humidity data acquisition options of
|
||||
// the device. Changes to this register only become effective after a write
|
||||
// operation to 'ctrl_meas'. Read the current value in, then write it back
|
||||
// operation to 'ctrl_meas' (0xF4). So we read the current value in, then write it back
|
||||
d.connection.WriteByteData(bme280RegControlHumidity, uint8(d.ctrlHumOversamp))
|
||||
|
||||
var cmr uint8
|
||||
cmr, err = d.connection.ReadByteData(bmp280RegisterControl)
|
||||
cmr, err = d.connection.ReadByteData(bmp280RegCtrl)
|
||||
if err == nil {
|
||||
err = d.connection.WriteByteData(bmp280RegisterControl, cmr)
|
||||
err = d.connection.WriteByteData(bmp280RegCtrl, cmr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *BME280Driver) rawHumidity() (uint32, error) {
|
||||
ret, err := d.read(bme280RegisterHumidityMSB, 2)
|
||||
if err != nil {
|
||||
ret := make([]byte, 2)
|
||||
if err := d.connection.ReadBlockData(bme280RegHumidityMSB, ret); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if ret[0] == 0x80 && ret[1] == 0x00 {
|
||||
@ -158,14 +226,14 @@ func (d *BME280Driver) calculateHumidity(rawH uint32) float32 {
|
||||
return 0 // TODO err is 'invalid data' from Bosch - include errors or not?
|
||||
}
|
||||
|
||||
x := float32(rawH) - (float32(d.hc.h4)*64.0 +
|
||||
(float32(d.hc.h5) / 16384.0 * h))
|
||||
x := float32(rawH) - (float32(d.humCalCoeffs.h4)*64.0 +
|
||||
(float32(d.humCalCoeffs.h5) / 16384.0 * h))
|
||||
|
||||
y := float32(d.hc.h2) / 65536.0 *
|
||||
(1.0 + float32(d.hc.h6)/67108864.0*h*
|
||||
(1.0+float32(d.hc.h3)/67108864.0*h))
|
||||
y := float32(d.humCalCoeffs.h2) / 65536.0 *
|
||||
(1.0 + float32(d.humCalCoeffs.h6)/67108864.0*h*
|
||||
(1.0+float32(d.humCalCoeffs.h3)/67108864.0*h))
|
||||
|
||||
h = x * y
|
||||
h = h * (1 - float32(d.hc.h1)*h/524288)
|
||||
h = h * (1 - float32(d.humCalCoeffs.h1)*h/524288)
|
||||
return h
|
||||
}
|
||||
|
@ -3,93 +3,68 @@ package i2c
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gobot.io/x/gobot"
|
||||
"gobot.io/x/gobot/gobottest"
|
||||
)
|
||||
|
||||
// this ensures that the implementation is based on i2c.Driver, which implements the gobot.Driver
|
||||
// and tests all implementations, so no further tests needed here for gobot.Driver interface
|
||||
var _ gobot.Driver = (*BME280Driver)(nil)
|
||||
|
||||
// --------- HELPERS
|
||||
func initTestBME280Driver() (driver *BME280Driver) {
|
||||
driver, _ = initTestBME280DriverWithStubbedAdaptor()
|
||||
return
|
||||
}
|
||||
|
||||
func initTestBME280DriverWithStubbedAdaptor() (*BME280Driver, *i2cTestAdaptor) {
|
||||
func initTestBME280WithStubbedAdaptor() (*BME280Driver, *i2cTestAdaptor) {
|
||||
adaptor := newI2cTestAdaptor()
|
||||
return NewBME280Driver(adaptor), adaptor
|
||||
}
|
||||
|
||||
// --------- TESTS
|
||||
|
||||
func TestNewBME280Driver(t *testing.T) {
|
||||
// Does it return a pointer to an instance of BME280Driver?
|
||||
var bme280 interface{} = NewBME280Driver(newI2cTestAdaptor())
|
||||
_, ok := bme280.(*BME280Driver)
|
||||
var di interface{} = NewBME280Driver(newI2cTestAdaptor())
|
||||
d, ok := di.(*BME280Driver)
|
||||
if !ok {
|
||||
t.Errorf("NewBME280Driver() should have returned a *BME280Driver")
|
||||
}
|
||||
gobottest.Refute(t, d.Driver, nil)
|
||||
gobottest.Assert(t, strings.HasPrefix(d.Name(), "BMP280"), true)
|
||||
gobottest.Assert(t, d.defaultAddress, 0x77)
|
||||
gobottest.Assert(t, d.ctrlPwrMode, uint8(0x03))
|
||||
gobottest.Assert(t, d.ctrlPressOversamp, BMP280PressureOversampling(0x05))
|
||||
gobottest.Assert(t, d.ctrlTempOversamp, BMP280TemperatureOversampling(0x01))
|
||||
gobottest.Assert(t, d.ctrlHumOversamp, BME280HumidityOversampling(0x05))
|
||||
gobottest.Assert(t, d.confFilter, BMP280IIRFilter(0x00))
|
||||
gobottest.Refute(t, d.calCoeffs, nil)
|
||||
}
|
||||
|
||||
func TestBME280Driver(t *testing.T) {
|
||||
bme280 := initTestBME280Driver()
|
||||
gobottest.Refute(t, bme280.Connection(), nil)
|
||||
func TestBME280Options(t *testing.T) {
|
||||
// This is a general test, that options are applied in constructor by using the common WithBus() option and
|
||||
// least one of this driver. Further tests for options can also be done by call of "WithOption(val)(d)".
|
||||
d := NewBME280Driver(newI2cTestAdaptor(), WithBus(2),
|
||||
WithBME280PressureOversampling(0x01),
|
||||
WithBME280TemperatureOversampling(0x02),
|
||||
WithBME280IIRFilter(0x03),
|
||||
WithBME280HumidityOversampling(0x04))
|
||||
gobottest.Assert(t, d.GetBusOrDefault(1), 2)
|
||||
gobottest.Assert(t, d.ctrlPressOversamp, BMP280PressureOversampling(0x01))
|
||||
gobottest.Assert(t, d.ctrlTempOversamp, BMP280TemperatureOversampling(0x02))
|
||||
gobottest.Assert(t, d.confFilter, BMP280IIRFilter(0x03))
|
||||
gobottest.Assert(t, d.ctrlHumOversamp, BME280HumidityOversampling(0x04))
|
||||
}
|
||||
|
||||
func TestBME280DriverStart(t *testing.T) {
|
||||
bme280, adaptor := initTestBME280DriverWithStubbedAdaptor()
|
||||
adaptor.i2cReadImpl = func(b []byte) (int, error) {
|
||||
// Simulate returning a single byte for the
|
||||
// ReadByteData(bmp280RegisterControl) call in Start()
|
||||
return 1, nil
|
||||
}
|
||||
gobottest.Assert(t, bme280.Start(), nil)
|
||||
}
|
||||
|
||||
func TestBME280StartConnectError(t *testing.T) {
|
||||
d, adaptor := initTestBME280DriverWithStubbedAdaptor()
|
||||
adaptor.Testi2cConnectErr(true)
|
||||
gobottest.Assert(t, d.Start(), errors.New("Invalid i2c connection"))
|
||||
}
|
||||
|
||||
func TestBME280DriverStartWriteError(t *testing.T) {
|
||||
bme280, adaptor := initTestBME280DriverWithStubbedAdaptor()
|
||||
adaptor.i2cWriteImpl = func([]byte) (int, error) {
|
||||
return 0, errors.New("write error")
|
||||
}
|
||||
gobottest.Assert(t, bme280.Start(), errors.New("write error"))
|
||||
}
|
||||
|
||||
func TestBME280DriverStartReadError(t *testing.T) {
|
||||
bme280, adaptor := initTestBME280DriverWithStubbedAdaptor()
|
||||
adaptor.i2cReadImpl = func(b []byte) (int, error) {
|
||||
return 0, errors.New("read error")
|
||||
}
|
||||
gobottest.Assert(t, bme280.Start(), errors.New("read error"))
|
||||
}
|
||||
|
||||
func TestBME280DriverHalt(t *testing.T) {
|
||||
bme280 := initTestBME280Driver()
|
||||
|
||||
gobottest.Assert(t, bme280.Halt(), nil)
|
||||
}
|
||||
|
||||
func TestBME280DriverMeasurements(t *testing.T) {
|
||||
bme280, adaptor := initTestBME280DriverWithStubbedAdaptor()
|
||||
func TestBME280Measurements(t *testing.T) {
|
||||
bme280, adaptor := initTestBME280WithStubbedAdaptor()
|
||||
adaptor.i2cReadImpl = func(b []byte) (int, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
// Values produced by dumping data from actual sensor
|
||||
if adaptor.written[len(adaptor.written)-1] == bmp280RegisterCalib00 {
|
||||
if adaptor.written[len(adaptor.written)-1] == bmp280RegCalib00 {
|
||||
buf.Write([]byte{126, 109, 214, 102, 50, 0, 54, 149, 220, 213, 208, 11, 64, 30, 166, 255, 249, 255, 172, 38, 10, 216, 189, 16})
|
||||
} else if adaptor.written[len(adaptor.written)-1] == bme280RegisterCalibDigH1 {
|
||||
} else if adaptor.written[len(adaptor.written)-1] == bme280RegCalibDigH1 {
|
||||
buf.Write([]byte{75})
|
||||
} else if adaptor.written[len(adaptor.written)-1] == bmp280RegisterTempData {
|
||||
} else if adaptor.written[len(adaptor.written)-1] == bmp280RegTempData {
|
||||
buf.Write([]byte{129, 0, 0})
|
||||
} else if adaptor.written[len(adaptor.written)-1] == bme280RegisterCalibDigH2LSB {
|
||||
} else if adaptor.written[len(adaptor.written)-1] == bme280RegCalibDigH2LSB {
|
||||
buf.Write([]byte{112, 1, 0, 19, 1, 0, 30})
|
||||
} else if adaptor.written[len(adaptor.written)-1] == bme280RegisterHumidityMSB {
|
||||
} else if adaptor.written[len(adaptor.written)-1] == bme280RegHumidityMSB {
|
||||
buf.Write([]byte{111, 83})
|
||||
}
|
||||
copy(b, buf.Bytes())
|
||||
@ -101,16 +76,16 @@ func TestBME280DriverMeasurements(t *testing.T) {
|
||||
gobottest.Assert(t, hum, float32(51.20179))
|
||||
}
|
||||
|
||||
func TestBME280DriverInitH1Error(t *testing.T) {
|
||||
bme280, adaptor := initTestBME280DriverWithStubbedAdaptor()
|
||||
func TestBME280InitH1Error(t *testing.T) {
|
||||
bme280, adaptor := initTestBME280WithStubbedAdaptor()
|
||||
adaptor.i2cReadImpl = func(b []byte) (int, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
// Values produced by dumping data from actual sensor
|
||||
if adaptor.written[len(adaptor.written)-1] == bmp280RegisterCalib00 {
|
||||
if adaptor.written[len(adaptor.written)-1] == bmp280RegCalib00 {
|
||||
buf.Write([]byte{126, 109, 214, 102, 50, 0, 54, 149, 220, 213, 208, 11, 64, 30, 166, 255, 249, 255, 172, 38, 10, 216, 189, 16})
|
||||
} else if adaptor.written[len(adaptor.written)-1] == bme280RegisterCalibDigH1 {
|
||||
} else if adaptor.written[len(adaptor.written)-1] == bme280RegCalibDigH1 {
|
||||
return 0, errors.New("h1 read error")
|
||||
} else if adaptor.written[len(adaptor.written)-1] == bme280RegisterCalibDigH2LSB {
|
||||
} else if adaptor.written[len(adaptor.written)-1] == bme280RegCalibDigH2LSB {
|
||||
buf.Write([]byte{112, 1, 0, 19, 1, 0, 30})
|
||||
}
|
||||
copy(b, buf.Bytes())
|
||||
@ -120,16 +95,16 @@ func TestBME280DriverInitH1Error(t *testing.T) {
|
||||
gobottest.Assert(t, bme280.Start(), errors.New("h1 read error"))
|
||||
}
|
||||
|
||||
func TestBME280DriverInitH2Error(t *testing.T) {
|
||||
bme280, adaptor := initTestBME280DriverWithStubbedAdaptor()
|
||||
func TestBME280InitH2Error(t *testing.T) {
|
||||
bme280, adaptor := initTestBME280WithStubbedAdaptor()
|
||||
adaptor.i2cReadImpl = func(b []byte) (int, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
// Values produced by dumping data from actual sensor
|
||||
if adaptor.written[len(adaptor.written)-1] == bmp280RegisterCalib00 {
|
||||
if adaptor.written[len(adaptor.written)-1] == bmp280RegCalib00 {
|
||||
buf.Write([]byte{126, 109, 214, 102, 50, 0, 54, 149, 220, 213, 208, 11, 64, 30, 166, 255, 249, 255, 172, 38, 10, 216, 189, 16})
|
||||
} else if adaptor.written[len(adaptor.written)-1] == bme280RegisterCalibDigH1 {
|
||||
} else if adaptor.written[len(adaptor.written)-1] == bme280RegCalibDigH1 {
|
||||
buf.Write([]byte{75})
|
||||
} else if adaptor.written[len(adaptor.written)-1] == bme280RegisterCalibDigH2LSB {
|
||||
} else if adaptor.written[len(adaptor.written)-1] == bme280RegCalibDigH2LSB {
|
||||
return 0, errors.New("h2 read error")
|
||||
}
|
||||
copy(b, buf.Bytes())
|
||||
@ -139,8 +114,8 @@ func TestBME280DriverInitH2Error(t *testing.T) {
|
||||
gobottest.Assert(t, bme280.Start(), errors.New("h2 read error"))
|
||||
}
|
||||
|
||||
func TestBME280DriverHumidityWriteError(t *testing.T) {
|
||||
bme280, adaptor := initTestBME280DriverWithStubbedAdaptor()
|
||||
func TestBME280HumidityWriteError(t *testing.T) {
|
||||
bme280, adaptor := initTestBME280WithStubbedAdaptor()
|
||||
bme280.Start()
|
||||
|
||||
adaptor.i2cWriteImpl = func([]byte) (int, error) {
|
||||
@ -151,8 +126,8 @@ func TestBME280DriverHumidityWriteError(t *testing.T) {
|
||||
gobottest.Assert(t, hum, float32(0.0))
|
||||
}
|
||||
|
||||
func TestBME280DriverHumidityReadError(t *testing.T) {
|
||||
bme280, adaptor := initTestBME280DriverWithStubbedAdaptor()
|
||||
func TestBME280HumidityReadError(t *testing.T) {
|
||||
bme280, adaptor := initTestBME280WithStubbedAdaptor()
|
||||
bme280.Start()
|
||||
|
||||
adaptor.i2cReadImpl = func([]byte) (int, error) {
|
||||
@ -163,20 +138,20 @@ func TestBME280DriverHumidityReadError(t *testing.T) {
|
||||
gobottest.Assert(t, hum, float32(0.0))
|
||||
}
|
||||
|
||||
func TestBME280DriverHumidityNotEnabled(t *testing.T) {
|
||||
bme280, adaptor := initTestBME280DriverWithStubbedAdaptor()
|
||||
func TestBME280HumidityNotEnabled(t *testing.T) {
|
||||
bme280, adaptor := initTestBME280WithStubbedAdaptor()
|
||||
adaptor.i2cReadImpl = func(b []byte) (int, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
// Values produced by dumping data from actual sensor
|
||||
if adaptor.written[len(adaptor.written)-1] == bmp280RegisterCalib00 {
|
||||
if adaptor.written[len(adaptor.written)-1] == bmp280RegCalib00 {
|
||||
buf.Write([]byte{126, 109, 214, 102, 50, 0, 54, 149, 220, 213, 208, 11, 64, 30, 166, 255, 249, 255, 172, 38, 10, 216, 189, 16})
|
||||
} else if adaptor.written[len(adaptor.written)-1] == bme280RegisterCalibDigH1 {
|
||||
} else if adaptor.written[len(adaptor.written)-1] == bme280RegCalibDigH1 {
|
||||
buf.Write([]byte{75})
|
||||
} else if adaptor.written[len(adaptor.written)-1] == bmp280RegisterTempData {
|
||||
} else if adaptor.written[len(adaptor.written)-1] == bmp280RegTempData {
|
||||
buf.Write([]byte{129, 0, 0})
|
||||
} else if adaptor.written[len(adaptor.written)-1] == bme280RegisterCalibDigH2LSB {
|
||||
} else if adaptor.written[len(adaptor.written)-1] == bme280RegCalibDigH2LSB {
|
||||
buf.Write([]byte{112, 1, 0, 19, 1, 0, 30})
|
||||
} else if adaptor.written[len(adaptor.written)-1] == bme280RegisterHumidityMSB {
|
||||
} else if adaptor.written[len(adaptor.written)-1] == bme280RegHumidityMSB {
|
||||
buf.Write([]byte{0x80, 0x00})
|
||||
}
|
||||
copy(b, buf.Bytes())
|
||||
@ -188,13 +163,28 @@ func TestBME280DriverHumidityNotEnabled(t *testing.T) {
|
||||
gobottest.Assert(t, hum, float32(0.0))
|
||||
}
|
||||
|
||||
func TestBME280DriverSetName(t *testing.T) {
|
||||
b := initTestBME280Driver()
|
||||
b.SetName("TESTME")
|
||||
gobottest.Assert(t, b.Name(), "TESTME")
|
||||
}
|
||||
|
||||
func TestBME280DriverOptions(t *testing.T) {
|
||||
b := NewBME280Driver(newI2cTestAdaptor(), WithBus(2))
|
||||
gobottest.Assert(t, b.GetBusOrDefault(1), 2)
|
||||
func TestBME280_initializationBME280(t *testing.T) {
|
||||
bme280, adaptor := initTestBME280WithStubbedAdaptor()
|
||||
readCallCounter := 0
|
||||
adaptor.i2cReadImpl = func(b []byte) (int, error) {
|
||||
readCallCounter++
|
||||
if readCallCounter == 1 {
|
||||
// Simulate returning 24 bytes for the coefficients (register bmp280RegCalib00)
|
||||
return 24, nil
|
||||
}
|
||||
if readCallCounter == 2 {
|
||||
// Simulate returning a single byte for the hc.h1 value (register bme280RegCalibDigH1)
|
||||
return 1, nil
|
||||
}
|
||||
if readCallCounter == 3 {
|
||||
// Simulate returning 7 bytes for the coefficients (register bme280RegCalibDigH2LSB)
|
||||
return 7, nil
|
||||
}
|
||||
if readCallCounter == 4 {
|
||||
// Simulate returning 1 byte for the cmr (register bmp280RegControl)
|
||||
return 1, nil
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
gobottest.Assert(t, bme280.Start(), nil)
|
||||
}
|
||||
|
@ -3,20 +3,23 @@ package i2c
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"gobot.io/x/gobot"
|
||||
)
|
||||
|
||||
const bmp180Address = 0x77
|
||||
const bmp180Debug = false
|
||||
|
||||
const bmp180RegisterAC1MSB = 0xAA
|
||||
// the default address is applicable for SDO to VDD, for SDO to GND it will be 0x76
|
||||
const bmp180DefaultAddress = 0x77
|
||||
|
||||
const bmp180RegisterCtl = 0xF4
|
||||
const bmp180CmdTemp = 0x2E
|
||||
const bmp180RegisterTempMSB = 0xF6
|
||||
const bmp180CmdPressure = 0x34
|
||||
const bmp180RegisterPressureMSB = 0xF6
|
||||
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.
|
||||
@ -32,7 +35,7 @@ const (
|
||||
// BMP180OversamplingMode is the oversampling ratio of the pressure measurement.
|
||||
type BMP180OversamplingMode uint
|
||||
|
||||
type calibrationCoefficients struct {
|
||||
type bmp180CalibrationCoefficients struct {
|
||||
ac1 int16
|
||||
ac2 int16
|
||||
ac3 int16
|
||||
@ -46,15 +49,12 @@ type calibrationCoefficients struct {
|
||||
md int16
|
||||
}
|
||||
|
||||
// BMP180Driver is the gobot driver for the Bosch pressure sensor BMP180.
|
||||
// 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 {
|
||||
name string
|
||||
Mode BMP180OversamplingMode
|
||||
connector Connector
|
||||
connection Connection
|
||||
Config
|
||||
calibrationCoefficients *calibrationCoefficients
|
||||
*Driver
|
||||
oversampling BMP180OversamplingMode
|
||||
calCoeffs *bmp180CalibrationCoefficients
|
||||
}
|
||||
|
||||
// NewBMP180Driver creates a new driver with the i2c interface for the BMP180 device.
|
||||
@ -66,80 +66,38 @@ type BMP180Driver struct {
|
||||
// i2c.WithAddress(int): address to use with this driver
|
||||
//
|
||||
func NewBMP180Driver(c Connector, options ...func(Config)) *BMP180Driver {
|
||||
b := &BMP180Driver{
|
||||
name: gobot.DefaultName("BMP180"),
|
||||
connector: c,
|
||||
Mode: BMP180UltraLowPower,
|
||||
Config: NewConfig(),
|
||||
calibrationCoefficients: &calibrationCoefficients{},
|
||||
d := &BMP180Driver{
|
||||
Driver: NewDriver(c, "BMP180", bmp180DefaultAddress),
|
||||
oversampling: BMP180UltraLowPower,
|
||||
calCoeffs: &bmp180CalibrationCoefficients{},
|
||||
}
|
||||
d.afterStart = d.initialization
|
||||
|
||||
for _, option := range options {
|
||||
option(b)
|
||||
option(d)
|
||||
}
|
||||
|
||||
// TODO: expose commands to API
|
||||
return b
|
||||
return d
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return d.connector.(gobot.Connection)
|
||||
}
|
||||
|
||||
// Start initializes the BMP180 and loads the calibration coefficients.
|
||||
func (d *BMP180Driver) 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
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
if err := d.initialization(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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)
|
||||
binary.Read(buf, binary.BigEndian, &d.calibrationCoefficients.ac6)
|
||||
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
|
||||
}
|
||||
|
||||
// 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) {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
|
||||
var rawTemp int16
|
||||
if rawTemp, err = d.rawTemp(); err != nil {
|
||||
return 0, err
|
||||
@ -149,23 +107,49 @@ func (d *BMP180Driver) Temperature() (temp float32, err error) {
|
||||
|
||||
// Pressure returns the current pressure, in pascals.
|
||||
func (d *BMP180Driver) Pressure() (pressure float32, err error) {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
|
||||
var rawTemp int16
|
||||
var rawPressure int32
|
||||
if rawTemp, err = d.rawTemp(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if rawPressure, err = d.rawPressure(d.Mode); err != nil {
|
||||
if rawPressure, err = d.rawPressure(d.oversampling); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return d.calculatePressure(rawTemp, rawPressure, d.Mode), nil
|
||||
return d.calculatePressure(rawTemp, rawPressure, d.oversampling), nil
|
||||
}
|
||||
|
||||
func (d *BMP180Driver) initialization() (err 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)
|
||||
binary.Read(buf, binary.BigEndian, &d.calCoeffs.ac1)
|
||||
binary.Read(buf, binary.BigEndian, &d.calCoeffs.ac2)
|
||||
binary.Read(buf, binary.BigEndian, &d.calCoeffs.ac3)
|
||||
binary.Read(buf, binary.BigEndian, &d.calCoeffs.ac4)
|
||||
binary.Read(buf, binary.BigEndian, &d.calCoeffs.ac5)
|
||||
binary.Read(buf, binary.BigEndian, &d.calCoeffs.ac6)
|
||||
binary.Read(buf, binary.BigEndian, &d.calCoeffs.b1)
|
||||
binary.Read(buf, binary.BigEndian, &d.calCoeffs.b2)
|
||||
binary.Read(buf, binary.BigEndian, &d.calCoeffs.mb)
|
||||
binary.Read(buf, binary.BigEndian, &d.calCoeffs.mc)
|
||||
binary.Read(buf, binary.BigEndian, &d.calCoeffs.md)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *BMP180Driver) rawTemp() (int16, error) {
|
||||
if _, err := d.connection.Write([]byte{bmp180RegisterCtl, bmp180CmdTemp}); err != nil {
|
||||
if _, err := d.connection.Write([]byte{bmp180RegisterCtl, bmp180CtlTemp}); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
ret, err := d.read(bmp180RegisterTempMSB, 2)
|
||||
ret := make([]byte, 2)
|
||||
err := d.connection.ReadBlockData(bmp180RegisterDataMSB, ret)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@ -175,18 +159,6 @@ func (d *BMP180Driver) rawTemp() (int16, error) {
|
||||
return rawTemp, nil
|
||||
}
|
||||
|
||||
func (d *BMP180Driver) 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
|
||||
}
|
||||
|
||||
func (d *BMP180Driver) calculateTemp(rawTemp int16) float32 {
|
||||
b5 := d.calculateB5(rawTemp)
|
||||
t := (b5 + 8) >> 4
|
||||
@ -194,36 +166,36 @@ func (d *BMP180Driver) calculateTemp(rawTemp int16) float32 {
|
||||
}
|
||||
|
||||
func (d *BMP180Driver) calculateB5(rawTemp int16) int32 {
|
||||
x1 := (int32(rawTemp) - int32(d.calibrationCoefficients.ac6)) * int32(d.calibrationCoefficients.ac5) >> 15
|
||||
x2 := int32(d.calibrationCoefficients.mc) << 11 / (x1 + int32(d.calibrationCoefficients.md))
|
||||
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(mode BMP180OversamplingMode) (rawPressure int32, err error) {
|
||||
if _, err = d.connection.Write([]byte{bmp180RegisterCtl, bmp180CmdPressure + byte(mode<<6)}); err != nil {
|
||||
func (d *BMP180Driver) rawPressure(oversampling BMP180OversamplingMode) (rawPressure int32, err error) {
|
||||
if _, err = d.connection.Write([]byte{bmp180RegisterCtl, bmp180CtlPressure + byte(oversampling<<6)}); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
time.Sleep(pauseForReading(mode))
|
||||
var ret []byte
|
||||
if ret, err = d.read(bmp180RegisterPressureMSB, 3); err != nil {
|
||||
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(mode))
|
||||
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, mode BMP180OversamplingMode) float32 {
|
||||
func (d *BMP180Driver) calculatePressure(rawTemp int16, rawPressure int32, oversampling BMP180OversamplingMode) float32 {
|
||||
b5 := d.calculateB5(rawTemp)
|
||||
b6 := b5 - 4000
|
||||
x1 := (int32(d.calibrationCoefficients.b2) * (b6 * b6 >> 12)) >> 11
|
||||
x2 := (int32(d.calibrationCoefficients.ac2) * b6) >> 11
|
||||
x1 := (int32(d.calCoeffs.b2) * (b6 * b6 >> 12)) >> 11
|
||||
x2 := (int32(d.calCoeffs.ac2) * b6) >> 11
|
||||
x3 := x1 + x2
|
||||
b3 := (((int32(d.calibrationCoefficients.ac1)*4 + x3) << uint(mode)) + 2) >> 2
|
||||
x1 = (int32(d.calibrationCoefficients.ac3) * b6) >> 13
|
||||
x2 = (int32(d.calibrationCoefficients.b1) * ((b6 * b6) >> 12)) >> 16
|
||||
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.calibrationCoefficients.ac4) * uint32(x3+32768)) >> 15
|
||||
b7 := (uint32(rawPressure-b3) * (50000 >> uint(mode)))
|
||||
b4 := (uint32(d.calCoeffs.ac4) * uint32(x3+32768)) >> 15
|
||||
b7 := (uint32(rawPressure-b3) * (50000 >> uint(oversampling)))
|
||||
var p int32
|
||||
if b7 < 0x80000000 {
|
||||
p = int32((b7 << 1) / b4)
|
||||
@ -236,9 +208,9 @@ func (d *BMP180Driver) calculatePressure(rawTemp int16, rawPressure int32, mode
|
||||
return float32(p + ((x1 + x2 + 3791) >> 4))
|
||||
}
|
||||
|
||||
func pauseForReading(mode BMP180OversamplingMode) time.Duration {
|
||||
func bmp180PauseForReading(oversampling BMP180OversamplingMode) time.Duration {
|
||||
var d time.Duration
|
||||
switch mode {
|
||||
switch oversampling {
|
||||
case BMP180UltraLowPower:
|
||||
d = 5 * time.Millisecond
|
||||
case BMP180Standard:
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -11,62 +12,39 @@ import (
|
||||
"gobot.io/x/gobot/gobottest"
|
||||
)
|
||||
|
||||
// this ensures that the implementation is based on i2c.Driver, which implements the gobot.Driver
|
||||
// and tests all implementations, so no further tests needed here for gobot.Driver interface
|
||||
var _ gobot.Driver = (*BMP180Driver)(nil)
|
||||
|
||||
// --------- HELPERS
|
||||
func initTestBMP180Driver() (driver *BMP180Driver) {
|
||||
driver, _ = initTestBMP180DriverWithStubbedAdaptor()
|
||||
return
|
||||
}
|
||||
|
||||
func initTestBMP180DriverWithStubbedAdaptor() (*BMP180Driver, *i2cTestAdaptor) {
|
||||
func initTestBMP180WithStubbedAdaptor() (*BMP180Driver, *i2cTestAdaptor) {
|
||||
adaptor := newI2cTestAdaptor()
|
||||
return NewBMP180Driver(adaptor), adaptor
|
||||
}
|
||||
|
||||
// --------- TESTS
|
||||
|
||||
func TestNewBMP180Driver(t *testing.T) {
|
||||
// Does it return a pointer to an instance of BMP180Driver?
|
||||
var bmp180 interface{} = NewBMP180Driver(newI2cTestAdaptor())
|
||||
_, ok := bmp180.(*BMP180Driver)
|
||||
var di interface{} = NewBMP180Driver(newI2cTestAdaptor())
|
||||
d, ok := di.(*BMP180Driver)
|
||||
if !ok {
|
||||
t.Errorf("NewBMP180Driver() should have returned a *BMP180Driver")
|
||||
}
|
||||
gobottest.Refute(t, d.Driver, nil)
|
||||
gobottest.Assert(t, strings.HasPrefix(d.Name(), "BMP180"), true)
|
||||
gobottest.Assert(t, d.defaultAddress, 0x77)
|
||||
gobottest.Assert(t, d.oversampling, BMP180OversamplingMode(0x00))
|
||||
gobottest.Refute(t, d.calCoeffs, nil)
|
||||
}
|
||||
|
||||
func TestBMP180Driver(t *testing.T) {
|
||||
bmp180 := initTestBMP180Driver()
|
||||
gobottest.Refute(t, bmp180.Connection(), nil)
|
||||
func TestBMP180Options(t *testing.T) {
|
||||
// This is a general test, that options are applied in constructor by using the common WithBus() option and
|
||||
// least one of this driver. Further tests for options can also be done by call of "WithOption(val)(d)".
|
||||
d := NewBMP180Driver(newI2cTestAdaptor(), WithBus(2), WithBMP180OversamplingMode(0x01))
|
||||
gobottest.Assert(t, d.GetBusOrDefault(1), 2)
|
||||
gobottest.Assert(t, d.oversampling, BMP180OversamplingMode(0x01))
|
||||
}
|
||||
|
||||
func TestBMP180DriverStart(t *testing.T) {
|
||||
bmp180, _ := initTestBMP180DriverWithStubbedAdaptor()
|
||||
gobottest.Assert(t, bmp180.Start(), nil)
|
||||
}
|
||||
|
||||
func TestBMP180StartConnectError(t *testing.T) {
|
||||
d, adaptor := initTestBMP180DriverWithStubbedAdaptor()
|
||||
adaptor.Testi2cConnectErr(true)
|
||||
gobottest.Assert(t, d.Start(), errors.New("Invalid i2c connection"))
|
||||
}
|
||||
|
||||
func TestBMP180DriverStartWriteError(t *testing.T) {
|
||||
bmp180, adaptor := initTestBMP180DriverWithStubbedAdaptor()
|
||||
adaptor.i2cWriteImpl = func([]byte) (int, error) {
|
||||
return 0, errors.New("write error")
|
||||
}
|
||||
gobottest.Assert(t, bmp180.Start(), errors.New("write error"))
|
||||
}
|
||||
|
||||
func TestBMP180DriverHalt(t *testing.T) {
|
||||
bmp180 := initTestBMP180Driver()
|
||||
|
||||
gobottest.Assert(t, bmp180.Halt(), nil)
|
||||
}
|
||||
|
||||
func TestBMP180DriverMeasurements(t *testing.T) {
|
||||
bmp180, adaptor := initTestBMP180DriverWithStubbedAdaptor()
|
||||
func TestBMP180Measurements(t *testing.T) {
|
||||
bmp180, adaptor := initTestBMP180WithStubbedAdaptor()
|
||||
adaptor.i2cReadImpl = func(b []byte) (int, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
// Values from the datasheet example.
|
||||
@ -82,9 +60,9 @@ func TestBMP180DriverMeasurements(t *testing.T) {
|
||||
binary.Write(buf, binary.BigEndian, int16(-32768))
|
||||
binary.Write(buf, binary.BigEndian, int16(-8711))
|
||||
binary.Write(buf, binary.BigEndian, int16(2868))
|
||||
} else if adaptor.written[len(adaptor.written)-2] == bmp180CmdTemp && adaptor.written[len(adaptor.written)-1] == bmp180RegisterTempMSB {
|
||||
} else if adaptor.written[len(adaptor.written)-2] == bmp180CtlTemp && adaptor.written[len(adaptor.written)-1] == bmp180RegisterDataMSB {
|
||||
binary.Write(buf, binary.BigEndian, int16(27898))
|
||||
} else if adaptor.written[len(adaptor.written)-2] == bmp180CmdPressure && adaptor.written[len(adaptor.written)-1] == bmp180RegisterPressureMSB {
|
||||
} else if adaptor.written[len(adaptor.written)-2] == bmp180CtlPressure && adaptor.written[len(adaptor.written)-1] == bmp180RegisterDataMSB {
|
||||
binary.Write(buf, binary.BigEndian, int16(23843))
|
||||
// XLSB, not used in this test.
|
||||
buf.WriteByte(0)
|
||||
@ -101,8 +79,8 @@ func TestBMP180DriverMeasurements(t *testing.T) {
|
||||
gobottest.Assert(t, pressure, float32(69964))
|
||||
}
|
||||
|
||||
func TestBMP180DriverTemperatureError(t *testing.T) {
|
||||
bmp180, adaptor := initTestBMP180DriverWithStubbedAdaptor()
|
||||
func TestBMP180TemperatureError(t *testing.T) {
|
||||
bmp180, adaptor := initTestBMP180WithStubbedAdaptor()
|
||||
adaptor.i2cReadImpl = func(b []byte) (int, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
// Values from the datasheet example.
|
||||
@ -118,9 +96,9 @@ func TestBMP180DriverTemperatureError(t *testing.T) {
|
||||
binary.Write(buf, binary.BigEndian, int16(-32768))
|
||||
binary.Write(buf, binary.BigEndian, int16(-8711))
|
||||
binary.Write(buf, binary.BigEndian, int16(2868))
|
||||
} else if adaptor.written[len(adaptor.written)-2] == bmp180CmdTemp && adaptor.written[len(adaptor.written)-1] == bmp180RegisterTempMSB {
|
||||
} else if adaptor.written[len(adaptor.written)-2] == bmp180CtlTemp && adaptor.written[len(adaptor.written)-1] == bmp180RegisterDataMSB {
|
||||
return 0, errors.New("temp error")
|
||||
} else if adaptor.written[len(adaptor.written)-2] == bmp180CmdPressure && adaptor.written[len(adaptor.written)-1] == bmp180RegisterPressureMSB {
|
||||
} else if adaptor.written[len(adaptor.written)-2] == bmp180CtlPressure && adaptor.written[len(adaptor.written)-1] == bmp180RegisterDataMSB {
|
||||
binary.Write(buf, binary.BigEndian, int16(23843))
|
||||
// XLSB, not used in this test.
|
||||
buf.WriteByte(0)
|
||||
@ -133,8 +111,8 @@ func TestBMP180DriverTemperatureError(t *testing.T) {
|
||||
gobottest.Assert(t, err, errors.New("temp error"))
|
||||
}
|
||||
|
||||
func TestBMP180DriverPressureError(t *testing.T) {
|
||||
bmp180, adaptor := initTestBMP180DriverWithStubbedAdaptor()
|
||||
func TestBMP180PressureError(t *testing.T) {
|
||||
bmp180, adaptor := initTestBMP180WithStubbedAdaptor()
|
||||
adaptor.i2cReadImpl = func(b []byte) (int, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
// Values from the datasheet example.
|
||||
@ -150,9 +128,9 @@ func TestBMP180DriverPressureError(t *testing.T) {
|
||||
binary.Write(buf, binary.BigEndian, int16(-32768))
|
||||
binary.Write(buf, binary.BigEndian, int16(-8711))
|
||||
binary.Write(buf, binary.BigEndian, int16(2868))
|
||||
} else if adaptor.written[len(adaptor.written)-2] == bmp180CmdTemp && adaptor.written[len(adaptor.written)-1] == bmp180RegisterTempMSB {
|
||||
} else if adaptor.written[len(adaptor.written)-2] == bmp180CtlTemp && adaptor.written[len(adaptor.written)-1] == bmp180RegisterDataMSB {
|
||||
binary.Write(buf, binary.BigEndian, int16(27898))
|
||||
} else if adaptor.written[len(adaptor.written)-2] == bmp180CmdPressure && adaptor.written[len(adaptor.written)-1] == bmp180RegisterPressureMSB {
|
||||
} else if adaptor.written[len(adaptor.written)-2] == bmp180CtlPressure && adaptor.written[len(adaptor.written)-1] == bmp180RegisterDataMSB {
|
||||
return 0, errors.New("press error")
|
||||
}
|
||||
copy(b, buf.Bytes())
|
||||
@ -163,8 +141,8 @@ func TestBMP180DriverPressureError(t *testing.T) {
|
||||
gobottest.Assert(t, err, errors.New("press error"))
|
||||
}
|
||||
|
||||
func TestBMP180DriverPressureWriteError(t *testing.T) {
|
||||
bmp180, adaptor := initTestBMP180DriverWithStubbedAdaptor()
|
||||
func TestBMP180PressureWriteError(t *testing.T) {
|
||||
bmp180, adaptor := initTestBMP180WithStubbedAdaptor()
|
||||
bmp180.Start()
|
||||
|
||||
adaptor.i2cWriteImpl = func([]byte) (int, error) {
|
||||
@ -175,20 +153,56 @@ func TestBMP180DriverPressureWriteError(t *testing.T) {
|
||||
gobottest.Assert(t, err, errors.New("write error"))
|
||||
}
|
||||
|
||||
func TestBMP180DriverSetName(t *testing.T) {
|
||||
b := initTestBMP180Driver()
|
||||
b.SetName("TESTME")
|
||||
gobottest.Assert(t, b.Name(), "TESTME")
|
||||
func TestBMP180_initialization(t *testing.T) {
|
||||
// sequence to read in initialization():
|
||||
// * read 22 bytes (11 x 16 bit calibration data), starting from AC1 register (0xAA)
|
||||
// * fill calibration struct with data (MSByte read first)
|
||||
// arrange
|
||||
d, a := initTestBMP180WithStubbedAdaptor()
|
||||
a.written = []byte{} // reset writes of former test
|
||||
// Values from the datasheet example.
|
||||
ac1 := []uint8{0x01, 0x98}
|
||||
ac2 := []uint8{0xFF, 0xB8}
|
||||
ac3 := []uint8{0xC7, 0xD1}
|
||||
ac4 := []uint8{0x7F, 0xE5}
|
||||
ac5 := []uint8{0x7F, 0xF5}
|
||||
ac6 := []uint8{0x5A, 0x71}
|
||||
b1 := []uint8{0x18, 0x2E}
|
||||
b2 := []uint8{0x00, 0x04}
|
||||
mb := []uint8{0x80, 0x00}
|
||||
mc := []uint8{0xDD, 0xF9}
|
||||
md := []uint8{0x0B, 0x34}
|
||||
returnRead := append(append(append(append(append(ac1, ac2...), ac3...), ac4...), ac5...), ac6...)
|
||||
returnRead = append(append(append(append(append(returnRead, b1...), b2...), mb...), mc...), md...)
|
||||
numCallsRead := 0
|
||||
a.i2cReadImpl = func(b []byte) (int, error) {
|
||||
numCallsRead++
|
||||
copy(b, returnRead)
|
||||
return len(b), nil
|
||||
}
|
||||
// act, assert - initialization() must be called on Start()
|
||||
err := d.Start()
|
||||
// assert
|
||||
gobottest.Assert(t, err, nil)
|
||||
gobottest.Assert(t, numCallsRead, 1)
|
||||
gobottest.Assert(t, len(a.written), 1)
|
||||
gobottest.Assert(t, a.written[0], uint8(0xAA))
|
||||
gobottest.Assert(t, d.calCoeffs.ac1, int16(408))
|
||||
gobottest.Assert(t, d.calCoeffs.ac2, int16(-72))
|
||||
gobottest.Assert(t, d.calCoeffs.ac3, int16(-14383))
|
||||
gobottest.Assert(t, d.calCoeffs.ac4, uint16(32741))
|
||||
gobottest.Assert(t, d.calCoeffs.ac5, uint16(32757))
|
||||
gobottest.Assert(t, d.calCoeffs.ac6, uint16(23153))
|
||||
gobottest.Assert(t, d.calCoeffs.b1, int16(6190))
|
||||
gobottest.Assert(t, d.calCoeffs.b2, int16(4))
|
||||
gobottest.Assert(t, d.calCoeffs.mb, int16(-32768))
|
||||
gobottest.Assert(t, d.calCoeffs.mc, int16(-8711))
|
||||
gobottest.Assert(t, d.calCoeffs.md, int16(2868))
|
||||
}
|
||||
|
||||
func TestBMP180DriverOptions(t *testing.T) {
|
||||
b := NewBMP180Driver(newI2cTestAdaptor(), WithBus(2))
|
||||
gobottest.Assert(t, b.GetBusOrDefault(1), 2)
|
||||
}
|
||||
|
||||
func TestBMP180PauseForReading(t *testing.T) {
|
||||
gobottest.Assert(t, pauseForReading(BMP180UltraLowPower), time.Duration(5*time.Millisecond))
|
||||
gobottest.Assert(t, pauseForReading(BMP180Standard), time.Duration(8*time.Millisecond))
|
||||
gobottest.Assert(t, pauseForReading(BMP180HighResolution), time.Duration(14*time.Millisecond))
|
||||
gobottest.Assert(t, pauseForReading(BMP180UltraHighResolution), time.Duration(26*time.Millisecond))
|
||||
func TestBMP180_bmp180PauseForReading(t *testing.T) {
|
||||
gobottest.Assert(t, bmp180PauseForReading(BMP180UltraLowPower), time.Duration(5*time.Millisecond))
|
||||
gobottest.Assert(t, bmp180PauseForReading(BMP180Standard), time.Duration(8*time.Millisecond))
|
||||
gobottest.Assert(t, bmp180PauseForReading(BMP180HighResolution), time.Duration(14*time.Millisecond))
|
||||
gobottest.Assert(t, bmp180PauseForReading(BMP180UltraHighResolution), time.Duration(26*time.Millisecond))
|
||||
}
|
||||
|
@ -3,18 +3,70 @@ package i2c
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"log"
|
||||
"math"
|
||||
|
||||
"gobot.io/x/gobot"
|
||||
)
|
||||
|
||||
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 (
|
||||
bmp280RegisterControl = 0xf4
|
||||
bmp280RegisterConfig = 0xf5
|
||||
bmp280RegisterPressureData = 0xf7
|
||||
bmp280RegisterTempData = 0xfa
|
||||
bmp280RegisterCalib00 = 0x88
|
||||
bmp280SeaLevelPressure = 1013.25
|
||||
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 {
|
||||
@ -34,76 +86,82 @@ type bmp280CalibrationCoefficients struct {
|
||||
|
||||
// BMP280Driver is a driver for the BMP280 temperature/pressure sensor
|
||||
type BMP280Driver struct {
|
||||
name string
|
||||
connector Connector
|
||||
connection Connection
|
||||
Config
|
||||
|
||||
tpc *bmp280CalibrationCoefficients
|
||||
*Driver
|
||||
calCoeffs *bmp280CalibrationCoefficients
|
||||
ctrlPwrMode uint8
|
||||
ctrlPressOversamp BMP280PressureOversampling
|
||||
ctrlTempOversamp BMP280TemperatureOversampling
|
||||
confFilter BMP280IIRFilter
|
||||
}
|
||||
|
||||
// NewBMP280Driver creates a new driver with specified i2c interface.
|
||||
// Params:
|
||||
// conn Connector - the Adaptor to use with this Driver
|
||||
// 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 {
|
||||
b := &BMP280Driver{
|
||||
name: gobot.DefaultName("BMP280"),
|
||||
connector: c,
|
||||
Config: NewConfig(),
|
||||
tpc: &bmp280CalibrationCoefficients{},
|
||||
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(b)
|
||||
option(d)
|
||||
}
|
||||
|
||||
// TODO: expose commands to API
|
||||
return b
|
||||
return d
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
if err := d.initialization(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Halt halts the device.
|
||||
func (d *BMP280Driver) Halt() (err error) {
|
||||
return nil
|
||||
// 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
|
||||
@ -114,6 +172,9 @@ func (d *BMP280Driver) Temperature() (temp float32, err error) {
|
||||
|
||||
// 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
|
||||
@ -140,34 +201,38 @@ func (d *BMP280Driver) Altitude() (alt float32, err error) {
|
||||
|
||||
// initialization reads the calibration coefficients.
|
||||
func (d *BMP280Driver) initialization() (err error) {
|
||||
var coefficients []byte
|
||||
if coefficients, err = d.read(bmp280RegisterCalib00, 24); err != nil {
|
||||
coefficients := make([]byte, 24)
|
||||
if err = d.connection.ReadBlockData(bmp280RegCalib00, coefficients); 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)
|
||||
binary.Read(buf, binary.LittleEndian, &d.calCoeffs.t1)
|
||||
binary.Read(buf, binary.LittleEndian, &d.calCoeffs.t2)
|
||||
binary.Read(buf, binary.LittleEndian, &d.calCoeffs.t3)
|
||||
binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p1)
|
||||
binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p2)
|
||||
binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p3)
|
||||
binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p4)
|
||||
binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p5)
|
||||
binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p6)
|
||||
binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p7)
|
||||
binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p8)
|
||||
binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p9)
|
||||
|
||||
d.connection.WriteByteData(bmp280RegisterControl, 0x3F)
|
||||
ctrlReg := uint8(d.ctrlPwrMode) | uint8(d.ctrlPressOversamp)<<2 | uint8(d.ctrlTempOversamp)<<5
|
||||
d.connection.WriteByteData(bmp280RegCtrl, ctrlReg)
|
||||
|
||||
confReg := uint8(bmp280ConfStandBy0005)<<2 | uint8(d.confFilter)<<5
|
||||
d.connection.WriteByteData(bmp280RegConf, confReg & ^uint8(bmp280ConfSPIBit))
|
||||
|
||||
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 {
|
||||
data := make([]byte, 3)
|
||||
if err = d.connection.ReadBlockData(bmp280RegTempData, data); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
buf := bytes.NewBuffer(data)
|
||||
@ -181,10 +246,10 @@ func (d *BMP280Driver) rawTemp() (temp int32, err error) {
|
||||
}
|
||||
|
||||
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 {
|
||||
data := make([]byte, 3)
|
||||
if err = d.connection.ReadBlockData(bmp280RegPressureData, data); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
buf := bytes.NewBuffer(data)
|
||||
@ -198,8 +263,8 @@ func (d *BMP280Driver) rawPressure() (press int32, err error) {
|
||||
}
|
||||
|
||||
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)
|
||||
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)
|
||||
@ -210,33 +275,21 @@ 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
|
||||
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.tpc.p9) * (p >> 13) * (p >> 13)) >> 25
|
||||
var2 = (int64(d.tpc.p8) * p) >> 19
|
||||
var1 = (int64(d.calCoeffs.p9) * (p >> 13) * (p >> 13)) >> 25
|
||||
var2 = (int64(d.calCoeffs.p8) * p) >> 19
|
||||
|
||||
p = ((p + var1 + var2) >> 8) + (int64(d.tpc.p7) << 4)
|
||||
p = ((p + var1 + var2) >> 8) + (int64(d.calCoeffs.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
|
||||
}
|
||||
|
@ -3,156 +3,209 @@ package i2c
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gobot.io/x/gobot"
|
||||
"gobot.io/x/gobot/gobottest"
|
||||
)
|
||||
|
||||
// this ensures that the implementation is based on i2c.Driver, which implements the gobot.Driver
|
||||
// and tests all implementations, so no further tests needed here for gobot.Driver interface
|
||||
var _ gobot.Driver = (*BMP280Driver)(nil)
|
||||
|
||||
// --------- HELPERS
|
||||
func initTestBMP280Driver() (driver *BMP280Driver) {
|
||||
driver, _ = initTestBMP280DriverWithStubbedAdaptor()
|
||||
return
|
||||
}
|
||||
|
||||
func initTestBMP280DriverWithStubbedAdaptor() (*BMP280Driver, *i2cTestAdaptor) {
|
||||
func initTestBMP280WithStubbedAdaptor() (*BMP280Driver, *i2cTestAdaptor) {
|
||||
adaptor := newI2cTestAdaptor()
|
||||
return NewBMP280Driver(adaptor), adaptor
|
||||
}
|
||||
|
||||
// --------- TESTS
|
||||
|
||||
func TestNewBMP280Driver(t *testing.T) {
|
||||
// Does it return a pointer to an instance of BME280Driver?
|
||||
var bmp280 interface{} = NewBMP280Driver(newI2cTestAdaptor())
|
||||
_, ok := bmp280.(*BMP280Driver)
|
||||
var di interface{} = NewBMP280Driver(newI2cTestAdaptor())
|
||||
d, ok := di.(*BMP280Driver)
|
||||
if !ok {
|
||||
t.Errorf("NewBMP280Driver() should have returned a *BMP280Driver")
|
||||
}
|
||||
gobottest.Refute(t, d.Driver, nil)
|
||||
gobottest.Assert(t, strings.HasPrefix(d.Name(), "BMP280"), true)
|
||||
gobottest.Assert(t, d.defaultAddress, 0x77)
|
||||
gobottest.Assert(t, d.ctrlPwrMode, uint8(0x03))
|
||||
gobottest.Assert(t, d.ctrlPressOversamp, BMP280PressureOversampling(0x05))
|
||||
gobottest.Assert(t, d.ctrlTempOversamp, BMP280TemperatureOversampling(0x01))
|
||||
gobottest.Assert(t, d.confFilter, BMP280IIRFilter(0x00))
|
||||
gobottest.Refute(t, d.calCoeffs, nil)
|
||||
}
|
||||
|
||||
func TestBMP280Driver(t *testing.T) {
|
||||
bmp280 := initTestBMP280Driver()
|
||||
gobottest.Refute(t, bmp280.Connection(), nil)
|
||||
func TestBMP280Options(t *testing.T) {
|
||||
// This is a general test, that options are applied in constructor by using the common WithBus() option and
|
||||
// least one of this driver. Further tests for options can also be done by call of "WithOption(val)(d)".
|
||||
d := NewBMP280Driver(newI2cTestAdaptor(), WithBus(2), WithBMP280PressureOversampling(0x04))
|
||||
gobottest.Assert(t, d.GetBusOrDefault(1), 2)
|
||||
gobottest.Assert(t, d.ctrlPressOversamp, BMP280PressureOversampling(0x04))
|
||||
}
|
||||
|
||||
func TestBMP280DriverStart(t *testing.T) {
|
||||
bmp280, _ := initTestBMP280DriverWithStubbedAdaptor()
|
||||
gobottest.Assert(t, bmp280.Start(), nil)
|
||||
func TestWithBMP280TemperatureOversampling(t *testing.T) {
|
||||
// arrange
|
||||
d, a := initTestBMP280WithStubbedAdaptor()
|
||||
a.written = []byte{} // reset writes of former test
|
||||
const (
|
||||
setVal = BMP280TemperatureOversampling(0x04) // 8 x
|
||||
)
|
||||
// act
|
||||
WithBMP280TemperatureOversampling(setVal)(d)
|
||||
// assert
|
||||
gobottest.Assert(t, d.ctrlTempOversamp, setVal)
|
||||
gobottest.Assert(t, len(a.written), 0)
|
||||
}
|
||||
|
||||
func TestBMP280StartConnectError(t *testing.T) {
|
||||
d, adaptor := initTestBMP280DriverWithStubbedAdaptor()
|
||||
adaptor.Testi2cConnectErr(true)
|
||||
gobottest.Assert(t, d.Start(), errors.New("Invalid i2c connection"))
|
||||
func TestWithBMP280IIRFilter(t *testing.T) {
|
||||
// arrange
|
||||
d, a := initTestBMP280WithStubbedAdaptor()
|
||||
a.written = []byte{} // reset writes of former test
|
||||
const (
|
||||
setVal = BMP280IIRFilter(0x02) // 4 x
|
||||
)
|
||||
// act
|
||||
WithBMP280IIRFilter(setVal)(d)
|
||||
// assert
|
||||
gobottest.Assert(t, d.confFilter, setVal)
|
||||
gobottest.Assert(t, len(a.written), 0)
|
||||
}
|
||||
|
||||
func TestBMP280DriverStartWriteError(t *testing.T) {
|
||||
bmp280, adaptor := initTestBMP280DriverWithStubbedAdaptor()
|
||||
adaptor.i2cWriteImpl = func([]byte) (int, error) {
|
||||
return 0, errors.New("write error")
|
||||
}
|
||||
gobottest.Assert(t, bmp280.Start(), errors.New("write error"))
|
||||
}
|
||||
|
||||
func TestBMP280DriverStartReadError(t *testing.T) {
|
||||
bmp280, adaptor := initTestBMP280DriverWithStubbedAdaptor()
|
||||
adaptor.i2cReadImpl = func(b []byte) (int, error) {
|
||||
return 0, errors.New("read error")
|
||||
}
|
||||
gobottest.Assert(t, bmp280.Start(), errors.New("read error"))
|
||||
}
|
||||
|
||||
func TestBMP280DriverHalt(t *testing.T) {
|
||||
bmp280 := initTestBMP280Driver()
|
||||
|
||||
gobottest.Assert(t, bmp280.Halt(), nil)
|
||||
}
|
||||
|
||||
func TestBMP280DriverMeasurements(t *testing.T) {
|
||||
bmp280, adaptor := initTestBMP280DriverWithStubbedAdaptor()
|
||||
func TestBMP280Measurements(t *testing.T) {
|
||||
d, adaptor := initTestBMP280WithStubbedAdaptor()
|
||||
adaptor.i2cReadImpl = func(b []byte) (int, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
// Values produced by dumping data from actual sensor
|
||||
if adaptor.written[len(adaptor.written)-1] == bmp280RegisterCalib00 {
|
||||
if adaptor.written[len(adaptor.written)-1] == bmp280RegCalib00 {
|
||||
buf.Write([]byte{126, 109, 214, 102, 50, 0, 54, 149, 220, 213, 208, 11, 64, 30, 166, 255, 249, 255, 172, 38, 10, 216, 189, 16})
|
||||
} else if adaptor.written[len(adaptor.written)-1] == bmp280RegisterTempData {
|
||||
} else if adaptor.written[len(adaptor.written)-1] == bmp280RegTempData {
|
||||
buf.Write([]byte{128, 243, 0})
|
||||
} else if adaptor.written[len(adaptor.written)-1] == bmp280RegisterPressureData {
|
||||
} else if adaptor.written[len(adaptor.written)-1] == bmp280RegPressureData {
|
||||
buf.Write([]byte{77, 23, 48})
|
||||
}
|
||||
copy(b, buf.Bytes())
|
||||
return buf.Len(), nil
|
||||
}
|
||||
bmp280.Start()
|
||||
temp, err := bmp280.Temperature()
|
||||
d.Start()
|
||||
temp, err := d.Temperature()
|
||||
gobottest.Assert(t, err, nil)
|
||||
gobottest.Assert(t, temp, float32(25.014637))
|
||||
pressure, err := bmp280.Pressure()
|
||||
pressure, err := d.Pressure()
|
||||
gobottest.Assert(t, err, nil)
|
||||
gobottest.Assert(t, pressure, float32(99545.414))
|
||||
alt, err := bmp280.Altitude()
|
||||
alt, err := d.Altitude()
|
||||
gobottest.Assert(t, err, nil)
|
||||
gobottest.Assert(t, alt, float32(149.22713))
|
||||
}
|
||||
|
||||
func TestBMP280DriverTemperatureWriteError(t *testing.T) {
|
||||
bmp280, adaptor := initTestBMP280DriverWithStubbedAdaptor()
|
||||
bmp280.Start()
|
||||
func TestBMP280TemperatureWriteError(t *testing.T) {
|
||||
d, adaptor := initTestBMP280WithStubbedAdaptor()
|
||||
d.Start()
|
||||
|
||||
adaptor.i2cWriteImpl = func([]byte) (int, error) {
|
||||
return 0, errors.New("write error")
|
||||
}
|
||||
temp, err := bmp280.Temperature()
|
||||
temp, err := d.Temperature()
|
||||
gobottest.Assert(t, err, errors.New("write error"))
|
||||
gobottest.Assert(t, temp, float32(0.0))
|
||||
}
|
||||
|
||||
func TestBMP280DriverTemperatureReadError(t *testing.T) {
|
||||
bmp280, adaptor := initTestBMP280DriverWithStubbedAdaptor()
|
||||
bmp280.Start()
|
||||
func TestBMP280TemperatureReadError(t *testing.T) {
|
||||
d, adaptor := initTestBMP280WithStubbedAdaptor()
|
||||
d.Start()
|
||||
|
||||
adaptor.i2cReadImpl = func([]byte) (int, error) {
|
||||
return 0, errors.New("read error")
|
||||
}
|
||||
temp, err := bmp280.Temperature()
|
||||
temp, err := d.Temperature()
|
||||
gobottest.Assert(t, err, errors.New("read error"))
|
||||
gobottest.Assert(t, temp, float32(0.0))
|
||||
}
|
||||
|
||||
func TestBMP280DriverPressureWriteError(t *testing.T) {
|
||||
bmp280, adaptor := initTestBMP280DriverWithStubbedAdaptor()
|
||||
bmp280.Start()
|
||||
func TestBMP280PressureWriteError(t *testing.T) {
|
||||
d, adaptor := initTestBMP280WithStubbedAdaptor()
|
||||
d.Start()
|
||||
|
||||
adaptor.i2cWriteImpl = func([]byte) (int, error) {
|
||||
return 0, errors.New("write error")
|
||||
}
|
||||
press, err := bmp280.Pressure()
|
||||
press, err := d.Pressure()
|
||||
gobottest.Assert(t, err, errors.New("write error"))
|
||||
gobottest.Assert(t, press, float32(0.0))
|
||||
}
|
||||
|
||||
func TestBMP280DriverPressureReadError(t *testing.T) {
|
||||
bmp280, adaptor := initTestBMP280DriverWithStubbedAdaptor()
|
||||
bmp280.Start()
|
||||
func TestBMP280PressureReadError(t *testing.T) {
|
||||
d, adaptor := initTestBMP280WithStubbedAdaptor()
|
||||
d.Start()
|
||||
|
||||
adaptor.i2cReadImpl = func([]byte) (int, error) {
|
||||
return 0, errors.New("read error")
|
||||
}
|
||||
press, err := bmp280.Pressure()
|
||||
press, err := d.Pressure()
|
||||
gobottest.Assert(t, err, errors.New("read error"))
|
||||
gobottest.Assert(t, press, float32(0.0))
|
||||
}
|
||||
|
||||
func TestBMP280DriverSetName(t *testing.T) {
|
||||
b := initTestBMP280Driver()
|
||||
b.SetName("TESTME")
|
||||
gobottest.Assert(t, b.Name(), "TESTME")
|
||||
}
|
||||
|
||||
func TestBMP280DriverOptions(t *testing.T) {
|
||||
b := NewBMP280Driver(newI2cTestAdaptor(), WithBus(2))
|
||||
gobottest.Assert(t, b.GetBusOrDefault(1), 2)
|
||||
func TestBMP280_initialization(t *testing.T) {
|
||||
// sequence to read and write in initialization():
|
||||
// * read 24 bytes (12 x 16 bit calibration data), starting from TC1 register (0x88)
|
||||
// * fill calibration struct with data (LSByte read first)
|
||||
// * prepare the content of control register
|
||||
// * write the control register (0xF4)
|
||||
// * prepare the content of config register
|
||||
// * write the config register (0xF5)
|
||||
// arrange
|
||||
d, a := initTestBMP280WithStubbedAdaptor()
|
||||
a.written = []byte{} // reset writes of former test
|
||||
const (
|
||||
wantCalibReg = uint8(0x88)
|
||||
wantCtrlReg = uint8(0xF4)
|
||||
wantCtrlRegVal = uint8(0x37) // normal power mode, 16 x pressure and 1 x temperature oversampling
|
||||
wantConfReg = uint8(0xF5)
|
||||
wantConfRegVal = uint8(0x00) // no SPI, no filter, smallest standby (unused, because normal power mode)
|
||||
)
|
||||
// Values from the datasheet example.
|
||||
t1 := []uint8{0x70, 0x6B}
|
||||
t2 := []uint8{0x43, 0x67}
|
||||
t3 := []uint8{0x18, 0xFC}
|
||||
p1 := []uint8{0x7D, 0x8E}
|
||||
p2 := []uint8{0x43, 0xD6}
|
||||
p3 := []uint8{0xD0, 0x0B}
|
||||
p4 := []uint8{0x27, 0x0B}
|
||||
p5 := []uint8{0x8C, 0x00}
|
||||
p6 := []uint8{0xF9, 0xFF}
|
||||
p7 := []uint8{0x8C, 0x3C}
|
||||
p8 := []uint8{0xF8, 0xC6}
|
||||
p9 := []uint8{0x70, 0x17}
|
||||
returnRead := append(append(append(append(append(append(t1, t2...), t3...), p1...), p2...), p3...), p4...)
|
||||
returnRead = append(append(append(append(append(returnRead, p5...), p6...), p7...), p8...), p9...)
|
||||
numCallsRead := 0
|
||||
a.i2cReadImpl = func(b []byte) (int, error) {
|
||||
numCallsRead++
|
||||
copy(b, returnRead)
|
||||
return len(b), nil
|
||||
}
|
||||
// act, assert - initialization() must be called on Start()
|
||||
err := d.Start()
|
||||
// assert
|
||||
gobottest.Assert(t, err, nil)
|
||||
gobottest.Assert(t, numCallsRead, 1)
|
||||
gobottest.Assert(t, len(a.written), 5)
|
||||
gobottest.Assert(t, a.written[0], wantCalibReg)
|
||||
gobottest.Assert(t, a.written[1], wantCtrlReg)
|
||||
gobottest.Assert(t, a.written[2], wantCtrlRegVal)
|
||||
gobottest.Assert(t, a.written[3], wantConfReg)
|
||||
gobottest.Assert(t, a.written[4], wantConfRegVal)
|
||||
gobottest.Assert(t, d.calCoeffs.t1, uint16(27504))
|
||||
gobottest.Assert(t, d.calCoeffs.t2, int16(26435))
|
||||
gobottest.Assert(t, d.calCoeffs.t3, int16(-1000))
|
||||
gobottest.Assert(t, d.calCoeffs.p1, uint16(36477))
|
||||
gobottest.Assert(t, d.calCoeffs.p2, int16(-10685))
|
||||
gobottest.Assert(t, d.calCoeffs.p3, int16(3024))
|
||||
gobottest.Assert(t, d.calCoeffs.p4, int16(2855))
|
||||
gobottest.Assert(t, d.calCoeffs.p5, int16(140))
|
||||
gobottest.Assert(t, d.calCoeffs.p6, int16(-7))
|
||||
gobottest.Assert(t, d.calCoeffs.p7, int16(15500))
|
||||
gobottest.Assert(t, d.calCoeffs.p8, int16(-14600))
|
||||
gobottest.Assert(t, d.calCoeffs.p9, int16(6000))
|
||||
}
|
||||
|
@ -4,57 +4,66 @@ import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
|
||||
"gobot.io/x/gobot"
|
||||
)
|
||||
|
||||
const bmp388Debug = false
|
||||
|
||||
// the default address is applicable for SDO to VDD, for SDO to GND it will be 0x76
|
||||
const bmp388DefaultAddress = 0x77
|
||||
|
||||
// BMP388Accuracy accuracy type
|
||||
type BMP388Accuracy uint8
|
||||
type BMP388IIRFilter uint8
|
||||
|
||||
const (
|
||||
bmp388ChipID = 0x50
|
||||
|
||||
bmp388RegisterChipID = 0x00
|
||||
bmp388RegisterStatus = 0x03
|
||||
bmp388RegisterConfig = 0x1F
|
||||
bmp388RegisterPressureData = 0x04
|
||||
bmp388RegisterTempData = 0x07
|
||||
bmp388RegisterCalib00 = 0x31
|
||||
bmp388RegisterCMD = 0x7E
|
||||
// CMD : 0x00 nop (reserved. No command.)
|
||||
// : 0x34 extmode_en_middle
|
||||
// : 0xB0 fifo_flush (Clears all data in the FIFO, does not change FIFO_CONFIG registers)
|
||||
// : 0xB6 softreset (Triggers a reset, all user configuration settings are overwritten with their default state)
|
||||
bmp388RegisterODR = 0x1D // Output Data Rates
|
||||
bmp388RegisterOSR = 0x1C // Oversampling Rates
|
||||
bmp388RegChipID = 0x00
|
||||
bmp388RegStatus = 0x03
|
||||
bmp388RegPressureData = 0x04 // XLSB, 0x05 LSByte, 0x06 MSByte
|
||||
bmp388RegTempData = 0x07 // XLSB, 0x08 LSByte, 0x09 MSByte
|
||||
bmp388RegPWRCTRL = 0x1B // enable/disable pressure and temperature measurement, mode
|
||||
bmp388RegOSR = 0x1C // Oversampling Rates
|
||||
bmp388RegODR = 0x1D // Output Data Rates
|
||||
bmp388RegConf = 0x1F // config filter for IIR coefficients
|
||||
bmp388RegCalib00 = 0x31
|
||||
bmp388RegCMD = 0x7E
|
||||
|
||||
bmp388RegisterPWRCTRL = 0x1B
|
||||
bmp388PWRCTRLSleep = 0
|
||||
bmp388PWRCTRLForced = 1
|
||||
bmp388PWRCTRLNormal = 3
|
||||
// bits 0, 1 of control register
|
||||
bmp388PWRCTRLPressEnableBit = 0x01
|
||||
bmp388PWRCTRLTempEnableBit = 0x02
|
||||
|
||||
bmp388SeaLevelPressure = 1013.25
|
||||
// bits 4, 5 of control register (will be shifted on write)
|
||||
bmp388PWRCTRLSleep = 0x00
|
||||
bmp388PWRCTRLForced = 0x01 // same as 0x02
|
||||
bmp388PWRCTRLNormal = 0x03
|
||||
|
||||
// IIR filter coefficients
|
||||
bmp388IIRFIlterCoef0 = 0 // bypass-mode
|
||||
bmp388IIRFIlterCoef1 = 1
|
||||
bmp388IIRFIlterCoef3 = 2
|
||||
bmp388IIRFIlterCoef7 = 3
|
||||
bmp388IIRFIlterCoef15 = 4
|
||||
bmp388IIRFIlterCoef31 = 5
|
||||
bmp388IIRFIlterCoef63 = 6
|
||||
bmp388IIRFIlterCoef127 = 7
|
||||
)
|
||||
// bits 1, 2 ,3 of config filter IIR filter coefficients (will be shifted on write)
|
||||
bmp388ConfFilterCoef0 BMP388IIRFilter = 0 // bypass-mode
|
||||
bmp388ConfFilterCoef1 BMP388IIRFilter = 1
|
||||
bmp388ConfFilterCoef3 BMP388IIRFilter = 2
|
||||
bmp388ConfFilterCoef7 BMP388IIRFilter = 3
|
||||
bmp388ConfFilterCoef15 BMP388IIRFilter = 4
|
||||
bmp388ConfFilterCoef31 BMP388IIRFilter = 5
|
||||
bmp388ConfFilterCoef63 BMP388IIRFilter = 6
|
||||
bmp388ConfFilterCoef127 BMP388IIRFilter = 7
|
||||
|
||||
// BMP388Accuracy accuracy type
|
||||
type BMP388Accuracy uint8
|
||||
|
||||
// BMP388Accuracy accuracy modes
|
||||
const (
|
||||
// oversampling rate, a single value is used (could be different for pressure and temperature)
|
||||
BMP388AccuracyUltraLow BMP388Accuracy = 0 // x1 sample
|
||||
BMP388AccuracyLow BMP388Accuracy = 1 // x2 samples
|
||||
BMP388AccuracyStandard BMP388Accuracy = 2 // x4 samples
|
||||
BMP388AccuracyHigh BMP388Accuracy = 3 // x8 samples
|
||||
BMP388AccuracyUltraHigh BMP388Accuracy = 4 // x16 samples
|
||||
BMP388AccuracyHighest BMP388Accuracy = 5 // x32 samples
|
||||
|
||||
bmp388CMDReserved = 0x00 // reserved, no command
|
||||
bmp388CMDExtModeEnMiddle = 0x34
|
||||
bmp388CMDFifoFlush = 0xB0 // clears all data in the FIFO, does not change FIFO_CONFIG registers
|
||||
bmp388CMDSoftReset = 0xB6 // triggers a reset, all user configuration settings are overwritten with their default state
|
||||
|
||||
bmp388SeaLevelPressure = 1013.25
|
||||
)
|
||||
|
||||
type bmp388CalibrationCoefficients struct {
|
||||
@ -76,96 +85,63 @@ type bmp388CalibrationCoefficients struct {
|
||||
|
||||
// BMP388Driver is a driver for the BMP388 temperature/pressure sensor
|
||||
type BMP388Driver struct {
|
||||
name string
|
||||
connector Connector
|
||||
connection Connection
|
||||
Config
|
||||
|
||||
tpc *bmp388CalibrationCoefficients
|
||||
*Driver
|
||||
calCoeffs *bmp388CalibrationCoefficients
|
||||
ctrlPwrMode uint8
|
||||
confFilter BMP388IIRFilter
|
||||
}
|
||||
|
||||
// NewBMP388Driver creates a new driver with specified i2c interface.
|
||||
// Params:
|
||||
// conn Connector - the Adaptor to use with this Driver
|
||||
// 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 NewBMP388Driver(c Connector, options ...func(Config)) *BMP388Driver {
|
||||
b := &BMP388Driver{
|
||||
name: gobot.DefaultName("BMP388"),
|
||||
connector: c,
|
||||
Config: NewConfig(),
|
||||
tpc: &bmp388CalibrationCoefficients{},
|
||||
d := &BMP388Driver{
|
||||
Driver: NewDriver(c, "BMP388", bmp388DefaultAddress),
|
||||
calCoeffs: &bmp388CalibrationCoefficients{},
|
||||
ctrlPwrMode: bmp388PWRCTRLForced,
|
||||
confFilter: bmp388ConfFilterCoef0,
|
||||
}
|
||||
d.afterStart = d.initialization
|
||||
|
||||
for _, option := range options {
|
||||
option(b)
|
||||
option(d)
|
||||
}
|
||||
|
||||
// TODO: expose commands to API
|
||||
return b
|
||||
return d
|
||||
}
|
||||
|
||||
// Name returns the name of the device.
|
||||
func (d *BMP388Driver) Name() string {
|
||||
return d.name
|
||||
}
|
||||
|
||||
// SetName sets the name of the device.
|
||||
func (d *BMP388Driver) SetName(n string) {
|
||||
d.name = n
|
||||
}
|
||||
|
||||
// Connection returns the connection of the device.
|
||||
func (d *BMP388Driver) Connection() gobot.Connection {
|
||||
return d.connector.(gobot.Connection)
|
||||
}
|
||||
|
||||
// Start initializes the BMP388 and loads the calibration coefficients.
|
||||
func (d *BMP388Driver) Start() (err error) {
|
||||
var chipID uint8
|
||||
|
||||
bus := d.GetBusOrDefault(d.connector.GetDefaultBus())
|
||||
address := d.GetAddressOrDefault(bmp180Address)
|
||||
|
||||
if d.connection, err = d.connector.GetConnection(address, bus); err != nil {
|
||||
return err
|
||||
// WithBMP388IIRFilter option sets count of IIR filter coefficients.
|
||||
// Valid settings are of type "BMP388IIRFilter"
|
||||
func WithBMP388IIRFilter(val BMP388IIRFilter) func(Config) {
|
||||
return func(c Config) {
|
||||
if d, ok := c.(*BMP388Driver); ok {
|
||||
d.confFilter = val
|
||||
} else if bmp388Debug {
|
||||
log.Printf("Trying to set IIR filter for non-BMP388Driver %v", c)
|
||||
}
|
||||
}
|
||||
|
||||
if chipID, err = d.connection.ReadByteData(bmp388RegisterChipID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if bmp388ChipID != chipID {
|
||||
return fmt.Errorf("Incorrect BMP388 chip ID '0%x' Expected 0x%x", chipID, bmp388ChipID)
|
||||
}
|
||||
|
||||
if err := d.initialization(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Halt halts the device.
|
||||
func (d *BMP388Driver) Halt() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Temperature returns the current temperature, in celsius degrees.
|
||||
func (d *BMP388Driver) Temperature(accuracy BMP388Accuracy) (temp float32, err error) {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
|
||||
var rawT int32
|
||||
|
||||
// Enable Pressure and Temperature measurement, set FORCED operating mode
|
||||
var mode byte = (bmp388PWRCTRLForced << 4) | 3 // 1100|1|1 == mode|T|P
|
||||
if err = d.connection.WriteByteData(bmp388RegisterPWRCTRL, mode); err != nil {
|
||||
mode := uint8(d.ctrlPwrMode)<<4 | bmp388PWRCTRLPressEnableBit | bmp388PWRCTRLTempEnableBit
|
||||
if err = d.connection.WriteByteData(bmp388RegPWRCTRL, mode); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Set Accuracy for temperature
|
||||
if err = d.connection.WriteByteData(bmp388RegisterOSR, uint8(accuracy<<3)); err != nil {
|
||||
if err = d.connection.WriteByteData(bmp388RegOSR, uint8(accuracy<<3)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@ -178,16 +154,18 @@ func (d *BMP388Driver) Temperature(accuracy BMP388Accuracy) (temp float32, err e
|
||||
|
||||
// Pressure returns the current barometric pressure, in Pa
|
||||
func (d *BMP388Driver) Pressure(accuracy BMP388Accuracy) (press float32, err error) {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
|
||||
var rawT, rawP int32
|
||||
|
||||
// Enable Pressure and Temperature measurement, set FORCED operating mode
|
||||
var mode byte = (bmp388PWRCTRLForced << 4) | 3 // 1100|1|1 == mode|T|P
|
||||
if err = d.connection.WriteByteData(bmp388RegisterPWRCTRL, mode); err != nil {
|
||||
mode := uint8(d.ctrlPwrMode)<<4 | bmp388PWRCTRLPressEnableBit | bmp388PWRCTRLTempEnableBit
|
||||
if err = d.connection.WriteByteData(bmp388RegPWRCTRL, mode); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Set Standard Accuracy for pressure
|
||||
if err = d.connection.WriteByteData(bmp388RegisterOSR, uint8(accuracy)); err != nil {
|
||||
if err = d.connection.WriteByteData(bmp388RegOSR, uint8(accuracy)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@ -215,25 +193,34 @@ func (d *BMP388Driver) Altitude(accuracy BMP388Accuracy) (alt float32, err error
|
||||
|
||||
// initialization reads the calibration coefficients.
|
||||
func (d *BMP388Driver) initialization() (err error) {
|
||||
var chipID uint8
|
||||
if chipID, err = d.connection.ReadByteData(bmp388RegChipID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if bmp388ChipID != chipID {
|
||||
return fmt.Errorf("Incorrect BMP388 chip ID '0%x' Expected 0x%x", chipID, bmp388ChipID)
|
||||
}
|
||||
|
||||
var (
|
||||
coefficients []byte
|
||||
t1 uint16
|
||||
t2 uint16
|
||||
t3 int8
|
||||
p1 int16
|
||||
p2 int16
|
||||
p3 int8
|
||||
p4 int8
|
||||
p5 uint16
|
||||
p6 uint16
|
||||
p7 int8
|
||||
p8 int8
|
||||
p9 int16
|
||||
p10 int8
|
||||
p11 int8
|
||||
t1 uint16
|
||||
t2 uint16
|
||||
t3 int8
|
||||
p1 int16
|
||||
p2 int16
|
||||
p3 int8
|
||||
p4 int8
|
||||
p5 uint16
|
||||
p6 uint16
|
||||
p7 int8
|
||||
p8 int8
|
||||
p9 int16
|
||||
p10 int8
|
||||
p11 int8
|
||||
)
|
||||
|
||||
if coefficients, err = d.read(bmp388RegisterCalib00, 24); err != nil {
|
||||
coefficients := make([]byte, 24)
|
||||
if err = d.connection.ReadBlockData(bmp388RegCalib00, coefficients); err != nil {
|
||||
return err
|
||||
}
|
||||
buf := bytes.NewBuffer(coefficients)
|
||||
@ -253,29 +240,26 @@ func (d *BMP388Driver) initialization() (err error) {
|
||||
binary.Read(buf, binary.LittleEndian, &p10)
|
||||
binary.Read(buf, binary.LittleEndian, &p11)
|
||||
|
||||
d.tpc.t1 = float32(float64(t1) / math.Pow(2, -8))
|
||||
d.tpc.t2 = float32(float64(t2) / math.Pow(2, 30))
|
||||
d.tpc.t3 = float32(float64(t3) / math.Pow(2, 48))
|
||||
d.tpc.p1 = float32((float64(p1) - math.Pow(2, 14)) / math.Pow(2, 20))
|
||||
d.tpc.p2 = float32((float64(p2) - math.Pow(2, 14)) / math.Pow(2, 29))
|
||||
d.tpc.p3 = float32(float64(p3) / math.Pow(2, 32))
|
||||
d.tpc.p4 = float32(float64(p4) / math.Pow(2, 37))
|
||||
d.tpc.p5 = float32(float64(p5) / math.Pow(2, -3))
|
||||
d.tpc.p6 = float32(float64(p6) / math.Pow(2, 6))
|
||||
d.tpc.p7 = float32(float64(p7) / math.Pow(2, 8))
|
||||
d.tpc.p8 = float32(float64(p8) / math.Pow(2, 15))
|
||||
d.tpc.p9 = float32(float64(p9) / math.Pow(2, 48))
|
||||
d.tpc.p10 = float32(float64(p10) / math.Pow(2, 48))
|
||||
d.tpc.p11 = float32(float64(p11) / math.Pow(2, 65))
|
||||
d.calCoeffs.t1 = float32(float64(t1) / math.Pow(2, -8))
|
||||
d.calCoeffs.t2 = float32(float64(t2) / math.Pow(2, 30))
|
||||
d.calCoeffs.t3 = float32(float64(t3) / math.Pow(2, 48))
|
||||
d.calCoeffs.p1 = float32((float64(p1) - math.Pow(2, 14)) / math.Pow(2, 20))
|
||||
d.calCoeffs.p2 = float32((float64(p2) - math.Pow(2, 14)) / math.Pow(2, 29))
|
||||
d.calCoeffs.p3 = float32(float64(p3) / math.Pow(2, 32))
|
||||
d.calCoeffs.p4 = float32(float64(p4) / math.Pow(2, 37))
|
||||
d.calCoeffs.p5 = float32(float64(p5) / math.Pow(2, -3))
|
||||
d.calCoeffs.p6 = float32(float64(p6) / math.Pow(2, 6))
|
||||
d.calCoeffs.p7 = float32(float64(p7) / math.Pow(2, 8))
|
||||
d.calCoeffs.p8 = float32(float64(p8) / math.Pow(2, 15))
|
||||
d.calCoeffs.p9 = float32(float64(p9) / math.Pow(2, 48))
|
||||
d.calCoeffs.p10 = float32(float64(p10) / math.Pow(2, 48))
|
||||
d.calCoeffs.p11 = float32(float64(p11) / math.Pow(2, 65))
|
||||
|
||||
// Perform a power on reset. All user configuration settings are overwritten
|
||||
// with their default state.
|
||||
if err = d.connection.WriteByteData(bmp388RegisterCMD, 0xB6); err != nil {
|
||||
if err = d.connection.WriteByteData(bmp388RegCMD, bmp388CMDSoftReset); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// set IIR filter to off
|
||||
if err = d.connection.WriteByteData(bmp388RegisterConfig, bmp388IIRFIlterCoef0<<1); err != nil {
|
||||
if err = d.connection.WriteByteData(bmp388RegConf, uint8(d.confFilter)<<1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -283,10 +267,10 @@ func (d *BMP388Driver) initialization() (err error) {
|
||||
}
|
||||
|
||||
func (d *BMP388Driver) rawTemp() (temp int32, err error) {
|
||||
var data []byte
|
||||
var tp0, tp1, tp2 byte
|
||||
|
||||
if data, err = d.read(bmp388RegisterTempData, 3); err != nil {
|
||||
data := make([]byte, 3)
|
||||
if err = d.connection.ReadBlockData(bmp388RegTempData, data); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
buf := bytes.NewBuffer(data)
|
||||
@ -300,10 +284,10 @@ func (d *BMP388Driver) rawTemp() (temp int32, err error) {
|
||||
}
|
||||
|
||||
func (d *BMP388Driver) rawPressure() (press int32, err error) {
|
||||
var data []byte
|
||||
var tp0, tp1, tp2 byte
|
||||
|
||||
if data, err = d.read(bmp388RegisterPressureData, 3); err != nil {
|
||||
data := make([]byte, 3)
|
||||
if err = d.connection.ReadBlockData(bmp388RegPressureData, data); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
buf := bytes.NewBuffer(data)
|
||||
@ -319,43 +303,31 @@ func (d *BMP388Driver) rawPressure() (press int32, err error) {
|
||||
|
||||
func (d *BMP388Driver) calculateTemp(rawTemp int32) float32 {
|
||||
// datasheet, sec 9.2 Temperature compensation
|
||||
pd1 := float32(rawTemp) - d.tpc.t1
|
||||
pd2 := pd1 * d.tpc.t2
|
||||
pd1 := float32(rawTemp) - d.calCoeffs.t1
|
||||
pd2 := pd1 * d.calCoeffs.t2
|
||||
|
||||
temperatureComp := pd2 + (pd1*pd1)*d.tpc.t3
|
||||
temperatureComp := pd2 + (pd1*pd1)*d.calCoeffs.t3
|
||||
|
||||
return temperatureComp
|
||||
}
|
||||
|
||||
func (d *BMP388Driver) calculatePress(rawPress int32, tLin float64) float32 {
|
||||
pd1 := float64(d.tpc.p6) * tLin
|
||||
pd2 := float64(d.tpc.p7) * math.Pow(tLin, 2)
|
||||
pd3 := float64(d.tpc.p8) * math.Pow(tLin, 3)
|
||||
po1 := float64(d.tpc.p5) + pd1 + pd2 + pd3
|
||||
pd1 := float64(d.calCoeffs.p6) * tLin
|
||||
pd2 := float64(d.calCoeffs.p7) * math.Pow(tLin, 2)
|
||||
pd3 := float64(d.calCoeffs.p8) * math.Pow(tLin, 3)
|
||||
po1 := float64(d.calCoeffs.p5) + pd1 + pd2 + pd3
|
||||
|
||||
pd1 = float64(d.tpc.p2) * tLin
|
||||
pd2 = float64(d.tpc.p3) * math.Pow(tLin, 2)
|
||||
pd3 = float64(d.tpc.p4) * math.Pow(tLin, 3)
|
||||
po2 := float64(rawPress) * (float64(d.tpc.p1) + pd1 + pd2 + pd3)
|
||||
pd1 = float64(d.calCoeffs.p2) * tLin
|
||||
pd2 = float64(d.calCoeffs.p3) * math.Pow(tLin, 2)
|
||||
pd3 = float64(d.calCoeffs.p4) * math.Pow(tLin, 3)
|
||||
po2 := float64(rawPress) * (float64(d.calCoeffs.p1) + pd1 + pd2 + pd3)
|
||||
|
||||
pd1 = math.Pow(float64(rawPress), 2)
|
||||
pd2 = float64(d.tpc.p9) + float64(d.tpc.p10)*tLin
|
||||
pd2 = float64(d.calCoeffs.p9) + float64(d.calCoeffs.p10)*tLin
|
||||
pd3 = pd1 * pd2
|
||||
pd4 := pd3 + math.Pow(float64(rawPress), 3)*float64(d.tpc.p11)
|
||||
pd4 := pd3 + math.Pow(float64(rawPress), 3)*float64(d.calCoeffs.p11)
|
||||
|
||||
pressure := po1 + po2 + pd4
|
||||
|
||||
return float32(pressure)
|
||||
}
|
||||
|
||||
func (d *BMP388Driver) 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
|
||||
}
|
||||
|
@ -4,171 +4,197 @@ import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gobot.io/x/gobot"
|
||||
"gobot.io/x/gobot/gobottest"
|
||||
)
|
||||
|
||||
// this ensures that the implementation is based on i2c.Driver, which implements the gobot.Driver
|
||||
// and tests all implementations, so no further tests needed here for gobot.Driver interface
|
||||
var _ gobot.Driver = (*BMP388Driver)(nil)
|
||||
|
||||
// --------- HELPERS
|
||||
func initTestBMP388Driver() (driver *BMP388Driver) {
|
||||
driver, _ = initTestBMP388DriverWithStubbedAdaptor()
|
||||
return
|
||||
}
|
||||
func initTestBMP388WithStubbedAdaptor() (*BMP388Driver, *i2cTestAdaptor) {
|
||||
a := newI2cTestAdaptor()
|
||||
|
||||
func initTestBMP388DriverWithStubbedAdaptor() (*BMP388Driver, *i2cTestAdaptor) {
|
||||
adaptor := newI2cTestAdaptor()
|
||||
adaptor.i2cReadImpl = func(b []byte) (int, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
// Simulate returning of 0x50 for the
|
||||
// ReadByteData(bmp388RegisterChipID) call in initialisation()
|
||||
binary.Write(buf, binary.LittleEndian, uint8(0x50))
|
||||
copy(b, buf.Bytes())
|
||||
return buf.Len(), nil
|
||||
readCallCounter := 0
|
||||
a.i2cReadImpl = func(b []byte) (int, error) {
|
||||
readCallCounter++
|
||||
if readCallCounter == 1 {
|
||||
buf := new(bytes.Buffer)
|
||||
// Simulate returning of 0x50 for the
|
||||
// ReadByteData(bmp388RegChipID) call in initialisation()
|
||||
binary.Write(buf, binary.LittleEndian, uint8(0x50))
|
||||
copy(b, buf.Bytes())
|
||||
return buf.Len(), nil
|
||||
}
|
||||
if readCallCounter == 2 {
|
||||
// Simulate returning 24 bytes for the coefficients (register bmp388RegCalib00)
|
||||
return 24, nil
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
return NewBMP388Driver(adaptor), adaptor
|
||||
return NewBMP388Driver(a), a
|
||||
}
|
||||
|
||||
// --------- TESTS
|
||||
|
||||
func TestNewBMP388Driver(t *testing.T) {
|
||||
// Does it return a pointer to an instance of BMP388Driver?
|
||||
var bmp388 interface{} = NewBMP388Driver(newI2cTestAdaptor())
|
||||
_, ok := bmp388.(*BMP388Driver)
|
||||
var di interface{} = NewBMP388Driver(newI2cTestAdaptor())
|
||||
d, ok := di.(*BMP388Driver)
|
||||
if !ok {
|
||||
t.Errorf("NewBMP388Driver() should have returned a *BMP388Driver")
|
||||
}
|
||||
gobottest.Refute(t, d.Driver, nil)
|
||||
gobottest.Assert(t, strings.HasPrefix(d.Name(), "BMP388"), true)
|
||||
gobottest.Assert(t, d.defaultAddress, 0x77)
|
||||
gobottest.Assert(t, d.ctrlPwrMode, uint8(0x01)) // forced mode
|
||||
gobottest.Assert(t, d.confFilter, BMP388IIRFilter(0x00)) // filter off
|
||||
gobottest.Refute(t, d.calCoeffs, nil)
|
||||
}
|
||||
|
||||
func TestBMP388Driver(t *testing.T) {
|
||||
bmp388 := initTestBMP388Driver()
|
||||
gobottest.Refute(t, bmp388.Connection(), nil)
|
||||
func TestBMP388Options(t *testing.T) {
|
||||
// This is a general test, that options are applied in constructor by using the common WithBus() option and
|
||||
// least one of this driver. Further tests for options can also be done by call of "WithOption(val)(d)".
|
||||
d := NewBMP388Driver(newI2cTestAdaptor(), WithBus(2), WithBMP388IIRFilter(BMP388IIRFilter(0x03)))
|
||||
gobottest.Assert(t, d.GetBusOrDefault(1), 2)
|
||||
gobottest.Assert(t, d.confFilter, BMP388IIRFilter(0x03))
|
||||
}
|
||||
|
||||
func TestBMP388DriverStart(t *testing.T) {
|
||||
bmp388, _ := initTestBMP388DriverWithStubbedAdaptor()
|
||||
gobottest.Assert(t, bmp388.Start(), nil)
|
||||
}
|
||||
|
||||
func TestBMP388StartConnectError(t *testing.T) {
|
||||
bmp388, adaptor := initTestBMP388DriverWithStubbedAdaptor()
|
||||
adaptor.Testi2cConnectErr(true)
|
||||
gobottest.Assert(t, bmp388.Start(), errors.New("Invalid i2c connection"))
|
||||
}
|
||||
|
||||
func TestBMP388DriverStartWriteError(t *testing.T) {
|
||||
bmp388, adaptor := initTestBMP388DriverWithStubbedAdaptor()
|
||||
adaptor.i2cWriteImpl = func([]byte) (int, error) {
|
||||
return 0, errors.New("write error")
|
||||
}
|
||||
gobottest.Assert(t, bmp388.Start(), errors.New("write error"))
|
||||
}
|
||||
|
||||
func TestBMP388DriverStartReadError(t *testing.T) {
|
||||
bmp388, adaptor := initTestBMP388DriverWithStubbedAdaptor()
|
||||
adaptor.i2cReadImpl = func(b []byte) (int, error) {
|
||||
return 0, errors.New("read error")
|
||||
}
|
||||
gobottest.Assert(t, bmp388.Start(), errors.New("read error"))
|
||||
}
|
||||
|
||||
func TestBMP388DriverHalt(t *testing.T) {
|
||||
bmp388 := initTestBMP388Driver()
|
||||
|
||||
gobottest.Assert(t, bmp388.Halt(), nil)
|
||||
}
|
||||
|
||||
func TestBMP388DriverMeasurements(t *testing.T) {
|
||||
bmp388, adaptor := initTestBMP388DriverWithStubbedAdaptor()
|
||||
adaptor.i2cReadImpl = func(b []byte) (int, error) {
|
||||
func TestBMP388Measurements(t *testing.T) {
|
||||
d, a := initTestBMP388WithStubbedAdaptor()
|
||||
a.i2cReadImpl = func(b []byte) (int, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
lastWritten := adaptor.written[len(adaptor.written)-1]
|
||||
lastWritten := a.written[len(a.written)-1]
|
||||
switch lastWritten {
|
||||
case bmp388RegisterChipID:
|
||||
case bmp388RegChipID:
|
||||
// Simulate returning of 0x50 for the
|
||||
// ReadByteData(bmp388RegisterChipID) call in initialisation()
|
||||
// ReadByteData(bmp388RegChipID) call in initialisation()
|
||||
binary.Write(buf, binary.LittleEndian, uint8(0x50))
|
||||
case bmp388RegisterCalib00:
|
||||
case bmp388RegCalib00:
|
||||
// Values produced by dumping data from actual sensor
|
||||
buf.Write([]byte{36, 107, 156, 73, 246, 104, 255, 189, 245, 35, 0, 151, 101, 184, 122, 243, 246, 211, 64, 14, 196, 0, 0, 0})
|
||||
case bmp388RegisterTempData:
|
||||
case bmp388RegTempData:
|
||||
buf.Write([]byte{0, 28, 127})
|
||||
case bmp388RegisterPressureData:
|
||||
case bmp388RegPressureData:
|
||||
buf.Write([]byte{0, 66, 113})
|
||||
}
|
||||
|
||||
copy(b, buf.Bytes())
|
||||
return buf.Len(), nil
|
||||
}
|
||||
bmp388.Start()
|
||||
temp, err := bmp388.Temperature(2)
|
||||
d.Start()
|
||||
temp, err := d.Temperature(2)
|
||||
gobottest.Assert(t, err, nil)
|
||||
gobottest.Assert(t, temp, float32(22.906143))
|
||||
pressure, err := bmp388.Pressure(2)
|
||||
pressure, err := d.Pressure(2)
|
||||
gobottest.Assert(t, err, nil)
|
||||
gobottest.Assert(t, pressure, float32(98874.85))
|
||||
alt, err := bmp388.Altitude(2)
|
||||
alt, err := d.Altitude(2)
|
||||
gobottest.Assert(t, err, nil)
|
||||
gobottest.Assert(t, alt, float32(205.89395))
|
||||
}
|
||||
|
||||
func TestBMP388DriverTemperatureWriteError(t *testing.T) {
|
||||
bmp388, adaptor := initTestBMP388DriverWithStubbedAdaptor()
|
||||
bmp388.Start()
|
||||
func TestBMP388TemperatureWriteError(t *testing.T) {
|
||||
d, a := initTestBMP388WithStubbedAdaptor()
|
||||
d.Start()
|
||||
|
||||
adaptor.i2cWriteImpl = func([]byte) (int, error) {
|
||||
a.i2cWriteImpl = func([]byte) (int, error) {
|
||||
return 0, errors.New("write error")
|
||||
}
|
||||
temp, err := bmp388.Temperature(2)
|
||||
temp, err := d.Temperature(2)
|
||||
gobottest.Assert(t, err, errors.New("write error"))
|
||||
gobottest.Assert(t, temp, float32(0.0))
|
||||
}
|
||||
|
||||
func TestBMP388DriverTemperatureReadError(t *testing.T) {
|
||||
bmp388, adaptor := initTestBMP388DriverWithStubbedAdaptor()
|
||||
bmp388.Start()
|
||||
func TestBMP388TemperatureReadError(t *testing.T) {
|
||||
d, a := initTestBMP388WithStubbedAdaptor()
|
||||
d.Start()
|
||||
|
||||
adaptor.i2cReadImpl = func([]byte) (int, error) {
|
||||
a.i2cReadImpl = func([]byte) (int, error) {
|
||||
return 0, errors.New("read error")
|
||||
}
|
||||
temp, err := bmp388.Temperature(2)
|
||||
temp, err := d.Temperature(2)
|
||||
gobottest.Assert(t, err, errors.New("read error"))
|
||||
gobottest.Assert(t, temp, float32(0.0))
|
||||
}
|
||||
|
||||
func TestBMP388DriverPressureWriteError(t *testing.T) {
|
||||
bmp388, adaptor := initTestBMP388DriverWithStubbedAdaptor()
|
||||
bmp388.Start()
|
||||
func TestBMP388PressureWriteError(t *testing.T) {
|
||||
d, a := initTestBMP388WithStubbedAdaptor()
|
||||
d.Start()
|
||||
|
||||
adaptor.i2cWriteImpl = func([]byte) (int, error) {
|
||||
a.i2cWriteImpl = func([]byte) (int, error) {
|
||||
return 0, errors.New("write error")
|
||||
}
|
||||
press, err := bmp388.Pressure(2)
|
||||
press, err := d.Pressure(2)
|
||||
gobottest.Assert(t, err, errors.New("write error"))
|
||||
gobottest.Assert(t, press, float32(0.0))
|
||||
}
|
||||
|
||||
func TestBMP388DriverPressureReadError(t *testing.T) {
|
||||
bmp388, adaptor := initTestBMP388DriverWithStubbedAdaptor()
|
||||
bmp388.Start()
|
||||
func TestBMP388PressureReadError(t *testing.T) {
|
||||
d, a := initTestBMP388WithStubbedAdaptor()
|
||||
d.Start()
|
||||
|
||||
adaptor.i2cReadImpl = func([]byte) (int, error) {
|
||||
a.i2cReadImpl = func([]byte) (int, error) {
|
||||
return 0, errors.New("read error")
|
||||
}
|
||||
press, err := bmp388.Pressure(2)
|
||||
press, err := d.Pressure(2)
|
||||
gobottest.Assert(t, err, errors.New("read error"))
|
||||
gobottest.Assert(t, press, float32(0.0))
|
||||
}
|
||||
|
||||
func TestBMP388DriverSetName(t *testing.T) {
|
||||
b := initTestBMP388Driver()
|
||||
b.SetName("TESTME")
|
||||
gobottest.Assert(t, b.Name(), "TESTME")
|
||||
}
|
||||
|
||||
func TestBMP388DriverOptions(t *testing.T) {
|
||||
b := NewBMP388Driver(newI2cTestAdaptor(), WithBus(2))
|
||||
gobottest.Assert(t, b.GetBusOrDefault(1), 2)
|
||||
func TestBMP388_initialization(t *testing.T) {
|
||||
// sequence to read and write in initialization():
|
||||
// * read chip ID register (0x00) and compare
|
||||
// * read 24 bytes (12 x 16 bit calibration data), starting from TC1 register (0x31)
|
||||
// * fill calibration struct with data (LSByte read first)
|
||||
// * perform a soft reset by command register (0x7E)
|
||||
// * prepare the content of config register
|
||||
// * write the config register (0x1F)
|
||||
// arrange
|
||||
d, a := initTestBMP388WithStubbedAdaptor()
|
||||
a.written = []byte{} // reset writes of former test
|
||||
const (
|
||||
wantChipIDReg = uint8(0x00)
|
||||
wantCalibReg = uint8(0x31)
|
||||
wantCommandReg = uint8(0x7E)
|
||||
wantCommandRegVal = uint8(0xB6) // soft reset
|
||||
wantConfReg = uint8(0x1F)
|
||||
wantConfRegVal = uint8(0x00) // no filter
|
||||
)
|
||||
// Values produced by dumping data from actual sensor
|
||||
returnRead := []byte{36, 107, 156, 73, 246, 104, 255, 189, 245, 35, 0, 151, 101, 184, 122, 243, 246, 211, 64, 14, 196, 0, 0, 0}
|
||||
numCallsRead := 0
|
||||
a.i2cReadImpl = func(b []byte) (int, error) {
|
||||
numCallsRead++
|
||||
if numCallsRead == 1 {
|
||||
b[0] = 0x50
|
||||
} else {
|
||||
copy(b, returnRead)
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
// act, assert - initialization() must be called on Start()
|
||||
err := d.Start()
|
||||
// assert
|
||||
gobottest.Assert(t, err, nil)
|
||||
gobottest.Assert(t, numCallsRead, 2)
|
||||
gobottest.Assert(t, len(a.written), 6)
|
||||
gobottest.Assert(t, a.written[0], wantChipIDReg)
|
||||
gobottest.Assert(t, a.written[1], wantCalibReg)
|
||||
gobottest.Assert(t, a.written[2], wantCommandReg)
|
||||
gobottest.Assert(t, a.written[3], wantCommandRegVal)
|
||||
gobottest.Assert(t, a.written[4], wantConfReg)
|
||||
gobottest.Assert(t, a.written[5], wantConfRegVal)
|
||||
gobottest.Assert(t, d.calCoeffs.t1, float32(7.021568e+06))
|
||||
gobottest.Assert(t, d.calCoeffs.t2, float32(1.7549843e-05))
|
||||
gobottest.Assert(t, d.calCoeffs.t3, float32(-3.5527137e-14))
|
||||
gobottest.Assert(t, d.calCoeffs.p1, float32(-0.015769958))
|
||||
gobottest.Assert(t, d.calCoeffs.p2, float32(-3.5410747e-05))
|
||||
gobottest.Assert(t, d.calCoeffs.p3, float32(8.1490725e-09))
|
||||
gobottest.Assert(t, d.calCoeffs.p4, float32(0))
|
||||
gobottest.Assert(t, d.calCoeffs.p5, float32(208056))
|
||||
gobottest.Assert(t, d.calCoeffs.p6, float32(490.875))
|
||||
gobottest.Assert(t, d.calCoeffs.p7, float32(-0.05078125))
|
||||
gobottest.Assert(t, d.calCoeffs.p8, float32(-0.00030517578))
|
||||
gobottest.Assert(t, d.calCoeffs.p9, float32(5.8957283e-11))
|
||||
}
|
||||
|
67
examples/tinkerboard_bme280.go
Normal file
67
examples/tinkerboard_bme280.go
Normal file
@ -0,0 +1,67 @@
|
||||
// +build example
|
||||
//
|
||||
// Do not build by default.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gobot.io/x/gobot"
|
||||
"gobot.io/x/gobot/drivers/i2c"
|
||||
"gobot.io/x/gobot/platforms/tinkerboard"
|
||||
)
|
||||
|
||||
// Wiring
|
||||
// PWR Tinkerboard: 1 (+3.3V, VCC), 2(+5V), 6, 9, 14, 20 (GND)
|
||||
// I2C1 Tinkerboard: 3 (SDA-ws), 5 (SCL-gn)
|
||||
// BME280 plate: VIN (2.5..5V), GND, SDL, SDA, SDO to GND
|
||||
func main() {
|
||||
a := tinkerboard.NewAdaptor()
|
||||
bme280 := i2c.NewBME280Driver(a, i2c.WithAddress(0x76),
|
||||
i2c.WithBME280PressureOversampling(0x05),
|
||||
i2c.WithBME280TemperatureOversampling(0x02),
|
||||
i2c.WithBME280HumidityOversampling(0x01),
|
||||
i2c.WithBME280IIRFilter(0x05))
|
||||
|
||||
work := func() {
|
||||
gobot.Every(2*time.Second, func() {
|
||||
t, e := bme280.Temperature()
|
||||
fmt.Println("Temperature [°C]", t)
|
||||
if e != nil {
|
||||
fmt.Println(e)
|
||||
}
|
||||
|
||||
p, e := bme280.Pressure()
|
||||
fmt.Println("Pressure [Pa]", p) // 100hPa = 1Pa
|
||||
if e != nil {
|
||||
fmt.Println(e)
|
||||
}
|
||||
|
||||
a, e := bme280.Altitude()
|
||||
fmt.Println("Altitude [m]", a)
|
||||
if e != nil {
|
||||
fmt.Println(e)
|
||||
}
|
||||
|
||||
h, e := bme280.Humidity()
|
||||
fmt.Println("Humidity [%]", h)
|
||||
if e != nil {
|
||||
fmt.Println(e)
|
||||
}
|
||||
fmt.Println("-------------")
|
||||
})
|
||||
}
|
||||
|
||||
robot := gobot.NewRobot("bme280bot",
|
||||
[]gobot.Connection{a},
|
||||
[]gobot.Device{bme280},
|
||||
work,
|
||||
)
|
||||
|
||||
err := robot.Start()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user