2017-01-03 17:21:09 +08:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2016-2017 Weston Schmidt <weston_schmidt@alumni.purdue.edu>
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
2017-02-06 07:19:42 +08:00
|
|
|
|
2017-01-03 17:21:09 +08:00
|
|
|
package i2c
|
|
|
|
|
|
|
|
// SHT3xDriver is a driver for the SHT3x-D based devices.
|
|
|
|
//
|
|
|
|
// This module was tested with AdaFruit Sensiron SHT32-D Breakout.
|
|
|
|
// https://www.adafruit.com/products/2857
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2017-02-02 22:46:00 +08:00
|
|
|
"time"
|
|
|
|
|
2017-01-03 17:21:09 +08:00
|
|
|
"github.com/sigurn/crc8"
|
|
|
|
)
|
|
|
|
|
|
|
|
// SHT3xAddressA is the default address of device
|
|
|
|
const SHT3xAddressA = 0x44
|
|
|
|
|
|
|
|
// SHT3xAddressB is the optional address of device
|
|
|
|
const SHT3xAddressB = 0x45
|
|
|
|
|
|
|
|
// SHT3xAccuracyLow is the faster, but lower accuracy sample setting
|
|
|
|
const SHT3xAccuracyLow = 0x16
|
|
|
|
|
|
|
|
// SHT3xAccuracyMedium is the medium accuracy and speed sample setting
|
|
|
|
const SHT3xAccuracyMedium = 0x0b
|
|
|
|
|
|
|
|
// SHT3xAccuracyHigh is the high accuracy and slowest sample setting
|
|
|
|
const SHT3xAccuracyHigh = 0x00
|
|
|
|
|
|
|
|
var (
|
2023-05-19 20:16:22 +08:00
|
|
|
crc8Params = crc8.Params{
|
|
|
|
Poly: 0x31, Init: 0xff, RefIn: false, RefOut: false, XorOut: 0x00, Check: 0xf7, Name: "CRC-8/SENSIRON",
|
|
|
|
}
|
2017-01-03 17:21:09 +08:00
|
|
|
ErrInvalidAccuracy = errors.New("Invalid accuracy")
|
|
|
|
ErrInvalidCrc = errors.New("Invalid crc")
|
|
|
|
ErrInvalidTemp = errors.New("Invalid temperature units")
|
|
|
|
)
|
|
|
|
|
|
|
|
// SHT3xDriver is a Driver for a SHT3x humidity and temperature sensor
|
|
|
|
type SHT3xDriver struct {
|
2022-12-10 20:10:23 +08:00
|
|
|
*Driver
|
|
|
|
Units string
|
|
|
|
accuracy byte
|
|
|
|
delay time.Duration
|
|
|
|
crcTable *crc8.Table
|
2017-01-03 17:21:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewSHT3xDriver creates a new driver with specified i2c interface
|
2017-02-09 23:47:11 +08:00
|
|
|
// Params:
|
2023-06-13 01:51:25 +08:00
|
|
|
//
|
|
|
|
// c Connector - the Adaptor to use with this Driver
|
2017-02-09 23:47:11 +08:00
|
|
|
//
|
|
|
|
// Optional params:
|
|
|
|
//
|
2023-06-13 01:51:25 +08:00
|
|
|
// i2c.WithBus(int): bus to use with this driver
|
|
|
|
// i2c.WithAddress(int): address to use with this driver
|
2022-12-10 20:10:23 +08:00
|
|
|
func NewSHT3xDriver(c Connector, options ...func(Config)) *SHT3xDriver {
|
2017-01-03 17:21:09 +08:00
|
|
|
s := &SHT3xDriver{
|
2022-12-10 20:10:23 +08:00
|
|
|
Driver: NewDriver(c, "SHT3x", SHT3xAddressA),
|
|
|
|
Units: "C",
|
|
|
|
crcTable: crc8.MakeTable(crc8Params),
|
2017-01-03 17:21:09 +08:00
|
|
|
}
|
2023-06-13 01:51:25 +08:00
|
|
|
if err := s.SetAccuracy(SHT3xAccuracyHigh); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2017-02-09 18:23:36 +08:00
|
|
|
|
|
|
|
for _, option := range options {
|
|
|
|
option(s)
|
|
|
|
}
|
|
|
|
|
2017-01-03 17:21:09 +08:00
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
|
|
|
// Accuracy returns the accuracy of the sampling
|
|
|
|
func (s *SHT3xDriver) Accuracy() byte { return s.accuracy }
|
|
|
|
|
|
|
|
// SetAccuracy sets the accuracy of the sampling
|
2023-11-16 03:51:52 +08:00
|
|
|
func (s *SHT3xDriver) SetAccuracy(a byte) error {
|
2017-01-03 17:21:09 +08:00
|
|
|
switch a {
|
|
|
|
case SHT3xAccuracyLow:
|
|
|
|
s.delay = 5 * time.Millisecond // Actual max is 4, wait 1 ms longer
|
|
|
|
case SHT3xAccuracyMedium:
|
|
|
|
s.delay = 7 * time.Millisecond // Actual max is 6, wait 1 ms longer
|
|
|
|
case SHT3xAccuracyHigh:
|
|
|
|
s.delay = 16 * time.Millisecond // Actual max is 15, wait 1 ms longer
|
|
|
|
default:
|
2023-11-16 03:51:52 +08:00
|
|
|
return ErrInvalidAccuracy
|
2017-01-03 17:21:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
s.accuracy = a
|
|
|
|
|
2023-11-16 03:51:52 +08:00
|
|
|
return nil
|
2017-01-03 17:21:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// SerialNumber returns the serial number of the chip
|
2023-11-16 03:51:52 +08:00
|
|
|
func (s *SHT3xDriver) SerialNumber() (uint32, error) {
|
2017-01-03 17:21:09 +08:00
|
|
|
ret, err := s.sendCommandDelayGetResponse([]byte{0x37, 0x80}, nil, 2)
|
2023-11-16 03:51:52 +08:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
2017-01-03 17:21:09 +08:00
|
|
|
}
|
|
|
|
|
2023-11-16 03:51:52 +08:00
|
|
|
sn := (uint32(ret[0]) << 16) | uint32(ret[1])
|
|
|
|
|
|
|
|
return sn, nil
|
2017-01-03 17:21:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Heater returns true if the heater is enabled
|
2023-11-16 03:51:52 +08:00
|
|
|
func (s *SHT3xDriver) Heater() (bool, error) {
|
2017-01-03 17:21:09 +08:00
|
|
|
sr, err := s.getStatusRegister()
|
2023-11-16 03:51:52 +08:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
2017-01-03 17:21:09 +08:00
|
|
|
}
|
2023-11-16 03:51:52 +08:00
|
|
|
|
|
|
|
if (1 << 13) == (sr & (1 << 13)) {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return false, nil
|
2017-01-03 17:21:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// SetHeater enables or disables the heater on the device
|
2023-11-16 03:51:52 +08:00
|
|
|
func (s *SHT3xDriver) SetHeater(enabled bool) error {
|
2017-01-03 17:21:09 +08:00
|
|
|
out := []byte{0x30, 0x66}
|
2023-05-19 20:16:22 +08:00
|
|
|
if enabled {
|
2017-01-03 17:21:09 +08:00
|
|
|
out[1] = 0x6d
|
|
|
|
}
|
2023-11-16 03:51:52 +08:00
|
|
|
_, err := s.connection.Write(out)
|
|
|
|
return err
|
2017-01-03 17:21:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Sample returns the temperature in celsius and relative humidity for one sample
|
2023-11-16 03:51:52 +08:00
|
|
|
//
|
|
|
|
//nolint:nonamedreturns // is sufficient here
|
2017-01-03 17:21:09 +08:00
|
|
|
func (s *SHT3xDriver) Sample() (temp float32, rh float32, err error) {
|
|
|
|
ret, err := s.sendCommandDelayGetResponse([]byte{0x24, s.accuracy}, &s.delay, 2)
|
|
|
|
if nil != err {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// From the datasheet:
|
|
|
|
// RH = 100 * Srh / (2^16 - 1)
|
|
|
|
rhSample := uint64(ret[1])
|
|
|
|
rh = float32((uint64(1000000)*rhSample)/uint64(0xffff)) / 10000.0
|
|
|
|
|
|
|
|
tempSample := uint64(ret[0])
|
|
|
|
switch s.Units {
|
|
|
|
case "C":
|
|
|
|
// From the datasheet:
|
|
|
|
// T[C] = -45 + 175 * (St / (2^16 - 1))
|
|
|
|
temp = float32((uint64(1750000)*tempSample)/uint64(0xffff)-uint64(450000)) / 10000.0
|
|
|
|
case "F":
|
|
|
|
// From the datasheet:
|
|
|
|
// T[F] = -49 + 315 * (St / (2^16 - 1))
|
|
|
|
temp = float32((uint64(3150000)*tempSample)/uint64(0xffff)-uint64(490000)) / 10000.0
|
|
|
|
default:
|
|
|
|
err = ErrInvalidTemp
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// getStatusRegister returns the device status register
|
2023-11-16 03:51:52 +08:00
|
|
|
func (s *SHT3xDriver) getStatusRegister() (uint16, error) {
|
2017-01-03 17:21:09 +08:00
|
|
|
ret, err := s.sendCommandDelayGetResponse([]byte{0xf3, 0x2d}, nil, 1)
|
2023-11-16 03:51:52 +08:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
2017-01-03 17:21:09 +08:00
|
|
|
}
|
2023-11-16 03:51:52 +08:00
|
|
|
|
|
|
|
return ret[0], nil
|
2017-01-03 17:21:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// sendCommandDelayGetResponse is a helper function to reduce duplicated code
|
2023-11-16 03:51:52 +08:00
|
|
|
func (s *SHT3xDriver) sendCommandDelayGetResponse(send []byte, delay *time.Duration, expect int) ([]uint16, error) {
|
|
|
|
if _, err := s.connection.Write(send); err != nil {
|
|
|
|
return nil, err
|
2017-01-03 17:21:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if nil != delay {
|
|
|
|
time.Sleep(*delay)
|
|
|
|
}
|
|
|
|
|
2017-02-06 07:19:42 +08:00
|
|
|
buf := make([]byte, 3*expect)
|
|
|
|
got, err := s.connection.Read(buf)
|
2017-01-03 17:21:09 +08:00
|
|
|
if err != nil {
|
2023-11-16 03:51:52 +08:00
|
|
|
return nil, err
|
2017-01-03 17:21:09 +08:00
|
|
|
}
|
2017-02-06 07:19:42 +08:00
|
|
|
if got != (3 * expect) {
|
2023-11-16 03:51:52 +08:00
|
|
|
return nil, ErrNotEnoughBytes
|
2017-01-03 17:21:09 +08:00
|
|
|
}
|
|
|
|
|
2023-11-16 03:51:52 +08:00
|
|
|
read := make([]uint16, expect)
|
2017-01-03 17:21:09 +08:00
|
|
|
for i := 0; i < expect; i++ {
|
2017-02-06 07:19:42 +08:00
|
|
|
crc := crc8.Checksum(buf[i*3:i*3+2], s.crcTable)
|
|
|
|
if buf[i*3+2] != crc {
|
2023-11-16 03:51:52 +08:00
|
|
|
return nil, ErrInvalidCrc
|
2017-01-03 17:21:09 +08:00
|
|
|
}
|
2017-02-06 07:19:42 +08:00
|
|
|
read[i] = uint16(buf[i*3])<<8 | uint16(buf[i*3+1])
|
2017-01-03 17:21:09 +08:00
|
|
|
}
|
|
|
|
|
2023-11-16 03:51:52 +08:00
|
|
|
return read, nil
|
2017-01-03 17:21:09 +08:00
|
|
|
}
|