1
0
mirror of https://github.com/hybridgroup/gobot.git synced 2025-04-24 13:48:49 +08:00

MPL115A2 use ReadBlockData()

This commit is contained in:
Thomas Kohler 2022-10-10 07:54:50 +02:00 committed by GitHub
commit e9980df214
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 228 additions and 178 deletions

View File

@ -284,7 +284,7 @@ drivers provided using the `gobot/drivers/i2c` package:
- LIDAR-Lite
- MCP23017 Port Expander
- MMA7660 3-Axis Accelerometer
- MPL115A2 Barometer
- MPL115A2 Barometric Pressure/Temperature
- MPU6050 Accelerometer/Gyroscope
- PCA9501 8-bit I/O port with interrupt, 2-kbit EEPROM
- PCA9685 16-channel 12-bit PWM/Servo Driver

View File

@ -36,7 +36,7 @@ Gobot has a extensible system for connecting to hardware devices. The following
- LIDAR-Lite
- MCP23017 Port Expander
- MMA7660 3-Axis Accelerometer
- MPL115A2 Barometer
- MPL115A2 Barometric Pressure/Temperature
- MPU6050 Accelerometer/Gyroscope
- PCA9501 8-bit I/O port with interrupt, 2-kbit EEPROM
- PCA9685 16-channel 12-bit PWM/Servo Driver

View File

