gobot.io/x/gobot/v2@v2.1.0/drivers/i2c/sht2x_driver.go (about)

     1  /*
     2   * Copyright (c) 2016-2017 Weston Schmidt <weston_schmidt@alumni.purdue.edu>
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   * http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package i2c
    18  
    19  // SHT2xDriver is a driver for the SHT2x based devices.
    20  //
    21  // This module was tested with Sensirion SHT21 Breakout.
    22  
    23  import (
    24  	"errors"
    25  	"time"
    26  
    27  	"github.com/sigurn/crc8"
    28  )
    29  
    30  const sht2xDefaultAddress = 0x40
    31  
    32  const (
    33  	// SHT2xAccuracyLow is the faster, but lower accuracy sample setting
    34  	//  0/1 = 8bit RH, 12bit Temp
    35  	SHT2xAccuracyLow = byte(0x01)
    36  
    37  	// SHT2xAccuracyMedium is the medium accuracy and speed sample setting
    38  	//  1/0 = 10bit RH, 13bit Temp
    39  	SHT2xAccuracyMedium = byte(0x80)
    40  
    41  	// SHT2xAccuracyHigh is the high accuracy and slowest sample setting
    42  	//  0/0 = 12bit RH, 14bit Temp
    43  	//  Power on default is 0/0
    44  	SHT2xAccuracyHigh = byte(0x00)
    45  
    46  	// SHT2xTriggerTempMeasureHold is the command for measureing temperature in hold master mode
    47  	SHT2xTriggerTempMeasureHold = 0xe3
    48  
    49  	// SHT2xTriggerHumdMeasureHold is the command for measureing humidity in hold master mode
    50  	SHT2xTriggerHumdMeasureHold = 0xe5
    51  
    52  	// SHT2xTriggerTempMeasureNohold is the command for measureing humidity in no hold master mode
    53  	SHT2xTriggerTempMeasureNohold = 0xf3
    54  
    55  	// SHT2xTriggerHumdMeasureNohold is the command for measureing humidity in no hold master mode
    56  	SHT2xTriggerHumdMeasureNohold = 0xf5
    57  
    58  	// SHT2xWriteUserReg is the command for writing user register
    59  	SHT2xWriteUserReg = 0xe6
    60  
    61  	// SHT2xReadUserReg is the command for reading user register
    62  	SHT2xReadUserReg = 0xe7
    63  
    64  	// SHT2xReadUserReg is the command for reading user register
    65  	SHT2xSoftReset = 0xfe
    66  )
    67  
    68  // SHT2xDriver is a Driver for a SHT2x humidity and temperature sensor
    69  type SHT2xDriver struct {
    70  	*Driver
    71  	Units        string
    72  	sht2xAddress int
    73  	accuracy     byte
    74  	delay        time.Duration
    75  	crcTable     *crc8.Table
    76  }
    77  
    78  // NewSHT2xDriver creates a new driver with specified i2c interface
    79  // Params:
    80  //		c Connector - the Adaptor to use with this Driver
    81  //
    82  // Optional params:
    83  //		i2c.WithBus(int):	bus to use with this driver
    84  //		i2c.WithAddress(int):	address to use with this driver
    85  //
    86  func NewSHT2xDriver(c Connector, options ...func(Config)) *SHT2xDriver {
    87  	// From the document "CRC Checksum Calculation -- For Safe Communication with SHT2x Sensors":
    88  	crc8Params := crc8.Params{
    89  		Poly:   0x31,
    90  		Init:   0x00,
    91  		RefIn:  false,
    92  		RefOut: false,
    93  		XorOut: 0x00,
    94  		Check:  0x00,
    95  		Name:   "CRC-8/SENSIRION-SHT2x",
    96  	}
    97  	d := &SHT2xDriver{
    98  		Driver:   NewDriver(c, "SHT2x", sht2xDefaultAddress),
    99  		Units:    "C",
   100  		crcTable: crc8.MakeTable(crc8Params),
   101  	}
   102  	d.afterStart = d.initialize
   103  
   104  	for _, option := range options {
   105  		option(d)
   106  	}
   107  
   108  	return d
   109  }
   110  
   111  func (d *SHT2xDriver) Accuracy() byte { return d.accuracy }
   112  
   113  // SetAccuracy sets the accuracy of the sampling
   114  func (d *SHT2xDriver) SetAccuracy(acc byte) (err error) {
   115  	d.accuracy = acc
   116  
   117  	if d.connection != nil {
   118  		err = d.sendAccuracy()
   119  	}
   120  
   121  	return
   122  }
   123  
   124  // Reset does a software reset of the device
   125  func (d *SHT2xDriver) Reset() (err error) {
   126  	if err = d.connection.WriteByte(SHT2xSoftReset); err != nil {
   127  		return
   128  	}
   129  
   130  	time.Sleep(15 * time.Millisecond) // 15ms delay (from the datasheet 5.5)
   131  
   132  	return
   133  }
   134  
   135  // Temperature returns the current temperature, in celsius degrees.
   136  func (d *SHT2xDriver) Temperature() (temp float32, err error) {
   137  	var rawT uint16
   138  	if rawT, err = d.readSensor(SHT2xTriggerTempMeasureNohold); err != nil {
   139  		return
   140  	}
   141  
   142  	// From the datasheet 6.2:
   143  	// T[C] = -46.85 + 175.72 * St / 2^16
   144  	temp = -46.85 + 175.72/65536.0*float32(rawT)
   145  
   146  	return
   147  }
   148  
   149  // Humidity returns the current humidity in percentage of relative humidity
   150  func (d *SHT2xDriver) Humidity() (humidity float32, err error) {
   151  	var rawH uint16
   152  	if rawH, err = d.readSensor(SHT2xTriggerHumdMeasureNohold); err != nil {
   153  		return
   154  	}
   155  
   156  	// From the datasheet 6.1:
   157  	// RH = -6 + 125 * Srh / 2^16
   158  	humidity = -6.0 + 125.0/65536.0*float32(rawH)
   159  
   160  	return
   161  }
   162  
   163  // sendCommandDelayGetResponse is a helper function to reduce duplicated code
   164  func (d *SHT2xDriver) readSensor(cmd byte) (read uint16, err error) {
   165  	if err = d.connection.WriteByte(cmd); err != nil {
   166  		return
   167  	}
   168  
   169  	//Hang out while measurement is taken. 85ms max, page 9 of datasheet.
   170  	time.Sleep(85 * time.Millisecond)
   171  
   172  	//Comes back in three bytes, data(MSB) / data(LSB) / Checksum
   173  	buf := make([]byte, 3)
   174  	counter := 0
   175  	for {
   176  		var got int
   177  		got, err = d.connection.Read(buf)
   178  		counter++
   179  		if counter > 50 {
   180  			return
   181  		}
   182  		if err == nil {
   183  			if got != 3 {
   184  				err = ErrNotEnoughBytes
   185  				return
   186  			}
   187  			break
   188  		}
   189  		time.Sleep(1 * time.Millisecond)
   190  	}
   191  
   192  	//Store the result
   193  	crc := crc8.Checksum(buf[0:2], d.crcTable)
   194  	if buf[2] != crc {
   195  		err = errors.New("Invalid crc")
   196  		return
   197  	}
   198  	read = uint16(buf[0])<<8 | uint16(buf[1])
   199  	read &= 0xfffc // clear two low bits (status bits)
   200  
   201  	return
   202  }
   203  
   204  func (d *SHT2xDriver) initialize() error {
   205  	if err := d.Reset(); err != nil {
   206  		return err
   207  	}
   208  
   209  	d.sendAccuracy()
   210  
   211  	return nil
   212  }
   213  
   214  func (d *SHT2xDriver) sendAccuracy() error {
   215  	if err := d.connection.WriteByte(SHT2xReadUserReg); err != nil {
   216  		return err
   217  	}
   218  	userRegister, err := d.connection.ReadByte()
   219  	if err != nil {
   220  		return err
   221  	}
   222  
   223  	userRegister &= 0x7e //Turn off the resolution bits
   224  	acc := d.accuracy
   225  	acc &= 0x81         //Turn off all other bits but resolution bits
   226  	userRegister |= acc //Mask in the requested resolution bits
   227  
   228  	//Request a write to user register
   229  	if _, err := d.connection.Write([]byte{SHT2xWriteUserReg, userRegister}); err != nil {
   230  		return err
   231  	}
   232  
   233  	if _, err := d.connection.ReadByte(); err != nil {
   234  		return err
   235  	}
   236  
   237  	return nil
   238  }