diff --git a/devices/bme280/bme280.go b/devices/bme280/bme280.go index f638960..29361c7 100644 --- a/devices/bme280/bme280.go +++ b/devices/bme280/bme280.go @@ -36,7 +36,7 @@ type Oversampling uint8 // Possible oversampling values. // // The higher the more time and power it takes to take a measurement. Even at -// 16x for all 3 sensor, it is less than 100ms albeit increased power +// 16x for all 3 sensors, it is less than 100ms albeit increased power // consumption may increase the temperature reading. const ( Off Oversampling = 0 @@ -103,6 +103,7 @@ type Dev struct { mu sync.Mutex stop chan struct{} + wg sync.WaitGroup } func (d *Dev) String() string { @@ -116,19 +117,19 @@ func (d *Dev) Sense(env *devices.Environment) error { d.mu.Lock() defer d.mu.Unlock() if d.stop != nil { - return errors.New("bme280: already sensing continuously") + return wrap(errors.New("already sensing continuously")) } err := d.writeCommands([]byte{ // ctrl_meas 0xF4, byte(d.opts.Temperature)<<5 | byte(d.opts.Pressure)<<2 | byte(forced), }) if err != nil { - return err + return wrap(err) } time.Sleep(d.measDelay) for idle := false; !idle; { if idle, err = d.isIdle(); err != nil { - return err + return wrap(err) } } return d.sense(env) @@ -149,6 +150,7 @@ func (d *Dev) SenseContinuous(interval time.Duration) (<-chan devices.Environmen // Don't send the stop command to the device. close(d.stop) d.stop = nil + d.wg.Wait() } s := chooseStandby(interval - d.measDelay) err := d.writeCommands([]byte{ @@ -158,18 +160,21 @@ func (d *Dev) SenseContinuous(interval time.Duration) (<-chan devices.Environmen 0xF4, byte(d.opts.Temperature)<<5 | byte(d.opts.Pressure)<<2 | byte(normal), }) if err != nil { - return nil, err + return nil, wrap(err) } sensing := make(chan devices.Environment) d.stop = make(chan struct{}) + d.wg.Add(1) go func() { + defer d.wg.Done() defer close(sensing) d.sensingContinuous(interval, sensing, d.stop) }() return sensing, nil } -// Halt stops the bme280 from acquiring measurements as initiated by Sense(). +// Halt stops the bme280 from acquiring measurements as initiated by +// SenseContinuous(). // // It is recommended to call this function before terminating the process to // reduce idle power usage. @@ -181,6 +186,7 @@ func (d *Dev) Halt() error { } close(d.stop) d.stop = nil + d.wg.Wait() // Page 27 (for register) and 12~13 section 3.3. return d.writeCommands([]byte{ // config @@ -254,7 +260,7 @@ func NewI2C(b i2c.Bus, opts *Opts) (*Dev, error) { case 0x00: // do not do anything default: - return nil, errors.New("bme280: given address not supported by device") + return nil, wrap(errors.New("given address not supported by device")) } } d := &Dev{d: &i2c.Dev{Bus: b, Addr: addr}, isSPI: false} @@ -273,12 +279,12 @@ func NewI2C(b i2c.Bus, opts *Opts) (*Dev, error) { // When using SPI, the CS line must be used. func NewSPI(p spi.Port, opts *Opts) (*Dev, error) { if opts != nil && opts.Address != 0 { - return nil, errors.New("bme280: do not use Address in SPI") + return nil, wrap(errors.New("do not use Address in SPI")) } // It works both in Mode0 and Mode3. c, err := p.Connect(10000000, spi.Mode3, 8) if err != nil { - return nil, err + return nil, wrap(err) } d := &Dev{d: c, isSPI: true} if err := d.makeDev(opts); err != nil { @@ -294,7 +300,7 @@ func (d *Dev) makeDev(opts *Opts) error { opts = &defaults } if opts.Temperature == Off { - return errors.New("temperature measurement is required, use at least O1x") + return wrap(errors.New("temperature measurement is required, use at least O1x")) } d.opts = *opts d.measDelay = d.opts.delayTypical() @@ -308,7 +314,7 @@ func (d *Dev) makeDev(opts *Opts) error { return err } if chipID[0] != 0x60 { - return fmt.Errorf("bme280: unexpected chip id %x; is this a BME280?", chipID[0]) + return wrap(fmt.Errorf("unexpected chip id %x; is this a BME280?", chipID[0])) } // TODO(maruel): We may want to wait for isIdle(). @@ -387,8 +393,11 @@ func (d *Dev) sensingContinuous(interval time.Duration, sensing chan<- devices.E log.Printf("bme280: failed to sense: %v", err) return } - sensing <- e - + select { + case sensing <- e: + case <-stop: + return + } select { case <-stop: return @@ -416,12 +425,15 @@ func (d *Dev) readReg(reg uint8, b []byte) error { // Rest of the write buffer is ignored. write[0] = reg if err := d.d.Tx(write, read); err != nil { - return err + return wrap(err) } copy(b, read[1:]) return nil } - return d.d.Tx([]byte{reg}, b) + if err := d.d.Tx([]byte{reg}, b); err != nil { + return wrap(err) + } + return nil } // writeCommands writes a command to the bme280. @@ -434,7 +446,10 @@ func (d *Dev) writeCommands(b []byte) error { b[i] &^= 0x80 } } - return d.d.Tx(b, nil) + if err := d.d.Tx(b, nil); err != nil { + return wrap(err) + } + return nil } // @@ -620,6 +635,10 @@ func (c *calibration) compensateHumidityInt(raw, tFine int32) uint32 { return uint32(x >> 12) } +func wrap(err error) error { + return fmt.Errorf("bme280: %v", err) +} + var _ devices.Environmental = &Dev{} var _ devices.Device = &Dev{} var _ fmt.Stringer = &Dev{} diff --git a/devices/bme280/bme280_test.go b/devices/bme280/bme280_test.go index 180024f..93e981e 100644 --- a/devices/bme280/bme280_test.go +++ b/devices/bme280/bme280_test.go @@ -47,7 +47,7 @@ func TestSPISense_success(t *testing.T) { s := spitest.Playback{ Playback: conntest.Playback{ Ops: []conntest.IO{ - // Chipd ID detection. + // Chip ID detection. { W: []byte{0xD0, 0x00}, R: []byte{0x00, 0x60}, @@ -123,7 +123,7 @@ func TestNewSPI_fail_len(t *testing.T) { Playback: conntest.Playback{ Ops: []conntest.IO{ { - // Chipd ID detection. + // Chip ID detection. W: []byte{0xD0, 0x00}, R: []byte{0x00}, }, @@ -146,7 +146,7 @@ func TestNewSPI_fail_chipid(t *testing.T) { Playback: conntest.Playback{ Ops: []conntest.IO{ { - // Chipd ID detection. + // Chip ID detection. W: []byte{0xD0, 0x00}, R: []byte{0x00, 0xFF}, }, @@ -164,7 +164,7 @@ func TestNewSPI_fail_chipid(t *testing.T) { func TestNewI2C_fail_io(t *testing.T) { bus := i2ctest.Playback{ Ops: []i2ctest.IO{ - // Chipd ID detection. + // Chip ID detection. {Addr: 0x76, W: []byte{0xd0}}, }, DontPanic: true, @@ -182,7 +182,7 @@ func TestNewI2C_fail_io(t *testing.T) { func TestNewI2C_fail_chipid(t *testing.T) { bus := i2ctest.Playback{ Ops: []i2ctest.IO{ - // Chipd ID detection. + // Chip ID detection. {Addr: 0x76, W: []byte{0xd0}, R: []byte{0x60}}, }, DontPanic: true, @@ -198,7 +198,7 @@ func TestNewI2C_fail_chipid(t *testing.T) { func TestNewI2C_calib1(t *testing.T) { bus := i2ctest.Playback{ Ops: []i2ctest.IO{ - // Chipd ID detection. + // Chip ID detection. {Addr: 0x76, W: []byte{0xd0}, R: []byte{0x60}}, // Calibration data. { @@ -221,7 +221,7 @@ func TestNewI2C_calib1(t *testing.T) { func TestNewI2C_calib2(t *testing.T) { bus := i2ctest.Playback{ Ops: []i2ctest.IO{ - // Chipd ID detection. + // Chip ID detection. {Addr: 0x76, W: []byte{0xd0}, R: []byte{0x60}}, // Calibration data. { @@ -267,7 +267,7 @@ func TestI2COpts(t *testing.T) { func TestI2CSense_fail(t *testing.T) { bus := i2ctest.Playback{ Ops: []i2ctest.IO{ - // Chipd ID detection. + // Chip ID detection. {Addr: 0x76, W: []byte{0xd0}, R: []byte{0x60}}, // Calibration data. { @@ -302,7 +302,7 @@ func TestI2CSense_fail(t *testing.T) { func TestI2CSense_success(t *testing.T) { bus := i2ctest.Playback{ Ops: []i2ctest.IO{ - // Chipd ID detection. + // Chip ID detection. {Addr: 0x76, W: []byte{0xd0}, R: []byte{0x60}}, // Calibration data. { @@ -355,7 +355,7 @@ func TestI2CSense_success(t *testing.T) { func TestI2CSense_idle_fail(t *testing.T) { bus := i2ctest.Playback{ Ops: []i2ctest.IO{ - // Chipd ID detection. + // Chip ID detection. {Addr: 0x76, W: []byte{0xd0}, R: []byte{0x60}}, // Calibration data. { @@ -389,7 +389,7 @@ func TestI2CSense_idle_fail(t *testing.T) { func TestI2CSense_command_fail(t *testing.T) { bus := i2ctest.Playback{ Ops: []i2ctest.IO{ - // Chipd ID detection. + // Chip ID detection. {Addr: 0x76, W: []byte{0xd0}, R: []byte{0x60}}, // Calibration data. { @@ -425,7 +425,7 @@ func TestI2CSense_command_fail(t *testing.T) { func TestI2CSenseContinuous_success(t *testing.T) { bus := i2ctest.Playback{ Ops: []i2ctest.IO{ - // Chipd ID detection. + // Chip ID detection. {Addr: 0x76, W: []byte{0xd0}, R: []byte{0x60}}, // Calibration data. { @@ -524,6 +524,9 @@ func TestI2CSenseContinuous_success(t *testing.T) { if err := dev.Halt(); err != nil { t.Fatal(err) } + if _, ok := <-c2; ok { + t.Fatal("c2 should be closed") + } if err := bus.Close(); err != nil { t.Fatal(err) } @@ -532,7 +535,7 @@ func TestI2CSenseContinuous_success(t *testing.T) { func TestI2CSenseContinuous_command_fail(t *testing.T) { bus := i2ctest.Playback{ Ops: []i2ctest.IO{ - // Chipd ID detection. + // Chip ID detection. {Addr: 0x76, W: []byte{0xd0}, R: []byte{0x60}}, // Calibration data. { @@ -564,7 +567,7 @@ func TestI2CSenseContinuous_sense_fail(t *testing.T) { } bus := i2ctest.Playback{ Ops: []i2ctest.IO{ - // Chipd ID detection. + // Chip ID detection. {Addr: 0x76, W: []byte{0xd0}, R: []byte{0x60}}, // Calibration data. { diff --git a/experimental/devices/bmp180/bmp180.go b/devices/bmp180/bmp180.go similarity index 58% rename from experimental/devices/bmp180/bmp180.go rename to devices/bmp180/bmp180.go index 9962698..78f052f 100644 --- a/experimental/devices/bmp180/bmp180.go +++ b/devices/bmp180/bmp180.go @@ -6,11 +6,9 @@ // // Datasheet // -// The official data sheet can be found here: -// // https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BMP180-DS000-121.pdf // -// The font the official datasheet on page 15 is unreadable, a copy with +// The font the official datasheet on page 15 is hard to read, a copy with // readable text can be found here: // // https://cdn-shop.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf @@ -27,6 +25,8 @@ import ( "encoding/binary" "errors" "fmt" + "log" + "sync" "time" "periph.io/x/periph/conn/i2c" @@ -37,50 +37,38 @@ import ( // Oversampling affects how much time is taken to measure pressure. type Oversampling uint8 +// Possible oversampling values. +// +// The higher the more time and power it takes to take a measurement. Even at +// 8x for pressure sensor, it is less than 30ms albeit at small increased power +// consumption, which may increase the temperature reading. const ( - // Possible oversampling values - No Oversampling = 0 + O1x Oversampling = 0 O2x Oversampling = 1 O4x Oversampling = 2 O8x Oversampling = 3 +) - // bit offsets in regCtrlMeas - ctrlMeasurementControlShift = 0 - ctrlStartConversionShift = 5 - ctrlOversamplingShift = 6 - - // commands - cmdStartTempConv uint8 = (1 << ctrlStartConversionShift) | (0x0E << ctrlMeasurementControlShift) - cmdStartPressureConv uint8 = (1 << ctrlStartConversionShift) | (0x14 << ctrlMeasurementControlShift) - - chipAddress = 0x77 // the address of a BMP180 is fixed at 0x77 - chipID = 0x55 // contents of the ID register, always 0x55 - - // registers - regChipID = 0xD0 // register contains the chip id - regCalibrationStart = 0xAA // first calibration register address - regSoftReset = 0xE0 // soft reset register - regCtrlMeas = 0xF4 // control measurement - regOut = 0xF6 // 3 bytes register with measurement data - - softResetValue = 0xB6 +const oversamplingName = "1x2x4x8x" - tempConvTime = 4500 * time.Microsecond // maximum conversion time for temperature -) +var oversamplingIndex = [...]uint8{0, 2, 4, 6, 8} -// maximum conversion time for pressure -var pressureConvTime = [...]time.Duration{ - 4500 * time.Microsecond, - 7500 * time.Microsecond, - 13500 * time.Microsecond, - 25500 * time.Microsecond, +func (i Oversampling) String() string { + if i >= Oversampling(len(oversamplingIndex)-1) { + return fmt.Sprintf("Oversampling(%d)", i) + } + return oversamplingName[oversamplingIndex[i]:oversamplingIndex[i+1]] } // Dev is a handle to a bmp180. type Dev struct { dev mmr.Dev8 - cal calibration os Oversampling + cal calibration + + mu sync.Mutex + stop chan struct{} + wg sync.WaitGroup } func (d *Dev) String() string { @@ -89,104 +77,142 @@ func (d *Dev) String() string { // Sense returns measurements as °C and kPa. func (d *Dev) Sense(env *devices.Environment) error { - // start conversion for temperature - if err := d.dev.WriteUint8(regCtrlMeas, cmdStartTempConv); err != nil { - return err - } - - time.Sleep(tempConvTime) - - // read value - ut, err := d.dev.ReadUint16(regOut) - if err != nil { - return err + d.mu.Lock() + defer d.mu.Unlock() + if d.stop != nil { + return wrap(errors.New("already sensing continuously")) } - - temp := d.cal.compensateTemp(ut) - - // start conversion for pressure - cmd := cmdStartPressureConv - cmd |= uint8(d.os) << ctrlOversamplingShift - - if err := d.dev.WriteUint8(regCtrlMeas, cmd); err != nil { - return err - } - - time.Sleep(pressureConvTime[d.os]) - - // read value - var pressureBuf [3]byte - if err := d.dev.ReadStruct(regOut, pressureBuf[:]); err != nil { - return err - } - - up := (int32(pressureBuf[0])<<16 + int32(pressureBuf[1])<<8 | int32(pressureBuf[2])) >> (8 - d.os) - - pressure := d.cal.compensatePressure(up, int32(ut), d.os) - - env.Temperature = devices.Celsius(temp * 100) - env.Pressure = devices.KPascal(pressure) - return nil + return d.sense(env) } // SenseContinuous implements devices.Environmental. func (d *Dev) SenseContinuous(interval time.Duration) (<-chan devices.Environment, error) { - // TODO(maruel): Manually poll in a loop via time.NewTicker. - return nil, errors.New("not implemented") -} + d.mu.Lock() + defer d.mu.Unlock() + if d.stop != nil { + close(d.stop) + d.stop = nil + d.wg.Wait() + } -// Halt is a noop for the BMP180. -func (d *Dev) Halt() error { - return nil + sensing := make(chan devices.Environment) + d.stop = make(chan struct{}) + d.wg.Add(1) + go func() { + defer d.wg.Done() + defer close(sensing) + d.sensingContinuous(interval, sensing, d.stop) + }() + return sensing, nil } -// Reset issues a soft reset to the device. -func (d *Dev) Reset() error { - // issue soft reset to initialize device - if err := d.dev.WriteUint8(regSoftReset, softResetValue); err != nil { - return err +// Halt stops continuous reading. +func (d *Dev) Halt() error { + d.mu.Lock() + defer d.mu.Unlock() + if d.stop != nil { + close(d.stop) + d.stop = nil + d.wg.Wait() } - - // wait for restart - time.Sleep(10 * time.Millisecond) - return nil } // New returns an object that communicates over I²C to BMP180 environmental -// sensor. The frequency for the bus can be up to 3.4MHz. +// sensor. +// +// The I²C bus frequency can be up to 3.4MHz. func New(b i2c.Bus, os Oversampling) (d *Dev, err error) { - bus := &i2c.Dev{Bus: b, Addr: chipAddress} - d = &Dev{ - os: os, - dev: mmr.Dev8{ - Conn: bus, - Order: binary.BigEndian, - }, - } + bus := &i2c.Dev{Bus: b, Addr: 0x77} + d = &Dev{dev: mmr.Dev8{Conn: bus, Order: binary.BigEndian}, os: os} - id, err := d.dev.ReadUint8(regChipID) + // Confirm the chip ID. + id, err := d.dev.ReadUint8(0xD0) if err != nil { - return nil, err + return nil, wrap(err) + } + if id != 0x55 { + return nil, wrap(fmt.Errorf("unexpected chip id 0x%x; is this a BMP180?", id)) } - if id != chipID { - return nil, fmt.Errorf("bmp180: unexpected chip id 0x%x; is this a BMP180?", id) + // Read calibration data. + if err := d.dev.ReadStruct(0xAA, &d.cal); err != nil { + return nil, wrap(err) + } + if !d.cal.isValid() { + return nil, wrap(errors.New("calibration data is invalid")) } + return d, nil +} - // read calibration data from internal EEPROM, 11 registers with two bytes each - if err := d.dev.ReadStruct(regCalibrationStart, &d.cal); err != nil { - return nil, err +// + +func (d *Dev) sense(env *devices.Environment) error { + // Request temperature convertion and read measurement. + if err := d.dev.WriteUint8(0xF4, 0x20|0x0E); err != nil { + return wrap(err) + } + time.Sleep(4500 * time.Microsecond) + ut, err := d.dev.ReadUint16(0xF6) + if err != nil { + return wrap(err) } + temp := d.cal.compensateTemp(ut) - if !d.cal.isValid() { - return nil, errors.New("calibration data is invalid") + // Request pressure conversion and read measurement. + if err := d.dev.WriteUint8(0xF4, 0x20|0x14|(uint8(d.os)<<6)); err != nil { + return wrap(err) + } + time.Sleep(pressureConvTime[d.os]) + var pressureBuf [3]byte + if err := d.dev.ReadStruct(0xF6, pressureBuf[:]); err != nil { + return wrap(err) } + up := (int32(pressureBuf[0])<<16 + int32(pressureBuf[1])<<8 | int32(pressureBuf[2])) >> (8 - d.os) + pressure := d.cal.compensatePressure(up, int32(ut), d.os) + env.Temperature = devices.Celsius(temp * 100) + env.Pressure = devices.KPascal(pressure) + return nil +} - return d, nil +func (d *Dev) sensingContinuous(interval time.Duration, sensing chan<- devices.Environment, stop <-chan struct{}) { + t := time.NewTicker(interval) + defer t.Stop() + + for { + // Do one initial sensing right away. + var e devices.Environment + d.mu.Lock() + err := d.sense(&e) + d.mu.Unlock() + if err != nil { + log.Printf("bmp180: failed to sense: %v", err) + return + } + select { + case sensing <- e: + case <-stop: + return + } + select { + case <-stop: + return + case <-t.C: + } + } +} + +// + +// Maximum conversion time for pressure. +var pressureConvTime = [...]time.Duration{ + 4500 * time.Microsecond, + 7500 * time.Microsecond, + 13500 * time.Microsecond, + 25500 * time.Microsecond, } -// calibration data read from the internal EEPROM (datasheet page 13) +// calibration data read from the internal EEPROM (datasheet page 13). type calibration struct { AC1, AC2, AC3 int16 AC4, AC5, AC6 uint16 @@ -249,6 +275,10 @@ func (c *calibration) compensatePressure(up, ut int32, os Oversampling) uint32 { return uint32(p) } +func wrap(err error) error { + return fmt.Errorf("bmp180: %v", err) +} + var _ devices.Environmental = &Dev{} var _ devices.Device = &Dev{} var _ fmt.Stringer = &Dev{} diff --git a/devices/bmp180/bmp180_test.go b/devices/bmp180/bmp180_test.go new file mode 100644 index 0000000..d3138bd --- /dev/null +++ b/devices/bmp180/bmp180_test.go @@ -0,0 +1,362 @@ +// Copyright 2017 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package bmp180 + +import ( + "testing" + "time" + + "periph.io/x/periph/conn/i2c/i2ctest" + "periph.io/x/periph/devices" +) + +func TestNew_fail_read_chipid(t *testing.T) { + bus := i2ctest.Playback{ + Ops: []i2ctest.IO{ + // Chip ID detection read fail. + }, + DontPanic: true, + } + if _, err := New(&bus, O1x); err == nil { + t.Fatal("can't read chip ID") + } +} + +func TestNew_bad_chipid(t *testing.T) { + bus := i2ctest.Playback{ + Ops: []i2ctest.IO{ + // Bad Chip ID detection. + {Addr: 0x77, W: []byte{0xd0}, R: []byte{0x60}}, + }, + } + if _, err := New(&bus, O1x); err == nil { + t.Fatal("bad chip ID") + } +} + +func TestNew_fail_calib(t *testing.T) { + bus := i2ctest.Playback{ + Ops: []i2ctest.IO{ + // Chip ID detection. + {Addr: 0x77, W: []byte{0xd0}, R: []byte{0x55}}, + // Fail to read calibration data. + }, + DontPanic: true, + } + if _, err := New(&bus, O1x); err == nil { + t.Fatal("can't read calibration") + } +} + +func TestNew_bad_calib(t *testing.T) { + bus := i2ctest.Playback{ + Ops: []i2ctest.IO{ + // Chip ID detection. + {Addr: 0x77, W: []byte{0xd0}, R: []byte{0x55}}, + // Calibration data. + { + Addr: 0x77, + W: []byte{0xaa}, + R: []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + }, + DontPanic: true, + } + if _, err := New(&bus, O1x); err == nil { + t.Fatal("bad calibration") + } +} + +func TestSense_success(t *testing.T) { + bus := i2ctest.Playback{ + Ops: []i2ctest.IO{ + // Chip ID detection. + {Addr: 0x77, W: []byte{0xd0}, R: []byte{0x55}}, + // Calibration data. + { + Addr: 0x77, + W: []byte{0xaa}, + R: []byte{35, 136, 251, 103, 199, 169, 135, 91, 98, 137, 80, 22, 25, 115, 0, 46, 128, 0, 209, 246, 10, 123}, + }, + // Request temperature. + {Addr: 0x77, W: []byte{0xF4, 0x2E}}, + // Read temperature. + {Addr: 0x77, W: []byte{0xF6}, R: []byte{0x71, 0xBf}}, + // Request pressure. + {Addr: 0x77, W: []byte{0xF4, 0x34}}, + // Read pressure. + {Addr: 0x77, W: []byte{0xF6}, R: []byte{0xAb, 0x96, 0}}, + }, + } + dev, err := New(&bus, O1x) + if err != nil { + t.Fatal(err) + } + if s := dev.String(); s != "BMP180{playback(119)}" { + t.Fatal(s) + } + env := devices.Environment{} + if err := dev.Sense(&env); err != nil { + t.Fatal(err) + } + if env.Temperature != 25300 { + t.Fatalf("temp %d", env.Temperature) + } + if env.Pressure != 100567 { + t.Fatalf("pressure %d", env.Pressure) + } + if env.Humidity != 0 { + t.Fatalf("humidity %d", env.Humidity) + } + if err := dev.Halt(); err != nil { + t.Fatal(err) + } + if err := bus.Close(); err != nil { + t.Fatal(err) + } +} + +func TestSense_fail_1(t *testing.T) { + bus := i2ctest.Playback{ + Ops: []i2ctest.IO{ + // Chip ID detection. + {Addr: 0x77, W: []byte{0xd0}, R: []byte{0x55}}, + // Calibration data. + { + Addr: 0x77, + W: []byte{0xaa}, + R: []byte{35, 136, 251, 103, 199, 169, 135, 91, 98, 137, 80, 22, 25, 115, 0, 46, 128, 0, 209, 246, 10, 123}, + }, + // Request temperature fail. + }, + DontPanic: true, + } + dev, err := New(&bus, O1x) + if err != nil { + t.Fatal(err) + } + env := devices.Environment{} + if dev.Sense(&env) == nil { + t.Fatal("sensing should have failed") + } + if err := bus.Close(); err != nil { + t.Fatal(err) + } +} + +func TestSense_fail_2(t *testing.T) { + bus := i2ctest.Playback{ + Ops: []i2ctest.IO{ + // Chip ID detection. + {Addr: 0x77, W: []byte{0xd0}, R: []byte{0x55}}, + // Calibration data. + { + Addr: 0x77, + W: []byte{0xaa}, + R: []byte{35, 136, 251, 103, 199, 169, 135, 91, 98, 137, 80, 22, 25, 115, 0, 46, 128, 0, 209, 246, 10, 123}, + }, + // Request temperature. + {Addr: 0x77, W: []byte{0xF4, 0x2E}}, + // Read temperature fail. + }, + DontPanic: true, + } + dev, err := New(&bus, O1x) + if err != nil { + t.Fatal(err) + } + env := devices.Environment{} + if dev.Sense(&env) == nil { + t.Fatal("sensing should have failed") + } + if err := bus.Close(); err != nil { + t.Fatal(err) + } +} + +func TestSense_fail_3(t *testing.T) { + bus := i2ctest.Playback{ + Ops: []i2ctest.IO{ + // Chip ID detection. + {Addr: 0x77, W: []byte{0xd0}, R: []byte{0x55}}, + // Calibration data. + { + Addr: 0x77, + W: []byte{0xaa}, + R: []byte{35, 136, 251, 103, 199, 169, 135, 91, 98, 137, 80, 22, 25, 115, 0, 46, 128, 0, 209, 246, 10, 123}, + }, + // Request temperature. + {Addr: 0x77, W: []byte{0xF4, 0x2E}}, + // Read temperature fail. + {Addr: 0x77, W: []byte{0xF6}, R: []byte{0x71, 0xBf}}, + // Request pressure fail. + }, + DontPanic: true, + } + dev, err := New(&bus, O1x) + if err != nil { + t.Fatal(err) + } + env := devices.Environment{} + if dev.Sense(&env) == nil { + t.Fatal("sensing should have failed") + } + if err := bus.Close(); err != nil { + t.Fatal(err) + } +} + +func TestSense_fail_4(t *testing.T) { + bus := i2ctest.Playback{ + Ops: []i2ctest.IO{ + // Chip ID detection. + {Addr: 0x77, W: []byte{0xd0}, R: []byte{0x55}}, + // Calibration data. + { + Addr: 0x77, + W: []byte{0xaa}, + R: []byte{35, 136, 251, 103, 199, 169, 135, 91, 98, 137, 80, 22, 25, 115, 0, 46, 128, 0, 209, 246, 10, 123}, + }, + // Request temperature. + {Addr: 0x77, W: []byte{0xF4, 0x2E}}, + // Read temperature fail. + {Addr: 0x77, W: []byte{0xF6}, R: []byte{0x71, 0xBf}}, + // Request pressure. + {Addr: 0x77, W: []byte{0xF4, 0x34}}, + // Read pressure fail. + }, + DontPanic: true, + } + dev, err := New(&bus, O1x) + if err != nil { + t.Fatal(err) + } + env := devices.Environment{} + if dev.Sense(&env) == nil { + t.Fatal("sensing should have failed") + } + if err := bus.Close(); err != nil { + t.Fatal(err) + } +} + +func TestSenseContinuous_success(t *testing.T) { + bus := i2ctest.Playback{ + Ops: []i2ctest.IO{ + // Chip ID detection. + {Addr: 0x77, W: []byte{0xd0}, R: []byte{0x55}}, + // Calibration data. + { + Addr: 0x77, + W: []byte{0xaa}, + R: []byte{35, 136, 251, 103, 199, 169, 135, 91, 98, 137, 80, 22, 25, 115, 0, 46, 128, 0, 209, 246, 10, 123}, + }, + // Request temperature. + {Addr: 0x77, W: []byte{0xF4, 0x2E}}, + // Read temperature. + {Addr: 0x77, W: []byte{0xF6}, R: []byte{0x71, 0xBf}}, + // Request pressure. + {Addr: 0x77, W: []byte{0xF4, 0x34}}, + // Read pressure. + {Addr: 0x77, W: []byte{0xF6}, R: []byte{0xAb, 0x96, 0}}, + // Request temperature. + {Addr: 0x77, W: []byte{0xF4, 0x2E}}, + // Read temperature. + {Addr: 0x77, W: []byte{0xF6}, R: []byte{0x71, 0xBf}}, + // Request pressure. + {Addr: 0x77, W: []byte{0xF4, 0x34}}, + // Read pressure. + {Addr: 0x77, W: []byte{0xF6}, R: []byte{0xAb, 0x96, 0}}, + }, + DontPanic: true, + } + dev, err := New(&bus, O1x) + if err != nil { + t.Fatal(err) + } + c, err := dev.SenseContinuous(time.Minute) + if err != nil { + t.Fatal(err) + } + env := <-c + if env.Temperature != 25300 { + t.Fatalf("temp %d", env.Temperature) + } + if env.Pressure != 100567 { + t.Fatalf("pressure %d", env.Pressure) + } + if env.Humidity != 0 { + t.Fatalf("humidity %d", env.Humidity) + } + + if dev.Sense(&env) == nil { + t.Fatal("Sense() should have failed") + } + + c2, err := dev.SenseContinuous(time.Minute) + if err != nil { + t.Fatal(err) + } + + env = <-c2 + + if _, ok := <-c; ok { + t.Fatal("c should be closed") + } + + if err := dev.Halt(); err != nil { + t.Fatal(err) + } + if _, ok := <-c2; ok { + t.Fatal("c2 should be closed") + } + + if err := bus.Close(); err != nil { + t.Fatal(err) + } +} + +func TestOversampling(t *testing.T) { + data := []struct { + o Oversampling + v int + s string + }{ + {O1x, 1, "1x"}, + {O2x, 2, "2x"}, + {O4x, 4, "4x"}, + {O8x, 8, "8x"}, + {Oversampling(100), 0, "Oversampling(100)"}, + } + for i, line := range data { + if s := line.o.String(); s != line.s { + t.Fatalf("#%d %s != %s", i, s, line.s) + } + } +} + +func TestCompensate(t *testing.T) { + c := calibration{ + AC1: 408, + AC2: -72, + AC3: -14383, + AC4: 32741, + AC5: 32757, + AC6: 23153, + B1: 6190, + B2: 4, + MB: -32768, + MC: -8711, + MD: 2868, + } + + if temp := c.compensateTemp(27898); temp != 150 { + t.Errorf("temperature is wrong, want %v, got %v", 150, temp) + } + + if pressure := c.compensatePressure(23843, 27898, 0); pressure != 69964 { + t.Errorf("pressure is wrong, want %v, got %v", 69964, pressure) + } +} diff --git a/experimental/devices/bmp180/bmp180_test.go b/experimental/devices/bmp180/bmp180_test.go deleted file mode 100644 index b5b2ba7..0000000 --- a/experimental/devices/bmp180/bmp180_test.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2017 The Periph Authors. All rights reserved. -// Use of this source code is governed under the Apache License, Version 2.0 -// that can be found in the LICENSE file. - -package bmp180 - -import "testing" - -func TestCompensate(t *testing.T) { - c := calibration{ - AC1: 408, - AC2: -72, - AC3: -14383, - AC4: 32741, - AC5: 32757, - AC6: 23153, - B1: 6190, - B2: 4, - MB: -32768, - MC: -8711, - MD: 2868, - } - - if temp := c.compensateTemp(27898); temp != 150 { - t.Errorf("temperature is wrong, want %v, got %v", 150, temp) - } - - if pressure := c.compensatePressure(23843, 27898, 0); pressure != 69964 { - t.Errorf("pressure is wrong, want %v, got %v", 69964, pressure) - } -}