hybridgroup.gobot/drivers/i2c/sht2x_driver.go

230 lines
5.8 KiB
Go

/*
* 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.
*/
package i2c
// SHT2xDriver is a driver for the SHT2x based devices.
//
// This module was tested with Sensirion SHT21 Breakout.
import (
"errors"
"time"
"github.com/sigurn/crc8"
)
const sht2xDefaultAddress = 0x40
const (
// SHT2xAccuracyLow is the faster, but lower accuracy sample setting
// 0/1 = 8bit RH, 12bit Temp
SHT2xAccuracyLow = byte(0x01)
// SHT2xAccuracyMedium is the medium accuracy and speed sample setting
// 1/0 = 10bit RH, 13bit Temp
SHT2xAccuracyMedium = byte(0x80)
// SHT2xAccuracyHigh is the high accuracy and slowest sample setting
// 0/0 = 12bit RH, 14bit Temp
// Power on default is 0/0
SHT2xAccuracyHigh = byte(0x00)
// SHT2xTriggerTempMeasureHold is the command for measuring temperature in hold controller mode
SHT2xTriggerTempMeasureHold = 0xe3
// SHT2xTriggerHumdMeasureHold is the command for measuring humidity in hold controller mode
SHT2xTriggerHumdMeasureHold = 0xe5
// SHT2xTriggerTempMeasureNohold is the command for measuring humidity in no hold controller mode
SHT2xTriggerTempMeasureNohold = 0xf3
// SHT2xTriggerHumdMeasureNohold is the command for measuring humidity in no hold controller mode
SHT2xTriggerHumdMeasureNohold = 0xf5
// SHT2xWriteUserReg is the command for writing user register
SHT2xWriteUserReg = 0xe6
// SHT2xReadUserReg is the command for reading user register
SHT2xReadUserReg = 0xe7
// SHT2xReadUserReg is the command for reading user register
SHT2xSoftReset = 0xfe
)
// SHT2xDriver is a Driver for a SHT2x humidity and temperature sensor
type SHT2xDriver struct {
*Driver
Units string
accuracy byte
crcTable *crc8.Table
}
// NewSHT2xDriver creates a new driver with specified i2c interface
// Params:
//
// 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 NewSHT2xDriver(c Connector, options ...func(Config)) *SHT2xDriver {
// From the document "CRC Checksum Calculation -- For Safe Communication with SHT2x Sensors":
crc8Params := crc8.Params{
Poly: 0x31,
Init: 0x00,
RefIn: false,
RefOut: false,
XorOut: 0x00,
Check: 0x00,
Name: "CRC-8/SENSIRION-SHT2x",
}
d := &SHT2xDriver{
Driver: NewDriver(c, "SHT2x", sht2xDefaultAddress),
Units: "C",
crcTable: crc8.MakeTable(crc8Params),
}
d.afterStart = d.initialize
for _, option := range options {
option(d)
}
return d
}
func (d *SHT2xDriver) Accuracy() byte { return d.accuracy }
// SetAccuracy sets the accuracy of the sampling
func (d *SHT2xDriver) SetAccuracy(acc byte) error {
d.accuracy = acc
if d.connection != nil {
return d.sendAccuracy()
}
return nil
}
// Reset does a software reset of the device
func (d *SHT2xDriver) Reset() error {
if err := d.connection.WriteByte(SHT2xSoftReset); err != nil {
return err
}
time.Sleep(15 * time.Millisecond) // 15ms delay (from the datasheet 5.5)
return nil
}
// Temperature returns the current temperature, in celsius degrees.
func (d *SHT2xDriver) Temperature() (float32, error) {
rawT, err := d.readSensor(SHT2xTriggerTempMeasureNohold)
if err != nil {
return 0, err
}
// From the datasheet 6.2:
// T[C] = -46.85 + 175.72 * St / 2^16
temp := -46.85 + 175.72/65536.0*float32(rawT)
return temp, nil
}
// Humidity returns the current humidity in percentage of relative humidity
func (d *SHT2xDriver) Humidity() (float32, error) {
rawH, err := d.readSensor(SHT2xTriggerHumdMeasureNohold)
if err != nil {
return 0, err
}
// From the datasheet 6.1:
// RH = -6 + 125 * Srh / 2^16
humidity := -6.0 + 125.0/65536.0*float32(rawH)
return humidity, nil
}
// sendCommandDelayGetResponse is a helper function to reduce duplicated code
func (d *SHT2xDriver) readSensor(cmd byte) (uint16, error) {
if err := d.connection.WriteByte(cmd); err != nil {
return 0, err
}
// Hang out while measurement is taken. 85ms max, page 9 of datasheet.
time.Sleep(85 * time.Millisecond)
// Comes back in three bytes, data(MSB) / data(LSB) / Checksum
buf := make([]byte, 3)
counter := 0
for {
got, err := d.connection.Read(buf)
counter++
if counter > 50 {
return 0, err
}
if err == nil {
if got != 3 {
return 0, ErrNotEnoughBytes
}
break
}
time.Sleep(1 * time.Millisecond)
}
// Store the result
crc := crc8.Checksum(buf[0:2], d.crcTable)
if buf[2] != crc {
return 0, errors.New("Invalid crc")
}
read := uint16(buf[0])<<8 | uint16(buf[1])
read &= 0xfffc // clear two low bits (status bits)
return read, nil
}
func (d *SHT2xDriver) initialize() error {
if err := d.Reset(); err != nil {
return err
}
return d.sendAccuracy()
}
func (d *SHT2xDriver) sendAccuracy() error {
if err := d.connection.WriteByte(SHT2xReadUserReg); err != nil {
return err
}
userRegister, err := d.connection.ReadByte()
if err != nil {
return err
}
userRegister &= 0x7e // Turn off the resolution bits
acc := d.accuracy
acc &= 0x81 // Turn off all other bits but resolution bits
userRegister |= acc // Mask in the requested resolution bits
// Request a write to user register
if _, err := d.connection.Write([]byte{SHT2xWriteUserReg, userRegister}); err != nil {
return err
}
_, err = d.connection.ReadByte()
return err
}