Skip to content

Commit cd2694e

Browse files
authored
Add support for HTS221 capacitive digital sensor for relative humidity and temperature (#295)
hts221: Add support for hts221.go
1 parent 734adb6 commit cd2694e

File tree

6 files changed

+285
-1
lines changed

6 files changed

+285
-1
lines changed

Makefile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ smoke-test:
6363
@md5sum ./build/test.hex
6464
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/hd44780i2c/main.go
6565
@md5sum ./build/test.hex
66+
tinygo build -size short -o ./build/test.hex -target=nano-33-ble ./examples/hts221/main.go
67+
@md5sum ./build/test.hex
6668
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/hub75/main.go
6769
@md5sum ./build/test.hex
6870
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/basic
@@ -212,7 +214,7 @@ DRIVERS = $(wildcard */)
212214
NOTESTS = build examples flash semihosting pcd8544 shiftregister st7789 microphone mcp3008 gps microbitmatrix \
213215
hcsr04 ssd1331 ws2812 thermistor apa102 easystepper ssd1351 ili9341 wifinina shifter hub75 \
214216
hd44780 buzzer ssd1306 espat l9110x st7735 bmi160 l293x dht keypad4x4 max72xx p1am tone tm1637 \
215-
pcf8563 mcp2515 servo sdcard rtl8720dn image cmd i2csoft
217+
pcf8563 mcp2515 servo sdcard rtl8720dn image cmd i2csoft hts221
216218
TESTS = $(filter-out $(addsuffix /%,$(NOTESTS)),$(DRIVERS))
217219

218220
unit-test:

examples/hts221/main.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package main
2+
3+
import (
4+
"machine"
5+
"time"
6+
7+
"tinygo.org/x/drivers/hts221"
8+
)
9+
10+
func main() {
11+
12+
machine.I2C1.Configure(machine.I2CConfig{
13+
SCL: machine.P0_15, // SCL1 on Nano 33 BLE Sense
14+
SDA: machine.P0_14, // SDA1 on Nano 33 BLE Sense
15+
Frequency: machine.TWI_FREQ_400KHZ,
16+
})
17+
18+
sensor := hts221.New(machine.I2C1)
19+
20+
if !sensor.Connected() {
21+
println("HTS221 not connected!")
22+
return
23+
}
24+
25+
sensor.Configure() // power on and calibrate
26+
27+
for {
28+
29+
h, _ := sensor.ReadHumidity()
30+
t, _ := sensor.ReadTemperature()
31+
println("h =", float32(h)/100.0, "% / t =", float32(t)/1000.0, "*C")
32+
time.Sleep(time.Second)
33+
34+
}
35+
36+
}

hts221/hts221.go

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
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+
}

hts221/hts221_generic.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// +build !nano_33_ble
2+
3+
package hts221
4+
5+
import "tinygo.org/x/drivers"
6+
7+
// New creates a new HTS221 connection. The I2C bus must already be
8+
// configured.
9+
//
10+
// This function only creates the Device object, it does not touch the device.
11+
func New(bus drivers.I2C) Device {
12+
return Device{bus: bus, Address: HTS221_ADDRESS}
13+
}

hts221/hts221_nano_33_ble.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// +build nano_33_ble
2+
3+
package hts221
4+
5+
import (
6+
"machine"
7+
"time"
8+
9+
"tinygo.org/x/drivers"
10+
)
11+
12+
// New creates a new HTS221 connection. The I2C bus must already be
13+
// configured.
14+
//
15+
// This function only creates the Device object, it does not touch the device.
16+
func New(bus drivers.I2C) Device {
17+
// turn on internal power pin (machine.P0_22) and I2C1 pullups power pin (machine.P1_00)
18+
// and wait a moment.
19+
ENV := machine.P0_22
20+
ENV.Configure(machine.PinConfig{Mode: machine.PinOutput})
21+
ENV.High()
22+
R := machine.P1_00
23+
R.Configure(machine.PinConfig{Mode: machine.PinOutput})
24+
R.High()
25+
time.Sleep(time.Millisecond * 10)
26+
27+
return Device{bus: bus, Address: HTS221_ADDRESS}
28+
}

hts221/registers.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package hts221
2+
3+
const (
4+
5+
// I2C address
6+
HTS221_ADDRESS = 0x5F
7+
8+
// control/status registers
9+
HTS221_WHO_AM_I_REG = 0x0F
10+
HTS221_AV_CONF_REG = 0x10
11+
HTS221_CTRL1_REG = 0x20
12+
HTS221_CTRL2_REG = 0x21
13+
HTS221_STATUS_REG = 0x27
14+
HTS221_HUMID_OUT_REG = 0x28
15+
HTS221_TEMP_OUT_REG = 0x2A
16+
17+
// calibration registers
18+
HTS221_H0_rH_x2_REG = 0x30
19+
HTS221_H1_rH_x2_REG = 0x31
20+
HTS221_T0_degC_x8_REG = 0x32
21+
HTS221_T1_degC_x8_REG = 0x33
22+
HTS221_T1_T0_MSB_REG = 0x35
23+
HTS221_H0_T0_OUT_REG = 0x36
24+
HTS221_H1_T0_OUT_REG = 0x3A
25+
HTS221_T0_OUT_REG = 0x3C
26+
HTS221_T1_OUT_REG = 0x3E
27+
)

0 commit comments

Comments
 (0)