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 }