gobot.io/x/gobot/v2@v2.1.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  	"log"
    28  	"time"
    29  )
    30  
    31  const (
    32  	th02Debug          = false
    33  	th02DefaultAddress = 0x40
    34  )
    35  
    36  const (
    37  	th02Reg_Status  = 0x00
    38  	th02Reg_DataMSB = 0x01
    39  	th02Reg_DataLSB = 0x02
    40  	th02Reg_Config  = 0x03
    41  	th02Reg_ID      = 0x11
    42  
    43  	th02Status_ReadyBit = 0x01 // D0 is /RDY
    44  
    45  	th02Config_StartBit = 0x01 // D0 is START
    46  	th02Config_HeatBit  = 0x02 // D1 is HEAT
    47  	th02Config_TempBit  = 0x10 // D4 is TEMP (if not set read humidity)
    48  	th02Config_FastBit  = 0x20 // D5 is FAST (if set use 18 ms, but lower accuracy T: 13 bit, H: 11 bit)
    49  )
    50  
    51  //Accuracy constants for the TH02 devices (deprecated, use WithFastMode() instead)
    52  const (
    53  	TH02HighAccuracy = 0 //High Accuracy (T: 14 bit, H: 12 bit), normal (35 ms)
    54  	TH02LowAccuracy  = 1 //Lower Accuracy (T: 13 bit, H: 11 bit), fast (18 ms)
    55  )
    56  
    57  // TH02Driver is a Driver for a TH02 humidity and temperature sensor
    58  type TH02Driver struct {
    59  	*Driver
    60  	Units    string
    61  	heating  bool
    62  	fastMode bool
    63  }
    64  
    65  // NewTH02Driver creates a new driver with specified i2c interface.
    66  // Defaults to:
    67  //	- Using high accuracy (lower speed) measurements cycles.
    68  //  - Emitting values in "C". If you want F, set Units to "F"
    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 NewTH02Driver(a Connector, options ...func(Config)) *TH02Driver {
    77  	s := &TH02Driver{
    78  		Driver:   NewDriver(a, "TH02", th02DefaultAddress, options...),
    79  		Units:    "C",
    80  		heating:  false,
    81  		fastMode: false,
    82  	}
    83  
    84  	for _, option := range options {
    85  		option(s)
    86  	}
    87  
    88  	return s
    89  }
    90  
    91  // WithTH02FastMode option sets the fast mode (leads to lower accuracy).
    92  // Valid settings are <=0 (off), >0 (on).
    93  func WithTH02FastMode(val int) func(Config) {
    94  	return func(c Config) {
    95  		d, ok := c.(*TH02Driver)
    96  		if ok {
    97  			d.fastMode = (val > 0)
    98  		} else if th02Debug {
    99  			log.Printf("Trying to set fast mode for non-TH02Driver %v", c)
   100  		}
   101  	}
   102  }
   103  
   104  // Accuracy returns the accuracy of the sampling (deprecated, use FastMode() instead)
   105  func (s *TH02Driver) Accuracy() byte {
   106  	if s.fastMode {
   107  		return TH02LowAccuracy
   108  	}
   109  	return TH02HighAccuracy
   110  }
   111  
   112  // SetAccuracy sets the accuracy of the sampling. (deprecated, use WithFastMode() instead)
   113  // It will only be used on the next measurement request.  Invalid value will use the default of High
   114  func (s *TH02Driver) SetAccuracy(a byte) {
   115  	s.fastMode = (a == TH02LowAccuracy)
   116  }
   117  
   118  // SerialNumber returns the serial number of the chip
   119  func (s *TH02Driver) SerialNumber() (uint8, error) {
   120  	s.mutex.Lock()
   121  	defer s.mutex.Unlock()
   122  
   123  	ret, err := s.connection.ReadByteData(th02Reg_ID)
   124  	return uint8(ret) >> 4, err
   125  }
   126  
   127  // FastMode returns true if the fast mode is enabled in the device
   128  func (s *TH02Driver) FastMode() (bool, error) {
   129  	s.mutex.Lock()
   130  	defer s.mutex.Unlock()
   131  
   132  	cfg, err := s.connection.ReadByteData(th02Reg_Config)
   133  	return (th02Config_FastBit & cfg) == th02Config_FastBit, err
   134  }
   135  
   136  // SetHeater sets the heater of the device to the given state.
   137  func (s *TH02Driver) SetHeater(state bool) error {
   138  	s.mutex.Lock()
   139  	defer s.mutex.Unlock()
   140  
   141  	s.heating = state
   142  	return s.connection.WriteByteData(th02Reg_Config, s.createConfig(false, false))
   143  }
   144  
   145  // Heater returns true if the heater is enabled in the device
   146  func (s *TH02Driver) Heater() (bool, error) {
   147  	s.mutex.Lock()
   148  	defer s.mutex.Unlock()
   149  
   150  	cfg, err := s.connection.ReadByteData(th02Reg_Config)
   151  	return (th02Config_HeatBit & cfg) == th02Config_HeatBit, err
   152  }
   153  
   154  // Sample returns the temperature in celsius and relative humidity for one sample
   155  func (s *TH02Driver) Sample() (temperature float32, relhumidity float32, _ error) {
   156  	s.mutex.Lock()
   157  	defer s.mutex.Unlock()
   158  
   159  	// read humidity
   160  	if err := s.connection.WriteByteData(th02Reg_Config, s.createConfig(true, false)); err != nil {
   161  		return 0, 0, err
   162  	}
   163  
   164  	rawrh, err := s.waitAndReadData()
   165  	if err != nil {
   166  		return 0, 0, err
   167  	}
   168  	relhumidity = float32(rawrh>>4)/16.0 - 24.0
   169  
   170  	// read temperature
   171  	if err := s.connection.WriteByteData(th02Reg_Config, s.createConfig(true, true)); err != nil {
   172  		return 0, relhumidity, err
   173  	}
   174  	rawt, err := s.waitAndReadData()
   175  	if err != nil {
   176  		return 0, relhumidity, err
   177  	}
   178  	temperature = float32(rawt>>2)/32.0 - 50.0
   179  
   180  	switch s.Units {
   181  	case "F":
   182  		temperature = 9.0/5.0*temperature + 32.0
   183  	}
   184  
   185  	return temperature, relhumidity, nil
   186  
   187  }
   188  
   189  func (s *TH02Driver) createConfig(measurement bool, readTemp bool) byte {
   190  	cfg := byte(0x00)
   191  	if measurement {
   192  		cfg = cfg | th02Config_StartBit
   193  		if readTemp {
   194  			cfg = cfg | th02Config_TempBit
   195  		}
   196  		if s.fastMode {
   197  			cfg = cfg | th02Config_FastBit
   198  		}
   199  	}
   200  	if s.heating {
   201  		cfg = cfg | th02Config_HeatBit
   202  	}
   203  	return cfg
   204  }
   205  
   206  func (s *TH02Driver) waitAndReadData() (uint16, error) {
   207  	if err := s.waitForReady(nil); err != nil {
   208  		return 0, err
   209  	}
   210  
   211  	rcvd := make([]byte, 2)
   212  	err := s.connection.ReadBlockData(th02Reg_DataMSB, rcvd)
   213  	if err != nil {
   214  		return 0, err
   215  	}
   216  	return uint16(rcvd[0])<<8 + uint16(rcvd[1]), nil
   217  }
   218  
   219  // waitForReady blocks for up to the passed duration (which defaults to 50mS if nil)
   220  // until the ~RDY bit is cleared, meaning a sample has been fully sampled and is ready for reading.
   221  func (s *TH02Driver) waitForReady(dur *time.Duration) error {
   222  	wait := 100 * time.Millisecond
   223  	if dur != nil {
   224  		wait = *dur
   225  	}
   226  	start := time.Now()
   227  	for {
   228  		if time.Since(start) > wait {
   229  			return fmt.Errorf("timeout on \\RDY")
   230  		}
   231  
   232  		if reg, err := s.connection.ReadByteData(th02Reg_Status); (reg == 0) && (err == nil) {
   233  			return nil
   234  		}
   235  		time.Sleep(wait / 10)
   236  	}
   237  }