@ -8,121 +8,98 @@ import (
"time"
)
const mpl115a2Address = 0x60
const mpl115a2DefaultAddress = 0x60
const MPL115A2_REGISTER_PRESSURE_MSB = 0x00
const MPL115A2_REGISTER_PRESSURE_LSB = 0x01
const MPL115A2_REGISTER_TEMP_MSB = 0x02
const MPL115A2_REGISTER_TEMP_LSB = 0x03
const MPL115A2_REGISTER_A0_COEFF_MSB = 0x04
const MPL115A2_REGISTER_A0_COEFF_LSB = 0x05
const MPL115A2_REGISTER_B1_COEFF_MSB = 0x06
const MPL115A2_REGISTER_B1_COEFF_LSB = 0x07
const MPL115A2_REGISTER_B2_COEFF_MSB = 0x08
const MPL115A2_REGISTER_B2_COEFF_LSB = 0x09
const MPL115A2_REGISTER_C12_COEFF_MSB = 0x0A
const MPL115A2_REGISTER_C12_COEFF_LSB = 0x0B
const MPL115A2_REGISTER_STARTCONVERSION = 0x12
const (
mpl115A2Reg_PressureMSB = 0x00 // first ADC register
mpl115A2Reg_PressureLSB = 0x01
mpl115A2Reg_TempMSB = 0x02
mpl115A2Reg_TempLSB = 0x03
// MPL115A2Driver is a Gobot Driver for the MPL115A2 I2C digitial pressure/temperature sensor.
mpl115A2Reg_A0_MSB = 0x04 // first coefficient register
mpl115A2Reg_A0_LSB = 0x05
mpl115A2Reg_B1_MSB = 0x06
mpl115A2Reg_B1_LSB = 0x07
mpl115A2Reg_B2_MSB = 0x08
mpl115A2Reg_B2_LSB = 0x09
mpl115A2Reg_C12_MSB = 0x0A
mpl115A2Reg_C12_LSB = 0x0B
mpl115A2Reg_StartConversion = 0x12
)
// MPL115A2Driver is a Gobot Driver for the MPL115A2 I2C digital pressure/temperature sensor.
// datasheet:
// https://www.nxp.com/docs/en/data-sheet/MPL115A2.pdf
//
// reference implementations:
// * https://github.com/adafruit/Adafruit_MPL115A2
type MPL115A2Driver struct {
name string
connector Connector
connection Connection
Config
*Driver
gobot.Eventer
A0 float32
B1 float32
B2 float32
C12 float32
a0 float32
b1 float32
b2 float32
c12 float32
}
// NewMPL115A2Driver creates a new Gobot Driver for an MPL115A2
// I2C Pressure/Temperature sensor.
//
// 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 NewMPL115A2Driver(a Connector, options ...func(Config)) *MPL115A2Driver {
m := &MPL115A2Driver{
name: gobot.DefaultName("MPL115A2"),
connector: a,
Config: NewConfig(),
Eventer: gobot.NewEventer(),
func NewMPL115A2Driver(c Connector, options ...func(Config)) *MPL115A2Driver {
d := &MPL115A2Driver{
Driver: NewDriver(c, "MPL115A2", mpl115a2DefaultAddress),
Eventer: gobot.NewEventer(),
}
d.afterStart = d.initialization
for _, option := range options {
option(m)
option(d)
}
// TODO: add commands to API
m.AddEvent(Error)
d.AddEvent(Error)
return m
return d
}
// Name returns the name of the device.
func (h *MPL115A2Driver) Name() string { return h.name }
// Pressure fetches the latest data from the MPL115A2, and returns the pressure in kPa
func (d *MPL115A2Driver) Pressure() (p float32, err error) {
d.mutex.Lock()
defer d.mutex.Unlock()
// SetName sets the name of the device.
func (h *MPL115A2Driver) SetName(n string) { h.name = n }
p, _, err = d.getData()
return
}
// Connection returns the Connection of the device.
func (h *MPL115A2Driver) Connection() gobot.Connection { return h.connector.(gobot.Connection) }
// Temperature fetches the latest data from the MPL115A2, and returns the temperature in °C
func (d *MPL115A2Driver) Temperature() (t float32, err error) {
d.mutex.Lock()
defer d.mutex.Unlock()
// Start writes initialization bytes and reads from adaptor
// using specified interval to accelerometer andtemperature data
func (h *MPL115A2Driver) Start() (err error) {
if err := h.initialization(); err != nil {
return err
_, t, err = d.getData()
return
}
func (d *MPL115A2Driver) initialization() (err error) {
data := make([]byte, 8)
if err = d.connection.ReadBlockData(mpl115A2Reg_A0_MSB, data); err != nil {
return
}
return
}
// Halt returns true if devices is halted successfully
func (h *MPL115A2Driver) Halt() (err error) { return }
// Pressure fetches the latest data from the MPL115A2, and returns the pressure
func (h *MPL115A2Driver) Pressure() (p float32, err error) {
p, _, err = h.getData()
return
}
// Temperature fetches the latest data from the MPL115A2, and returns the temperature
func (h *MPL115A2Driver) Temperature() (t float32, err error) {
_, t, err = h.getData()
return
}
func (h *MPL115A2Driver) initialization() (err error) {
var coA0 int16
var coB1 int16
var coB2 int16
var coC12 int16
bus := h.GetBusOrDefault(h.connector.GetDefaultBus())
address := h.GetAddressOrDefault(mpl115a2Address)
h.connection, err = h.connector.GetConnection(address, bus)
if err != nil {
return err
}
if _, err = h.connection.Write([]byte{MPL115A2_REGISTER_A0_COEFF_MSB}); err != nil {
return
}
data := make([]byte, 8)
if _, err = h.connection.Read(data); err != nil {
return
}
buf := bytes.NewBuffer(data)
binary.Read(buf, binary.BigEndian, &coA0)
binary.Read(buf, binary.BigEndian, &coB1)
binary.Read(buf, binary.BigEndian, &coB2)
@ -130,48 +107,40 @@ func (h *MPL115A2Driver) initialization() (err error) {
coC12 = coC12 >> 2
h.A0 = float32(coA0) / 8.0
h.B1 = float32(coB1) / 8192.0
h.B2 = float32(coB2) / 16384.0
h.C12 = float32(coC12) / 4194304.0
d.a0 = float32(coA0) / 8.0
d.b1 = float32(coB1) / 8192.0
d.b2 = float32(coB2) / 16384.0
d.c12 = float32(coC12) / 4194304.0
return
}
// getData fetches the latest data from the MPL115A2
func (h *MPL115A2Driver) getData() (p, t float32, err error) {
func (d *MPL115A2Driver) getData() (p, t float32, err error) {
var temperature uint16
var pressure uint16
var pressureComp float32
if _, err = h.connection.Write([]byte{MPL115A2_REGISTER_STARTCONVERSION, 0}); err != nil {
if err = d.connection.WriteByteData(mpl115A2Reg_StartConversion, 0); err != nil {
return
}
time.Sleep(5 * time.Millisecond)
if _, err = h.connection.Write([]byte{MPL115A2_REGISTER_PRESSURE_MSB}); err != nil {
return
}
data := []byte{0, 0, 0, 0}
bytesRead, err1 := h.connection.Read(data)
if err1 != nil {
err = err1
if err = d.connection.ReadBlockData(mpl115A2Reg_PressureMSB, data); err != nil {
return
}
if bytesRead == 4 {
buf := bytes.NewBuffer(data)
binary.Read(buf, binary.BigEndian, &pressure)
binary.Read(buf, binary.BigEndian, &temperature)
buf := bytes.NewBuffer(data)
binary.Read(buf, binary.BigEndian, &pressure)
binary.Read(buf, binary.BigEndian, &temperature)
temperature = temperature >> 6
pressure = pressure >> 6
temperature = temperature >> 6
pressure = pressure >> 6
pressureComp = float32(h.A0) + (float32(h.B1)+float32(h.C12)*float32(temperature))*float32(pressure) + float32(h.B2)*float32(temperature)
p = (65.0/1023.0)*pressureComp + 50.0
t = ((float32(temperature) - 498.0) / -5.35) + 25.0
}
pressureComp = d.a0 + (d.b1+d.c12*float32(temperature))*float32(pressure) + d.b2*float32(temperature)
p = (65.0/1023.0)*pressureComp + 50.0
t = ((float32(temperature) - 498.0) / -5.35) + 25.0
return
}

View File

@ -9,98 +9,127 @@ 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 = (*MPL115A2Driver)(nil)
// --------- HELPERS
func initTestMPL115A2Driver() (driver *MPL115A2Driver) {
driver, _ = initTestMPL115A2DriverWithStubbedAdaptor()
return
}
func initTestMPL115A2DriverWithStubbedAdaptor() (*MPL115A2Driver, *i2cTestAdaptor) {
adaptor := newI2cTestAdaptor()
return NewMPL115A2Driver(adaptor), adaptor
a := newI2cTestAdaptor()
return NewMPL115A2Driver(a), a
}
// --------- TESTS
func TestNewMPL115A2Driver(t *testing.T) {
// Does it return a pointer to an instance of MPL115A2Driver?
var mpl interface{} = NewMPL115A2Driver(newI2cTestAdaptor())
_, ok := mpl.(*MPL115A2Driver)
var di interface{} = NewMPL115A2Driver(newI2cTestAdaptor())
d, ok := di.(*MPL115A2Driver)
if !ok {
t.Errorf("NewMPL115A2Driver() should have returned a *MPL115A2Driver")
}
gobottest.Refute(t, d.Connection(), nil)
gobottest.Assert(t, strings.HasPrefix(d.Name(), "MPL115A2"), true)
gobottest.Assert(t, d.defaultAddress, 0x60)
}
// Methods
func TestMPL115A2Driver(t *testing.T) {
mpl := initTestMPL115A2Driver()
gobottest.Refute(t, mpl.Connection(), nil)
gobottest.Assert(t, strings.HasPrefix(mpl.Name(), "MPL115A2"), true)
func TestMPL115A2Options(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 := NewMPL115A2Driver(newI2cTestAdaptor(), WithBus(2))
gobottest.Assert(t, d.GetBusOrDefault(1), 2)
}
func TestMPL115A2DriverOptions(t *testing.T) {
mpl := NewMPL115A2Driver(newI2cTestAdaptor(), WithBus(2))
gobottest.Assert(t, mpl.GetBusOrDefault(1), 2)
func TestMPL115A2ReadData(t *testing.T) {
// sequence for read data
// * retrieve the coefficients for temperature compensation of pressure - see test for Start()
// * write start conversion register address (0x12)
// * write start value - 0x00
// * wait at least 3 ms according to data sheet (tc - conversion time)
// * write pressure MSB register address (0x00)
// * read pressure (16 bit, order MSB-LSB)
// * read temperature (16 bit, order MSB-LSB)
// * calculate temperature compensated pressure in kPa according to data sheet
// * shift the temperature value right for 6 bits (resolution is 10 bit)
// * shift the pressure value right for 6 bits (resolution is 10 bit)
// * calculate temperature in °C according to this implementation:
// https://github.com/adafruit/Adafruit_MPL115A2/blob/master/Adafruit_MPL115A2.cpp
//
// arrange
d, a := initTestMPL115A2DriverWithStubbedAdaptor()
d.Start()
a.written = []byte{}
// arrange coefficients according the example from data sheet
d.a0 = 2009.75
d.b1 = -2.37585
d.b2 = -0.92047
d.c12 = 0.00079
readReturnP := []byte{0x66, 0x80, 0x7E, 0xC0} // use example from data sheet
readReturnT := []byte{0x00, 0x00, 0x7E, 0xC0} // use example from data sheet
readCallCounter := 0
a.i2cReadImpl = func(b []byte) (int, error) {
readCallCounter++
if readCallCounter == 1 {
copy(b, readReturnP)
}
if readCallCounter == 2 {
copy(b, readReturnT)
}
return len(b), nil
}
// act
press, errP := d.Pressure()
temp, errT := d.Temperature()
// assert
gobottest.Assert(t, errP, nil)
gobottest.Assert(t, errT, nil)
gobottest.Assert(t, readCallCounter, 2)
gobottest.Assert(t, len(a.written), 6)
gobottest.Assert(t, a.written[0], uint8(0x12))
gobottest.Assert(t, a.written[1], uint8(0x00))
gobottest.Assert(t, a.written[2], uint8(0x00))
gobottest.Assert(t, a.written[3], uint8(0x12))
gobottest.Assert(t, a.written[4], uint8(0x00))
gobottest.Assert(t, a.written[5], uint8(0x00))
gobottest.Assert(t, press, float32(96.585915))
gobottest.Assert(t, temp, float32(23.317757))
}
func TestMPL115A2DriverSetName(t *testing.T) {
mpl := initTestMPL115A2Driver()
mpl.SetName("TESTME")
gobottest.Assert(t, mpl.Name(), "TESTME")
}
func TestMPL115A2ReadDataError(t *testing.T) {
d, a := initTestMPL115A2DriverWithStubbedAdaptor()
d.Start()
func TestMPL115A2DriverStart(t *testing.T) {
mpl, _ := initTestMPL115A2DriverWithStubbedAdaptor()
gobottest.Assert(t, mpl.Start(), nil)
}
func TestMPL115A2StartConnectError(t *testing.T) {
d, adaptor := initTestMPL115A2DriverWithStubbedAdaptor()
adaptor.Testi2cConnectErr(true)
gobottest.Assert(t, d.Start(), errors.New("Invalid i2c connection"))
}
func TestMPL115A2DriverStartWriteError(t *testing.T) {
mpl, adaptor := initTestMPL115A2DriverWithStubbedAdaptor()
adaptor.i2cWriteImpl = func([]byte) (int, error) {
a.i2cWriteImpl = func([]byte) (int, error) {
return 0, errors.New("write error")
}
gobottest.Assert(t, mpl.Start(), errors.New("write error"))
}
func TestMPL115A2DriverReadData(t *testing.T) {
mpl, adaptor := initTestMPL115A2DriverWithStubbedAdaptor()
adaptor.i2cReadImpl = func(b []byte) (int, error) {
copy(b, []byte{0x00, 0x01, 0x02, 0x04})
return 4, nil
}
mpl.Start()
press, _ := mpl.Pressure()
temp, _ := mpl.Temperature()
gobottest.Assert(t, press, float32(50.007942))
gobottest.Assert(t, temp, float32(116.58878))
}
func TestMPL115A2DriverReadDataError(t *testing.T) {
mpl, adaptor := initTestMPL115A2DriverWithStubbedAdaptor()
mpl.Start()
adaptor.i2cWriteImpl = func([]byte) (int, error) {
return 0, errors.New("write error")
}
_, err := mpl.Pressure()
_, err := d.Pressure()
gobottest.Assert(t, err, errors.New("write error"))
}
func TestMPL115A2DriverHalt(t *testing.T) {
mpl := initTestMPL115A2Driver()
gobottest.Assert(t, mpl.Halt(), nil)
func TestMPL115A2_initialization(t *testing.T) {
// sequence for initialization the device on Start(), which calculates
// the coefficients for temperature compensation of pressure
// * write coefficient A0 MSB register address (0x04)
// * read all 4 coefficients (16 bit, order MSB-LSB)
// * write signal path reset register address (0x68)
// * calculate A0, B1, B2, C12 according to data sheet
//
// arrange
d, a := initTestMPL115A2DriverWithStubbedAdaptor()
readCallCounter := 0
readReturn := []byte{0x3E, 0xCE, 0xB3, 0xF9, 0xC5, 0x17, 0x33, 0xC8} // use example from data sheet
a.i2cReadImpl = func(b []byte) (int, error) {
readCallCounter++
copy(b, readReturn)
return len(b), nil
}
// act, assert - initialization() must be called on Start()
err := d.Start()
// assert
gobottest.Assert(t, err, nil)
gobottest.Assert(t, readCallCounter, 1)
gobottest.Assert(t, len(a.written), 1)
gobottest.Assert(t, a.written[0], uint8(0x04))
gobottest.Assert(t, d.a0, float32(2009.75))
gobottest.Assert(t, d.b1, float32(-2.3758545))
gobottest.Assert(t, d.b2, float32(-0.9204712))
gobottest.Assert(t, d.c12, float32(0.0007901192))
}

View File

@ -27,7 +27,7 @@ func main() {
})
}
robot := gobot.NewRobot("blinkBot",
robot := gobot.NewRobot("mpl115Bot",
[]gobot.Connection{board},
[]gobot.Device{mpl115a2},
work,

View File

@ -0,0 +1,52 @@
// +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)
// MPL115A2 plate: VDD (2.375..5.5V), GND, SDL, SDA
func main() {
board := tinkerboard.NewAdaptor()
mpl115a2 := i2c.NewMPL115A2Driver(board)
work := func() {
gobot.Every(2*time.Second, func() {
if press, err := mpl115a2.Pressure(); err != nil {
fmt.Println(err)
} else {
fmt.Println("Pressure [kPa]", press)
}
if temp, err := mpl115a2.Temperature(); err != nil {
fmt.Println(err)
} else {
fmt.Println("Temperature [°C]", temp)
}
fmt.Println("-------------")
})
}
robot := gobot.NewRobot("mpl115Bot",
[]gobot.Connection{board},
[]gobot.Device{mpl115a2},
work,
)
err := robot.Start()
if err != nil {
fmt.Println(err)
}
}