gobot.io/x/gobot@v1.16.0/drivers/i2c/sht3x_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  // SHT3xDriver is a driver for the SHT3x-D based devices.
    20  //
    21  // This module was tested with AdaFruit Sensiron SHT32-D Breakout.
    22  // https://www.adafruit.com/products/2857
    23  
    24  import (
    25  	"errors"
    26  	"time"
    27  
    28  	"github.com/sigurn/crc8"
    29  	"gobot.io/x/gobot"
    30  )
    31  
    32  // SHT3xAddressA is the default address of device
    33  const SHT3xAddressA = 0x44
    34  
    35  // SHT3xAddressB is the optional address of device
    36  const SHT3xAddressB = 0x45
    37  
    38  // SHT3xAccuracyLow is the faster, but lower accuracy sample setting
    39  const SHT3xAccuracyLow = 0x16
    40  
    41  // SHT3xAccuracyMedium is the medium accuracy and speed sample setting
    42  const SHT3xAccuracyMedium = 0x0b
    43  
    44  // SHT3xAccuracyHigh is the high accuracy and slowest sample setting
    45  const SHT3xAccuracyHigh = 0x00
    46  
    47  var (
    48  	crc8Params         = crc8.Params{0x31, 0xff, false, false, 0x00, 0xf7, "CRC-8/SENSIRON"}
    49  	ErrInvalidAccuracy = errors.New("Invalid accuracy")
    50  	ErrInvalidCrc      = errors.New("Invalid crc")
    51  	ErrInvalidTemp     = errors.New("Invalid temperature units")
    52  )
    53  
    54  // SHT3xDriver is a Driver for a SHT3x humidity and temperature sensor
    55  type SHT3xDriver struct {
    56  	Units string
    57  
    58  	name       string
    59  	connector  Connector
    60  	connection Connection
    61  	Config
    62  	sht3xAddress int
    63  	accuracy     byte
    64  	delay        time.Duration
    65  	crcTable     *crc8.Table
    66  }
    67  
    68  // NewSHT3xDriver creates a new driver with specified i2c interface
    69  // Params:
    70  //		conn Connector - the Adaptor to use with this Driver
    71  //
    72  // Optional params:
    73  //		i2c.WithBus(int):	bus to use with this driver
    74  //		i2c.WithAddress(int):	address to use with this driver
    75  //
    76  func NewSHT3xDriver(a Connector, options ...func(Config)) *SHT3xDriver {
    77  	s := &SHT3xDriver{
    78  		Units:        "C",
    79  		name:         gobot.DefaultName("SHT3x"),
    80  		connector:    a,
    81  		Config:       NewConfig(),
    82  		sht3xAddress: SHT3xAddressA,
    83  		crcTable:     crc8.MakeTable(crc8Params),
    84  	}
    85  	s.SetAccuracy(SHT3xAccuracyHigh)
    86  
    87  	for _, option := range options {
    88  		option(s)
    89  	}
    90  
    91  	return s
    92  }
    93  
    94  // Name returns the name for this Driver
    95  func (s *SHT3xDriver) Name() string { return s.name }
    96  
    97  // SetName sets the name for this Driver
    98  func (s *SHT3xDriver) SetName(n string) { s.name = n }
    99  
   100  // Connection returns the connection for this Driver
   101  func (s *SHT3xDriver) Connection() gobot.Connection { return s.connector.(gobot.Connection) }
   102  
   103  // Start initializes the SHT3x
   104  func (s *SHT3xDriver) Start() (err error) {
   105  	bus := s.GetBusOrDefault(s.connector.GetDefaultBus())
   106  	address := s.GetAddressOrDefault(s.sht3xAddress)
   107  
   108  	s.connection, err = s.connector.GetConnection(address, bus)
   109  	return
   110  }
   111  
   112  // Halt returns true if devices is halted successfully
   113  func (s *SHT3xDriver) Halt() (err error) { return }
   114  
   115  // SetAddress sets the address of the device
   116  func (s *SHT3xDriver) SetAddress(address int) { s.sht3xAddress = address }
   117  
   118  // Accuracy returns the accuracy of the sampling
   119  func (s *SHT3xDriver) Accuracy() byte { return s.accuracy }
   120  
   121  // SetAccuracy sets the accuracy of the sampling
   122  func (s *SHT3xDriver) SetAccuracy(a byte) (err error) {
   123  	switch a {
   124  	case SHT3xAccuracyLow:
   125  		s.delay = 5 * time.Millisecond // Actual max is 4, wait 1 ms longer
   126  	case SHT3xAccuracyMedium:
   127  		s.delay = 7 * time.Millisecond // Actual max is 6, wait 1 ms longer
   128  	case SHT3xAccuracyHigh:
   129  		s.delay = 16 * time.Millisecond // Actual max is 15, wait 1 ms longer
   130  	default:
   131  		err = ErrInvalidAccuracy
   132  		return
   133  	}
   134  
   135  	s.accuracy = a
   136  
   137  	return
   138  }
   139  
   140  // SerialNumber returns the serial number of the chip
   141  func (s *SHT3xDriver) SerialNumber() (sn uint32, err error) {
   142  	ret, err := s.sendCommandDelayGetResponse([]byte{0x37, 0x80}, nil, 2)
   143  	if nil == err {
   144  		sn = (uint32(ret[0]) << 16) | uint32(ret[1])
   145  	}
   146  
   147  	return
   148  }
   149  
   150  // Heater returns true if the heater is enabled
   151  func (s *SHT3xDriver) Heater() (status bool, err error) {
   152  	sr, err := s.getStatusRegister()
   153  	if err == nil {
   154  		if (1 << 13) == (sr & (1 << 13)) {
   155  			status = true
   156  		}
   157  	}
   158  	return
   159  }
   160  
   161  // SetHeater enables or disables the heater on the device
   162  func (s *SHT3xDriver) SetHeater(enabled bool) (err error) {
   163  	out := []byte{0x30, 0x66}
   164  	if true == enabled {
   165  		out[1] = 0x6d
   166  	}
   167  	_, err = s.connection.Write(out)
   168  	return
   169  }
   170  
   171  // Sample returns the temperature in celsius and relative humidity for one sample
   172  func (s *SHT3xDriver) Sample() (temp float32, rh float32, err error) {
   173  	ret, err := s.sendCommandDelayGetResponse([]byte{0x24, s.accuracy}, &s.delay, 2)
   174  	if nil != err {
   175  		return
   176  	}
   177  
   178  	// From the datasheet:
   179  	// RH = 100 * Srh / (2^16 - 1)
   180  	rhSample := uint64(ret[1])
   181  	rh = float32((uint64(1000000)*rhSample)/uint64(0xffff)) / 10000.0
   182  
   183  	tempSample := uint64(ret[0])
   184  	switch s.Units {
   185  	case "C":
   186  		// From the datasheet:
   187  		// T[C] = -45 + 175 * (St / (2^16 - 1))
   188  		temp = float32((uint64(1750000)*tempSample)/uint64(0xffff)-uint64(450000)) / 10000.0
   189  	case "F":
   190  		// From the datasheet:
   191  		// T[F] = -49 + 315 * (St / (2^16 - 1))
   192  		temp = float32((uint64(3150000)*tempSample)/uint64(0xffff)-uint64(490000)) / 10000.0
   193  	default:
   194  		err = ErrInvalidTemp
   195  	}
   196  
   197  	return
   198  }
   199  
   200  // getStatusRegister returns the device status register
   201  func (s *SHT3xDriver) getStatusRegister() (status uint16, err error) {
   202  	ret, err := s.sendCommandDelayGetResponse([]byte{0xf3, 0x2d}, nil, 1)
   203  	if nil == err {
   204  		status = ret[0]
   205  	}
   206  	return
   207  }
   208  
   209  // sendCommandDelayGetResponse is a helper function to reduce duplicated code
   210  func (s *SHT3xDriver) sendCommandDelayGetResponse(send []byte, delay *time.Duration, expect int) (read []uint16, err error) {
   211  	if _, err = s.connection.Write(send); err != nil {
   212  		return
   213  	}
   214  
   215  	if nil != delay {
   216  		time.Sleep(*delay)
   217  	}
   218  
   219  	buf := make([]byte, 3*expect)
   220  	got, err := s.connection.Read(buf)
   221  	if err != nil {
   222  		return
   223  	}
   224  	if got != (3 * expect) {
   225  		err = ErrNotEnoughBytes
   226  		return
   227  	}
   228  
   229  	read = make([]uint16, expect)
   230  	for i := 0; i < expect; i++ {
   231  		crc := crc8.Checksum(buf[i*3:i*3+2], s.crcTable)
   232  		if buf[i*3+2] != crc {
   233  			err = ErrInvalidCrc
   234  			return
   235  		}
   236  		read[i] = uint16(buf[i*3])<<8 | uint16(buf[i*3+1])
   237  	}
   238  
   239  	return
   240  }