mirror of https://github.com/periph/devices
tmp102: Initial add of tmp102 driver. (#76)
parent
2cf8fa10c9
commit
f9d46888f5
@ -0,0 +1,21 @@
|
|||||||
|
// Copyright 2024 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.
|
||||||
|
//
|
||||||
|
// tmp102 provides a package for interfacing a Texas Instruments TMP102 I2C
|
||||||
|
// temperature sensor. This driver is also compatible with the TMP112 and
|
||||||
|
// TMP75 sensors.
|
||||||
|
//
|
||||||
|
// Range: -40°C - 125°C
|
||||||
|
//
|
||||||
|
// Accuracy: +/- 0.5°C
|
||||||
|
//
|
||||||
|
// Resolution: 0.0625°C
|
||||||
|
//
|
||||||
|
// For detailed information, refer to the [datasheet].
|
||||||
|
//
|
||||||
|
// A [command line example] is available in periph.io/x/devices/cmd/tmp102
|
||||||
|
//
|
||||||
|
// [datasheet]: https://www.ti.com/lit/ds/symlink/tmp102.pdf
|
||||||
|
// [command line example]: https://github.com/periph/cmd/tree/main/tmp102/
|
||||||
|
package tmp102
|
||||||
@ -0,0 +1,389 @@
|
|||||||
|
// Copyright 2024 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 tmp102
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"periph.io/x/conn/v3"
|
||||||
|
"periph.io/x/conn/v3/i2c"
|
||||||
|
"periph.io/x/conn/v3/physic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConversionRate byte
|
||||||
|
|
||||||
|
type AlertMode byte
|
||||||
|
|
||||||
|
// Dev represents a TMP102 sensor.
|
||||||
|
type Dev struct {
|
||||||
|
d *i2c.Dev
|
||||||
|
shutdown chan bool
|
||||||
|
mu sync.Mutex
|
||||||
|
opts *Opts
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Conversion (sample) Rates. The device default is 4 readings/second.
|
||||||
|
RateQuarterHertz ConversionRate = iota
|
||||||
|
RateOneHertz
|
||||||
|
RateFourHertz
|
||||||
|
RateEightHertz
|
||||||
|
|
||||||
|
// ModeComparator sets the device to operate in Comparator mode.
|
||||||
|
// Refer to section 6.4.5.1 of the TMP102 datasheet. When used, the
|
||||||
|
// ALERT pin of the TMP102 will trigger.
|
||||||
|
ModeComparator AlertMode = 0
|
||||||
|
// ModeInterrupt sets the device to operate in Interrupt mode.
|
||||||
|
// Note that reading the temperature will clear the alert, so be aware
|
||||||
|
// if you're using SenseContinuous.
|
||||||
|
ModeInterrupt AlertMode = 1
|
||||||
|
|
||||||
|
// Addresses of registers to read/write.
|
||||||
|
_REGISTER_TEMPERATURE byte = 0
|
||||||
|
_REGISTER_CONFIGURATION byte = 1
|
||||||
|
_REGISTER_RANGE_LOW byte = 2
|
||||||
|
_REGISTER_RANGE_HIGH byte = 3
|
||||||
|
|
||||||
|
// Bit numbers for various configuration operations.
|
||||||
|
_SHUTDOWN_BIT int = 8
|
||||||
|
_THERMOSTAT_MODE int = 9
|
||||||
|
_CONVERSION_RATE_POS int = 6
|
||||||
|
|
||||||
|
_DEGREES_RESOLUTION physic.Temperature = 62_500 * physic.MicroKelvin
|
||||||
|
|
||||||
|
// The minimum temperature in StandardMode the device can read.
|
||||||
|
MinimumTemperature physic.Temperature = physic.ZeroCelsius - 40*physic.Kelvin
|
||||||
|
// The maximum temperature in StandardMode the device can read.
|
||||||
|
MaximumTemperature physic.Temperature = physic.ZeroCelsius + 125*physic.Kelvin
|
||||||
|
)
|
||||||
|
|
||||||
|
// Opts represents configurable options for the TMP102.
|
||||||
|
type Opts struct {
|
||||||
|
SampleRate ConversionRate
|
||||||
|
AlertSetting AlertMode
|
||||||
|
AlertLow physic.Temperature
|
||||||
|
AlertHigh physic.Temperature
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dev *Dev) isShutdown() bool {
|
||||||
|
return dev.shutdown == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// start initializes the device to a known state and ensures its
|
||||||
|
// not in shutdown mode.
|
||||||
|
func (dev *Dev) start() error {
|
||||||
|
config := dev.ReadConfiguration()
|
||||||
|
mask := uint16(0xffff) ^ (uint16(1<<_SHUTDOWN_BIT) | uint16(1<<_THERMOSTAT_MODE))
|
||||||
|
config &= mask
|
||||||
|
|
||||||
|
config |= uint16(dev.opts.AlertSetting) << _THERMOSTAT_MODE
|
||||||
|
|
||||||
|
cr := ConversionRate((config >> _CONVERSION_RATE_POS) & 0x03)
|
||||||
|
if cr != dev.opts.SampleRate {
|
||||||
|
// Turn off the sample rate bits.
|
||||||
|
config &= 0xffff ^ uint16(0x03<<_CONVERSION_RATE_POS)
|
||||||
|
// Now set the new value.
|
||||||
|
config |= uint16(dev.opts.SampleRate) << _CONVERSION_RATE_POS
|
||||||
|
}
|
||||||
|
|
||||||
|
var bits []byte
|
||||||
|
w := make([]byte, 3)
|
||||||
|
w[0] = _REGISTER_CONFIGURATION
|
||||||
|
w[1] = byte(config>>8) & 0xff
|
||||||
|
w[2] = byte(config & 0xff)
|
||||||
|
|
||||||
|
err := dev.d.Tx(w, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dev.shutdown = make(chan bool)
|
||||||
|
if dev.opts.AlertLow != 0 {
|
||||||
|
bits, err = temperatureToCount(dev.opts.AlertLow)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w[0] = _REGISTER_RANGE_LOW
|
||||||
|
w[1] = bits[0]
|
||||||
|
w[2] = bits[1]
|
||||||
|
err = dev.d.Tx(w, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if dev.opts.AlertHigh != 0 {
|
||||||
|
bits, err = temperatureToCount(dev.opts.AlertHigh)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w[0] = _REGISTER_RANGE_HIGH
|
||||||
|
w[1] = bits[0]
|
||||||
|
w[2] = bits[1]
|
||||||
|
err = dev.d.Tx(w, nil)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// temperatureToCount converts a temperature into the count that the device
|
||||||
|
// uses. Required to set the Low/High Range registers for alerts.
|
||||||
|
func temperatureToCount(temp physic.Temperature) ([]byte, error) {
|
||||||
|
result := make([]byte, 2)
|
||||||
|
if temp == physic.ZeroCelsius {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
negative := temp < physic.ZeroCelsius
|
||||||
|
var count uint16
|
||||||
|
if negative {
|
||||||
|
temp = physic.ZeroCelsius + physic.Temperature(-1*temp.Celsius())*physic.Kelvin
|
||||||
|
count = uint16((temp - physic.ZeroCelsius) / _DEGREES_RESOLUTION)
|
||||||
|
count = ((twosComplement(count) | (1 << 11)) + 1)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
count = uint16((temp - physic.ZeroCelsius) / _DEGREES_RESOLUTION)
|
||||||
|
|
||||||
|
}
|
||||||
|
count = count << 4
|
||||||
|
result[0] = byte(count >> 8 & 0xff)
|
||||||
|
result[1] = byte(count & 0xf0)
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func twosComplement(value uint16) uint16 {
|
||||||
|
var result uint16
|
||||||
|
for iter := 0; iter < 11; iter++ {
|
||||||
|
bitVal := uint16(1 << iter)
|
||||||
|
if (value & bitVal) == 0 {
|
||||||
|
result |= bitVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// countToTemperature returns the temperature from the raw device count.
|
||||||
|
func countToTemperature(bytes []byte) physic.Temperature {
|
||||||
|
var t physic.Temperature
|
||||||
|
count := (uint16(bytes[0]) << 4) | (uint16(bytes[1]) >> 4)
|
||||||
|
negative := (count & (1 << 11)) > 0
|
||||||
|
|
||||||
|
if negative {
|
||||||
|
count = twosComplement(count) + 1
|
||||||
|
t = physic.ZeroCelsius - (physic.Temperature(count) * _DEGREES_RESOLUTION)
|
||||||
|
} else {
|
||||||
|
t = physic.ZeroCelsius + (physic.Temperature(count) * _DEGREES_RESOLUTION)
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// readConfiguration returns the device's configuration registers as a 16 bit
|
||||||
|
// unsigned integer. Refer to the datasheet for interpretation.
|
||||||
|
func (dev *Dev) ReadConfiguration() uint16 {
|
||||||
|
w := make([]byte, 1)
|
||||||
|
w[0] = _REGISTER_CONFIGURATION
|
||||||
|
r := make([]byte, 2)
|
||||||
|
_ = dev.d.Tx(w, r)
|
||||||
|
result := uint16(r[0])<<8 | uint16(r[1])
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// readTemperature returns the raw counts from the device temperature registers.
|
||||||
|
func (dev *Dev) readTemperature() (physic.Temperature, error) {
|
||||||
|
var err error
|
||||||
|
if dev.isShutdown() {
|
||||||
|
err = dev.start()
|
||||||
|
if err != nil {
|
||||||
|
return MinimumTemperature, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r := make([]byte, 2)
|
||||||
|
err = dev.d.Tx([]byte{_REGISTER_TEMPERATURE}, r)
|
||||||
|
if err != nil {
|
||||||
|
return MinimumTemperature, err
|
||||||
|
}
|
||||||
|
return countToTemperature(r), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewI2C returns a new TMP102 sensor using the specified bus and address.
|
||||||
|
// If opts is not supplied, the configuration of the sensor is set to the
|
||||||
|
// default on startup.
|
||||||
|
func NewI2C(b i2c.Bus, addr uint16, opts *Opts) (*Dev, error) {
|
||||||
|
if opts == nil {
|
||||||
|
opts = &Opts{SampleRate: RateFourHertz, AlertSetting: ModeComparator}
|
||||||
|
}
|
||||||
|
d := &Dev{d: &i2c.Dev{Bus: b, Addr: addr}, opts: opts, shutdown: nil}
|
||||||
|
return d, d.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAlertMode returns the current alert settings for the device.
|
||||||
|
func (dev *Dev) GetAlertMode() (mode AlertMode, rangeLow, rangeHigh physic.Temperature, err error) {
|
||||||
|
dev.mu.Lock()
|
||||||
|
defer dev.mu.Unlock()
|
||||||
|
|
||||||
|
mode = AlertMode((dev.ReadConfiguration() >> _THERMOSTAT_MODE) & 0x01)
|
||||||
|
rangeLow = MinimumTemperature
|
||||||
|
rangeHigh = MaximumTemperature
|
||||||
|
|
||||||
|
w := make([]byte, 1)
|
||||||
|
r := make([]byte, 2)
|
||||||
|
|
||||||
|
w[0] = _REGISTER_RANGE_LOW
|
||||||
|
err = dev.d.Tx(w, r)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rangeLow = countToTemperature(r)
|
||||||
|
|
||||||
|
w[0] = _REGISTER_RANGE_HIGH
|
||||||
|
err = dev.d.Tx(w, r)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rangeHigh = countToTemperature(r)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Halt shuts down the device. If a SenseContinuous operation is in progress,
|
||||||
|
// its aborted. Implements conn.Resource
|
||||||
|
func (dev *Dev) Halt() error {
|
||||||
|
dev.mu.Lock()
|
||||||
|
defer dev.mu.Unlock()
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if dev.shutdown != nil {
|
||||||
|
close(dev.shutdown)
|
||||||
|
dev.shutdown = nil
|
||||||
|
}
|
||||||
|
current := dev.ReadConfiguration()
|
||||||
|
mask := uint16(0xffff ^ (1 << _SHUTDOWN_BIT))
|
||||||
|
new := current & mask
|
||||||
|
if current != new {
|
||||||
|
w := make([]byte, 3)
|
||||||
|
w[0] = _REGISTER_CONFIGURATION
|
||||||
|
w[1] = byte(new >> 8)
|
||||||
|
w[2] = byte(new & 0xff)
|
||||||
|
err = dev.d.Tx(w, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sense reads temperature from the device and writes the value to the specified
|
||||||
|
// env variable. Implements physic.SenseEnv.
|
||||||
|
func (dev *Dev) Sense(env *physic.Env) error {
|
||||||
|
dev.mu.Lock()
|
||||||
|
defer dev.mu.Unlock()
|
||||||
|
t, err := dev.readTemperature()
|
||||||
|
if err == nil {
|
||||||
|
env.Temperature = t
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SenseContinuous continuously reads from the device and writes the value to
|
||||||
|
// the returned channel. Implements physic.SenseEnv. To terminate the
|
||||||
|
// continuous read, call Halt().
|
||||||
|
func (dev *Dev) SenseContinuous(interval time.Duration) (<-chan physic.Env, error) {
|
||||||
|
channelSize := 16
|
||||||
|
if interval < (125 * time.Millisecond) {
|
||||||
|
return nil, errors.New("invalid duration. minimum 125ms")
|
||||||
|
}
|
||||||
|
channel := make(chan physic.Env, channelSize)
|
||||||
|
go func(channel chan physic.Env, shutdown <-chan bool) {
|
||||||
|
ticker := time.NewTicker(interval)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-shutdown:
|
||||||
|
close(channel)
|
||||||
|
return
|
||||||
|
case <-ticker.C:
|
||||||
|
// do the reading and write to the channel.
|
||||||
|
e := physic.Env{}
|
||||||
|
err := dev.Sense(&e)
|
||||||
|
if err == nil && len(channel) < channelSize {
|
||||||
|
channel <- e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(channel, dev.shutdown)
|
||||||
|
|
||||||
|
return channel, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAlertMode sets the device to operate in alert (thermostat) mode. Alert
|
||||||
|
// mode will set the Alert pin on the device to active mode when the conditions
|
||||||
|
// apply. Refer to section 6.4.5 and section 6.5.4 of the TMP102 datasheet.
|
||||||
|
//
|
||||||
|
// To detect the alert trigger, you will need to connect the device ALERT pin
|
||||||
|
// to a GPIO pin on your SBC and configure that GPIO pin with edge detection,
|
||||||
|
// or continuously poll the GPIO pin state. If you choose polling, care should
|
||||||
|
// be taken if you're also using SenseContinuous.
|
||||||
|
func (dev *Dev) SetAlertMode(mode AlertMode, rangeLow, rangeHigh physic.Temperature) error {
|
||||||
|
if rangeLow >= rangeHigh ||
|
||||||
|
rangeLow < MinimumTemperature ||
|
||||||
|
rangeHigh > MaximumTemperature {
|
||||||
|
return errors.New("invalid temperature range")
|
||||||
|
}
|
||||||
|
dev.opts.AlertSetting = mode
|
||||||
|
dev.opts.AlertLow = rangeLow
|
||||||
|
dev.opts.AlertHigh = rangeHigh
|
||||||
|
var err error
|
||||||
|
|
||||||
|
dev.mu.Lock()
|
||||||
|
defer dev.mu.Unlock()
|
||||||
|
|
||||||
|
// Write the low range temperature
|
||||||
|
rangeBytes, _ := temperatureToCount(rangeLow)
|
||||||
|
w := make([]byte, 3)
|
||||||
|
w[0] = _REGISTER_RANGE_LOW
|
||||||
|
w[1] = rangeBytes[0]
|
||||||
|
w[2] = rangeBytes[1]
|
||||||
|
err = dev.d.Tx(w, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Write the High Range Temperature
|
||||||
|
rangeBytes, _ = temperatureToCount(rangeHigh)
|
||||||
|
w[0] = _REGISTER_RANGE_HIGH
|
||||||
|
w[1] = rangeBytes[0]
|
||||||
|
w[2] = rangeBytes[1]
|
||||||
|
err = dev.d.Tx(w, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Check if the device is in shutdown, or if the mode has
|
||||||
|
// changed, and update the device running configuration
|
||||||
|
running := dev.ReadConfiguration()
|
||||||
|
mask := uint16(0xffff ^ ((1 << _SHUTDOWN_BIT) | (1 << _THERMOSTAT_MODE)))
|
||||||
|
new := (running & mask) | uint16(mode)<<_THERMOSTAT_MODE
|
||||||
|
if new != running {
|
||||||
|
w[0] = _REGISTER_CONFIGURATION
|
||||||
|
w[1] = byte(new >> 8)
|
||||||
|
w[2] = byte(new & 0xff)
|
||||||
|
err = dev.d.Tx(w, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Precision returns the sensor's precision, or minimum value between steps the
|
||||||
|
// device can make. The specified precision is 0.0625 degrees Celsius. Note
|
||||||
|
// that the accuracy of the device is +/- 0.5 degrees Celsius.
|
||||||
|
func (dev *Dev) Precision(env *physic.Env) {
|
||||||
|
env.Temperature = _DEGREES_RESOLUTION
|
||||||
|
env.Pressure = 0
|
||||||
|
env.Humidity = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dev *Dev) String() string {
|
||||||
|
return fmt.Sprintf("tmp102: %s", dev.d.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ conn.Resource = &Dev{}
|
||||||
|
var _ physic.SenseEnv = &Dev{}
|
||||||
@ -0,0 +1,176 @@
|
|||||||
|
// Copyright 2024 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 tmp102
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"periph.io/x/conn/v3/i2c/i2ctest"
|
||||||
|
"periph.io/x/conn/v3/physic"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Default value for alert low range value.
|
||||||
|
DefaultLow physic.Temperature = physic.ZeroCelsius + 75*physic.Kelvin
|
||||||
|
// Default value for alert high range value.
|
||||||
|
DefaultHigh physic.Temperature = physic.ZeroCelsius + 80*physic.Kelvin
|
||||||
|
|
||||||
|
addr uint16 = 0x48
|
||||||
|
)
|
||||||
|
|
||||||
|
func defaultOps() []i2ctest.IO {
|
||||||
|
ops := []i2ctest.IO{
|
||||||
|
{Addr: addr, W: []byte{_REGISTER_CONFIGURATION}},
|
||||||
|
{Addr: addr, W: []byte{_REGISTER_CONFIGURATION, 0x00, 0x80}}, // Write the config
|
||||||
|
{Addr: addr, W: []byte{_REGISTER_RANGE_LOW, 0x4b, 0x00}}, // Write the low alert temp
|
||||||
|
{Addr: addr, W: []byte{_REGISTER_RANGE_HIGH, 0x50, 0x00}}, // Write the high alert temp
|
||||||
|
}
|
||||||
|
return ops
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSenseContinous test the sense continuous function, which
|
||||||
|
// implicitly tests Sense() and countToTemperature().
|
||||||
|
func TestSenseContinuous(t *testing.T) {
|
||||||
|
// A set of counts, and the expected temperature value.
|
||||||
|
tests := []struct {
|
||||||
|
bits []byte
|
||||||
|
expected physic.Temperature
|
||||||
|
}{
|
||||||
|
{[]byte{0x64, 0x00}, physic.ZeroCelsius + 100*physic.Kelvin},
|
||||||
|
{[]byte{0x50, 0x00}, physic.ZeroCelsius + 80*physic.Kelvin},
|
||||||
|
{[]byte{0x32, 0x00}, physic.ZeroCelsius + 50*physic.Kelvin},
|
||||||
|
{[]byte{0x19, 0x00}, physic.ZeroCelsius + 25*physic.Kelvin},
|
||||||
|
{[]byte{0x00, 0x00}, physic.ZeroCelsius},
|
||||||
|
{[]byte{0xe7, 0x00}, physic.ZeroCelsius - 25*physic.Kelvin},
|
||||||
|
{[]byte{0xc9, 0x00}, physic.ZeroCelsius - 55*physic.Kelvin},
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := Opts{
|
||||||
|
SampleRate: RateFourHertz,
|
||||||
|
AlertSetting: ModeComparator,
|
||||||
|
AlertLow: DefaultLow,
|
||||||
|
AlertHigh: DefaultHigh,
|
||||||
|
}
|
||||||
|
|
||||||
|
ops := defaultOps()
|
||||||
|
// Add the test values to our playback bus.
|
||||||
|
for _, test := range tests {
|
||||||
|
ops = append(ops, i2ctest.IO{Addr: addr, W: []byte{_REGISTER_TEMPERATURE}, R: test.bits})
|
||||||
|
}
|
||||||
|
pb := &i2ctest.Playback{Ops: ops, DontPanic: true, Count: 1}
|
||||||
|
defer pb.Close()
|
||||||
|
record := &i2ctest.Record{Bus: pb}
|
||||||
|
|
||||||
|
tmp102, err := NewI2C(record, addr, &opts)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ch, err := tmp102.SenseContinuous(250 * time.Millisecond)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for count := 0; count < len(tests); count++ {
|
||||||
|
env := <-ch
|
||||||
|
t.Logf("Temperature = %.4f", env.Temperature.Celsius())
|
||||||
|
if env.Temperature != tests[count].expected {
|
||||||
|
t.Errorf("Error testing. Read: %.4f Expected %.4f", env.Temperature.Celsius(), tests[count].expected.Celsius())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
err = tmp102.Halt()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
t.Logf("record.ops=%#v", record.Ops)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestString(t *testing.T) {
|
||||||
|
ops := defaultOps()
|
||||||
|
pb := &i2ctest.Playback{Ops: ops, DontPanic: true, Count: 1}
|
||||||
|
defer pb.Close()
|
||||||
|
record := &i2ctest.Record{Bus: pb}
|
||||||
|
tmp102, err := NewI2C(record, addr, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s := tmp102.String()
|
||||||
|
t.Log(s)
|
||||||
|
if len(s) == 0 {
|
||||||
|
t.Error("invalid String() result")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetAlertMode(t *testing.T) {
|
||||||
|
ops := make([]i2ctest.IO, 0)
|
||||||
|
ops = append(ops, []i2ctest.IO{
|
||||||
|
{Addr: addr, W: []byte{_REGISTER_CONFIGURATION}, R: []byte{0x00, 0x00}}, // Read the device config.
|
||||||
|
{Addr: addr, W: []byte{_REGISTER_CONFIGURATION, 0x00, 0x80}}, // Set the device config.
|
||||||
|
{Addr: addr, W: []byte{_REGISTER_RANGE_LOW}, R: []byte{0x4b, 0}}, // Read the low limit register
|
||||||
|
{Addr: addr, W: []byte{_REGISTER_RANGE_HIGH}, R: []byte{0x50, 0}}, // Read the High Limit Register
|
||||||
|
{Addr: addr, W: []byte{_REGISTER_RANGE_LOW, 0x4b, 0x80}}, // Set the read of the low limit to 75C
|
||||||
|
{Addr: addr, W: []byte{_REGISTER_RANGE_HIGH, 0x4f, 0x80}}, // Set the read of the high limit to 80C
|
||||||
|
{Addr: addr, W: []byte{_REGISTER_CONFIGURATION, 0x02, 0x00}}, // Add 1/2 Degree C to the range low
|
||||||
|
{Addr: addr, W: []byte{_REGISTER_CONFIGURATION}, R: []byte{0x02, 0}}, // Read the confugration register.
|
||||||
|
{Addr: addr, W: []byte{_REGISTER_RANGE_LOW}, R: []byte{0x4b, 0x80}}, // Read the low temp register
|
||||||
|
{Addr: addr, W: []byte{_REGISTER_RANGE_HIGH}, R: []byte{0x4f, 0x80}}, // Read the high temp register
|
||||||
|
{Addr: addr, W: []byte{_REGISTER_RANGE_LOW, 0x4b, 0x00}}, // write it back to 75C
|
||||||
|
{Addr: addr, W: []byte{_REGISTER_RANGE_HIGH, 0x50, 0x00}}, // set it back to 80C
|
||||||
|
}...)
|
||||||
|
pb := &i2ctest.Playback{Ops: ops, DontPanic: true, Count: 1}
|
||||||
|
defer pb.Close()
|
||||||
|
record := &i2ctest.Record{Bus: pb}
|
||||||
|
defer t.Logf("record=%#v", record)
|
||||||
|
tmp102, err := NewI2C(record, addr, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mode, low, high, err := tmp102.GetAlertMode()
|
||||||
|
t.Logf("newMode=%d, newLow=%.4f, newHigh=%.4f", mode, low.Celsius(), high.Celsius())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
var newMode AlertMode
|
||||||
|
if mode == ModeComparator {
|
||||||
|
newMode = ModeInterrupt
|
||||||
|
} else {
|
||||||
|
newMode = ModeComparator
|
||||||
|
}
|
||||||
|
newLow := low + 500*physic.MilliKelvin
|
||||||
|
newHigh := high - 500*physic.MilliKelvin
|
||||||
|
t.Logf("newMode=%d, newLow=%.4f, newHigh=%.4f", newMode, newLow.Celsius(), newHigh.Celsius())
|
||||||
|
err = tmp102.SetAlertMode(newMode, newLow, newHigh)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkMode, checkLow, checkHigh, err := tmp102.GetAlertMode()
|
||||||
|
t.Logf("checkMode=%d checkLow=%.4f checkHigh=%.4f", checkMode, checkLow.Celsius(), checkHigh.Celsius())
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if checkMode != newMode || checkLow != newLow || checkHigh != newHigh {
|
||||||
|
t.Errorf("Error setting/reading alert mode. Received: Mode=%d, Low=%.4f, High=%.4f. Expected: Mode:%d, Low=%.4f, High=%.4f",
|
||||||
|
checkMode, checkLow.Celsius(), checkHigh.Celsius(),
|
||||||
|
newMode, newLow.Celsius(), newHigh.Celsius())
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tmp102.SetAlertMode(mode, low, high)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
checkMode, _, _, _ = tmp102.GetAlertMode()
|
||||||
|
if checkMode != mode {
|
||||||
|
t.Errorf("Error resetting mode. Got %d Expected %d", checkMode, mode)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue