gobot.io/x/gobot@v1.16.0/drivers/i2c/th02_driver.go (about)

     1  /*
     2   * Copyright (c) 2018 Nick Potts <nick@the-potts.com>
     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  // TH02Driver is a driver for the TH02-D based devices.
    20  //
    21  // This module was tested with a Grove "Temperature&Humidity Sensor (High-Accuracy & Mini ) v1.0"
    22  // from https://www.seeedstudio.com/Grove-Temperature-Humidity-Sensor-High-Accuracy-Min-p-1921.htm
    23  // Datasheet is at http://www.hoperf.com/upload/sensor/TH02_V1.1.pdf
    24  
    25  import (
    26  	"fmt"
    27  	"time"
    28  
    29  	"gobot.io/x/gobot"
    30  )
    31  
    32  const (
    33  
    34  	// TH02Address is the default address of device
    35  	TH02Address = 0x40
    36  
    37  	//TH02ConfigReg is the configuration register
    38  	TH02ConfigReg = 0x03
    39  )
    40  
    41  //Accuracy constants for the TH02 devices
    42  const (
    43  	TH02HighAccuracy = 0 //High Accuracy
    44  	TH02LowAccuracy  = 1 //Lower Accuracy
    45  )
    46  
    47  // TH02Driver is a Driver for a TH02 humidity and temperature sensor
    48  type TH02Driver struct {
    49  	Units      string
    50  	name       string
    51  	connector  Connector
    52  	connection Connection
    53  	Config
    54  	addr     byte
    55  	accuracy byte
    56  	heating  bool
    57  
    58  	delay time.Duration
    59  }
    60  
    61  // NewTH02Driver creates a new driver with specified i2c interface.
    62  // Defaults to:
    63  //	- Using high accuracy (lower speed) measurements cycles.
    64  //  - Emitting values in "C". If you want F, set Units to "F"
    65  // Params:
    66  //		conn Connector - the Adaptor to use with this Driver
    67  //
    68  // Optional params:
    69  //		i2c.WithBus(int):	bus to use with this driver
    70  //		i2c.WithAddress(int):	address to use with this driver
    71  //
    72  func NewTH02Driver(a Connector, options ...func(Config)) *TH02Driver {
    73  	s := &TH02Driver{
    74  		Units:     "C",
    75  		name:      gobot.DefaultName("TH02"),
    76  		connector: a,
    77  		addr:      TH02Address,
    78  		Config:    NewConfig(),
    79  		heating:   false,
    80  	}
    81  
    82  	s.SetAccuracy(1)
    83  
    84  	for _, option := range options {
    85  		option(s)
    86  	}
    87  
    88  	return s
    89  }
    90  
    91  // Name returns the name for this Driver
    92  func (s *TH02Driver) Name() string { return s.name }
    93  
    94  // SetName sets the name for this Driver
    95  func (s *TH02Driver) SetName(n string) { s.name = n }
    96  
    97  // Connection returns the connection for this Driver
    98  func (s *TH02Driver) Connection() gobot.Connection { return s.connector.(gobot.Connection) }
    99  
   100  // Start initializes the TH02
   101  func (s *TH02Driver) Start() (err error) {
   102  	bus := s.GetBusOrDefault(s.connector.GetDefaultBus())
   103  	address := s.GetAddressOrDefault(int(s.addr))
   104  
   105  	s.connection, err = s.connector.GetConnection(address, bus)
   106  	return err
   107  }
   108  
   109  // Halt returns true if devices is halted successfully
   110  func (s *TH02Driver) Halt() (err error) { return }
   111  
   112  // SetAddress sets the address of the device
   113  func (s *TH02Driver) SetAddress(address int) { s.addr = byte(address) }
   114  
   115  // Accuracy returns the accuracy of the sampling
   116  func (s *TH02Driver) Accuracy() byte { return s.accuracy }
   117  
   118  // SetAccuracy sets the accuracy of the sampling.  It will only be used on the next
   119  // measurment request.  Invalid value will use the default of High
   120  func (s *TH02Driver) SetAccuracy(a byte) {
   121  	if a == TH02LowAccuracy {
   122  		s.accuracy = a
   123  	} else {
   124  		s.accuracy = TH02HighAccuracy
   125  	}
   126  }
   127  
   128  // SerialNumber returns the serial number of the chip
   129  func (s *TH02Driver) SerialNumber() (sn uint32, err error) {
   130  	ret, err := s.readRegister(0x11)
   131  	return uint32(ret) >> 4, err
   132  }
   133  
   134  // Heater returns true if the heater is enabled
   135  func (s *TH02Driver) Heater() (status bool, err error) {
   136  	st, err := s.readRegister(0x11)
   137  	return (0x02 & st) == 0x02, err
   138  }
   139  
   140  func (s *TH02Driver) applysettings(base byte) byte {
   141  	if s.accuracy == TH02LowAccuracy {
   142  		base = base & 0xd5
   143  	} else {
   144  		base = base | 0x20
   145  	}
   146  	if s.heating {
   147  		base = base & 0xfd
   148  	} else {
   149  		base = base | 0x02
   150  	}
   151  	base = base | 0x01 //set the "sample" bit
   152  	return base
   153  }
   154  
   155  // Sample returns the temperature in celsius and relative humidity for one sample
   156  func (s *TH02Driver) Sample() (temperature float32, relhumidity float32, _ error) {
   157  
   158  	if err := s.writeRegister(TH02ConfigReg, s.applysettings(0x10)); err != nil {
   159  		return 0, 0, err
   160  	}
   161  
   162  	rawrh, err := s.readData()
   163  	if err != nil {
   164  		return 0, 0, err
   165  	}
   166  	relhumidity = float32(rawrh>>4)/16.0 - 24.0
   167  
   168  	if err := s.writeRegister(TH02ConfigReg, s.applysettings(0x00)); err != nil {
   169  		return 0, relhumidity, err
   170  	}
   171  	rawt, err := s.readData()
   172  	if err != nil {
   173  		return 0, relhumidity, err
   174  	}
   175  	temperature = float32(rawt>>2)/32.0 - 50.0
   176  
   177  	switch s.Units {
   178  	case "F":
   179  		temperature = 9.0/5.0*temperature + 32.0
   180  	}
   181  
   182  	return temperature, relhumidity, nil
   183  
   184  }
   185  
   186  //writeRegister writes the value to the register.
   187  func (s *TH02Driver) writeRegister(reg, value byte) error {
   188  	_, err := s.connection.Write([]byte{reg, value})
   189  	return err
   190  }
   191  
   192  //readRegister returns the value of a single regusterm and a non-nil error on problem
   193  func (s *TH02Driver) readRegister(reg byte) (byte, error) {
   194  	if _, err := s.connection.Write([]byte{reg}); err != nil {
   195  		return 0, err
   196  	}
   197  	rcvd := make([]byte, 1)
   198  	_, err := s.connection.Read(rcvd)
   199  	return rcvd[0], err
   200  }
   201  
   202  /*waitForReady blocks for up to the passed duration (which defaults to 50mS if nil)
   203  until the ~RDY bit is cleared, meanign a sample has been fully sampled and is ready for reading.
   204  
   205  This is greedy.
   206  */
   207  func (s *TH02Driver) waitForReady(dur *time.Duration) error {
   208  	wait := 100 * time.Millisecond
   209  	if dur != nil {
   210  		wait = *dur
   211  	}
   212  	start := time.Now()
   213  	for {
   214  		if time.Since(start) > wait {
   215  			return fmt.Errorf("timeout on \\RDY")
   216  		}
   217  
   218  		//yes, i am eating the error.
   219  		if reg, _ := s.readRegister(0x00); reg == 0 {
   220  			return nil
   221  		}
   222  	}
   223  }
   224  
   225  /*readData fetches the data from the data 'registers'*/
   226  func (s *TH02Driver) readData() (uint16, error) {
   227  	if err := s.waitForReady(nil); err != nil {
   228  		return 0, err
   229  	}
   230  
   231  	if n, err := s.connection.Write([]byte{0x01}); err != nil || n != 1 {
   232  		return 0, fmt.Errorf("n=%d not 1, or err = %v", n, err)
   233  	}
   234  	rcvd := make([]byte, 3)
   235  	n, err := s.connection.Read(rcvd)
   236  	if err != nil || n != 3 {
   237  		return 0, fmt.Errorf("n=%d not 3, or err = %v", n, err)
   238  	}
   239  	return uint16(rcvd[1])<<8 + uint16(rcvd[2]), nil
   240  
   241  }