|
| 1 | +// Package hts221 implements a driver for HTS221, |
| 2 | +// a capacitive digital sensor for relative humidity and temperature. |
| 3 | +// |
| 4 | +// Datasheet: https://www.st.com/resource/en/datasheet/hts221.pdf |
| 5 | +// |
| 6 | +package hts221 |
| 7 | + |
| 8 | +import ( |
| 9 | + "errors" |
| 10 | + |
| 11 | + "tinygo.org/x/drivers" |
| 12 | +) |
| 13 | + |
| 14 | +// Device wraps an I2C connection to a HTS221 device. |
| 15 | +type Device struct { |
| 16 | + bus drivers.I2C |
| 17 | + Address uint8 |
| 18 | + humiditySlope float32 |
| 19 | + humidityZero float32 |
| 20 | + temperatureSlope float32 |
| 21 | + temperatureZero float32 |
| 22 | +} |
| 23 | + |
| 24 | +// Connected returns whether HTS221 has been found. |
| 25 | +// It does a "who am I" request and checks the response. |
| 26 | +func (d *Device) Connected() bool { |
| 27 | + data := []byte{0} |
| 28 | + d.bus.ReadRegister(d.Address, HTS221_WHO_AM_I_REG, data) |
| 29 | + return data[0] == 0xBC |
| 30 | +} |
| 31 | + |
| 32 | +// Configure sets up the HTS221 device for communication. |
| 33 | +func (d *Device) Configure() { |
| 34 | + // read calibration data |
| 35 | + d.calibration() |
| 36 | + // activate device and use block data update mode |
| 37 | + d.Power(true) |
| 38 | +} |
| 39 | + |
| 40 | +// Power is for turn on/off the HTS221 device |
| 41 | +func (d *Device) Power(status bool) { |
| 42 | + data := []byte{0} |
| 43 | + if status { |
| 44 | + data[0] = 0x84 |
| 45 | + } |
| 46 | + d.bus.WriteRegister(d.Address, HTS221_CTRL1_REG, data) |
| 47 | +} |
| 48 | + |
| 49 | +// ReadHumidity returns the relative humidity in percent * 100. |
| 50 | +// Returns an error if the device is not turned on. |
| 51 | +func (d *Device) ReadHumidity() (humidity int32, err error) { |
| 52 | + err = d.waitForOneShot(0x02) |
| 53 | + if err != nil { |
| 54 | + return |
| 55 | + } |
| 56 | + |
| 57 | + // read data and calibrate |
| 58 | + data := []byte{0, 0} |
| 59 | + d.bus.ReadRegister(d.Address, HTS221_HUMID_OUT_REG, data[:1]) |
| 60 | + d.bus.ReadRegister(d.Address, HTS221_HUMID_OUT_REG+1, data[1:]) |
| 61 | + hValue := readInt(data[1], data[0]) |
| 62 | + hValueCalib := float32(hValue)*d.humiditySlope + d.humidityZero |
| 63 | + |
| 64 | + return int32(hValueCalib * 100), nil |
| 65 | +} |
| 66 | + |
| 67 | +// ReadTemperature returns the temperature in celsius milli degrees (°C/1000). |
| 68 | +// Returns an error if the device is not turned on. |
| 69 | +func (d *Device) ReadTemperature() (temperature int32, err error) { |
| 70 | + err = d.waitForOneShot(0x01) |
| 71 | + if err != nil { |
| 72 | + return |
| 73 | + } |
| 74 | + |
| 75 | + // read data and calibrate |
| 76 | + data := []byte{0, 0} |
| 77 | + d.bus.ReadRegister(d.Address, HTS221_TEMP_OUT_REG, data[:1]) |
| 78 | + d.bus.ReadRegister(d.Address, HTS221_TEMP_OUT_REG+1, data[1:]) |
| 79 | + tValue := readInt(data[1], data[0]) |
| 80 | + tValueCalib := float32(tValue)*d.temperatureSlope + d.temperatureZero |
| 81 | + |
| 82 | + return int32(tValueCalib * 1000), nil |
| 83 | +} |
| 84 | + |
| 85 | +// Resolution sets the HTS221's resolution mode. |
| 86 | +// The higher resolutions are more accurate but comsume more power (see datasheet). |
| 87 | +// The number of averaged samples will be (h + 2) ^ 2, (t + 1) ^ 2 |
| 88 | +// |
| 89 | +func (d *Device) Resolution(h uint8, t uint8) { |
| 90 | + if h > 7 { |
| 91 | + h = 3 // default |
| 92 | + } |
| 93 | + if t > 7 { |
| 94 | + t = 3 // default |
| 95 | + } |
| 96 | + d.bus.WriteRegister(d.Address, HTS221_AV_CONF_REG, []byte{h<<3 | t}) |
| 97 | +} |
| 98 | + |
| 99 | +// private functions |
| 100 | + |
| 101 | +// read factory calibration data |
| 102 | +func (d *Device) calibration() { |
| 103 | + h0rH, h1rH := []byte{0}, []byte{0} |
| 104 | + t0degC, t1degC := []byte{0}, []byte{0} |
| 105 | + t1t0msb := []byte{0} |
| 106 | + h0t0Out, h1t0Out := []byte{0, 0}, []byte{0, 0} |
| 107 | + t0Out, t1Out := []byte{0, 0}, []byte{0, 0} |
| 108 | + |
| 109 | + d.bus.ReadRegister(d.Address, HTS221_H0_rH_x2_REG, h0rH) |
| 110 | + d.bus.ReadRegister(d.Address, HTS221_H1_rH_x2_REG, h1rH) |
| 111 | + d.bus.ReadRegister(d.Address, HTS221_T0_degC_x8_REG, t0degC) |
| 112 | + d.bus.ReadRegister(d.Address, HTS221_T1_degC_x8_REG, t1degC) |
| 113 | + d.bus.ReadRegister(d.Address, HTS221_T1_T0_MSB_REG, t1t0msb) |
| 114 | + d.bus.ReadRegister(d.Address, HTS221_H0_T0_OUT_REG, h0t0Out[:1]) |
| 115 | + d.bus.ReadRegister(d.Address, HTS221_H0_T0_OUT_REG+1, h0t0Out[1:]) |
| 116 | + d.bus.ReadRegister(d.Address, HTS221_H1_T0_OUT_REG, h1t0Out[:1]) |
| 117 | + d.bus.ReadRegister(d.Address, HTS221_H1_T0_OUT_REG+1, h1t0Out[1:]) |
| 118 | + d.bus.ReadRegister(d.Address, HTS221_T0_OUT_REG, t0Out[:1]) |
| 119 | + d.bus.ReadRegister(d.Address, HTS221_T0_OUT_REG+1, t0Out[1:]) |
| 120 | + d.bus.ReadRegister(d.Address, HTS221_T1_OUT_REG, t1Out[:1]) |
| 121 | + d.bus.ReadRegister(d.Address, HTS221_T1_OUT_REG+1, t1Out[1:]) |
| 122 | + |
| 123 | + h0rH_v := float32(h0rH[0]) / 2.0 |
| 124 | + h1rH_v := float32(h1rH[0]) / 2.0 |
| 125 | + t0degC_v := float32(readUint(t1t0msb[0]&0x03, t0degC[0])) / 8.0 |
| 126 | + t1degC_v := float32(readUint(t1t0msb[0]&0x0C>>2, t1degC[0])) / 8.0 |
| 127 | + h0t0Out_v := float32(readInt(h0t0Out[1], h0t0Out[0])) |
| 128 | + h1t0Out_v := float32(readInt(h1t0Out[1], h1t0Out[0])) |
| 129 | + t0Out_v := float32(readInt(t0Out[1], t0Out[0])) |
| 130 | + t1Out_v := float32(readInt(t1Out[1], t1Out[0])) |
| 131 | + |
| 132 | + d.humiditySlope = (h1rH_v - h0rH_v) / (h1t0Out_v - h0t0Out_v) |
| 133 | + d.humidityZero = h0rH_v - d.humiditySlope*h0t0Out_v |
| 134 | + d.temperatureSlope = (t1degC_v - t0degC_v) / (t1Out_v - t0Out_v) |
| 135 | + d.temperatureZero = t0degC_v - d.temperatureSlope*t0Out_v |
| 136 | +} |
| 137 | + |
| 138 | +// wait and trigger one shot in block update |
| 139 | +func (d *Device) waitForOneShot(filter uint8) error { |
| 140 | + data := []byte{0} |
| 141 | + |
| 142 | + // check if the device is on |
| 143 | + d.bus.ReadRegister(d.Address, HTS221_CTRL1_REG, data) |
| 144 | + if data[0]&0x80 == 0 { |
| 145 | + return errors.New("device is off, unable to query") |
| 146 | + } |
| 147 | + |
| 148 | + // wait until one shot (one conversion) is ready to go |
| 149 | + data[0] = 1 |
| 150 | + for { |
| 151 | + d.bus.ReadRegister(d.Address, HTS221_CTRL2_REG, data) |
| 152 | + if data[0]&0x01 == 0 { |
| 153 | + break |
| 154 | + } |
| 155 | + } |
| 156 | + |
| 157 | + // trigger one shot |
| 158 | + d.bus.WriteRegister(d.Address, HTS221_CTRL2_REG, []byte{0x01}) |
| 159 | + |
| 160 | + // wait until conversion completed |
| 161 | + data[0] = 0 |
| 162 | + for { |
| 163 | + d.bus.ReadRegister(d.Address, HTS221_STATUS_REG, data) |
| 164 | + if data[0]&filter == filter { |
| 165 | + break |
| 166 | + } |
| 167 | + } |
| 168 | + |
| 169 | + return nil |
| 170 | +} |
| 171 | + |
| 172 | +func readUint(msb byte, lsb byte) uint16 { |
| 173 | + return uint16(msb)<<8 | uint16(lsb) |
| 174 | +} |
| 175 | + |
| 176 | +func readInt(msb byte, lsb byte) int16 { |
| 177 | + return int16(uint16(msb)<<8 | uint16(lsb)) |
| 178 | +} |
0 commit comments