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 }