Add support for HTS221 capacitive digital sensor for relative humidity and temperature (#295)

hts221: Add support for hts221.go
This commit is contained in:
Alan Wang
2021-11-06 18:02:30 +08:00
committed by GitHub
parent 734adb6df8
commit cd2694e85f
6 changed files with 285 additions and 1 deletions
+3 -1
View File
@@ -63,6 +63,8 @@ smoke-test:
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/hd44780i2c/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=nano-33-ble ./examples/hts221/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/hub75/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/basic
@@ -212,7 +214,7 @@ DRIVERS = $(wildcard */)
NOTESTS = build examples flash semihosting pcd8544 shiftregister st7789 microphone mcp3008 gps microbitmatrix \
hcsr04 ssd1331 ws2812 thermistor apa102 easystepper ssd1351 ili9341 wifinina shifter hub75 \
hd44780 buzzer ssd1306 espat l9110x st7735 bmi160 l293x dht keypad4x4 max72xx p1am tone tm1637 \
pcf8563 mcp2515 servo sdcard rtl8720dn image cmd i2csoft
pcf8563 mcp2515 servo sdcard rtl8720dn image cmd i2csoft hts221
TESTS = $(filter-out $(addsuffix /%,$(NOTESTS)),$(DRIVERS))
unit-test:
+36
View File
@@ -0,0 +1,36 @@
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/hts221"
)
func main() {
machine.I2C1.Configure(machine.I2CConfig{
SCL: machine.P0_15, // SCL1 on Nano 33 BLE Sense
SDA: machine.P0_14, // SDA1 on Nano 33 BLE Sense
Frequency: machine.TWI_FREQ_400KHZ,
})
sensor := hts221.New(machine.I2C1)
if !sensor.Connected() {
println("HTS221 not connected!")
return
}
sensor.Configure() // power on and calibrate
for {
h, _ := sensor.ReadHumidity()
t, _ := sensor.ReadTemperature()
println("h =", float32(h)/100.0, "% / t =", float32(t)/1000.0, "*C")
time.Sleep(time.Second)
}
}
+178
View File
@@ -0,0 +1,178 @@
// Package hts221 implements a driver for HTS221,
// a capacitive digital sensor for relative humidity and temperature.
//
// Datasheet: https://www.st.com/resource/en/datasheet/hts221.pdf
//
package hts221
import (
"errors"
"tinygo.org/x/drivers"
)
// Device wraps an I2C connection to a HTS221 device.
type Device struct {
bus drivers.I2C
Address uint8
humiditySlope float32
humidityZero float32
temperatureSlope float32
temperatureZero float32
}
// Connected returns whether HTS221 has been found.
// It does a "who am I" request and checks the response.
func (d *Device) Connected() bool {
data := []byte{0}
d.bus.ReadRegister(d.Address, HTS221_WHO_AM_I_REG, data)
return data[0] == 0xBC
}
// Configure sets up the HTS221 device for communication.
func (d *Device) Configure() {
// read calibration data
d.calibration()
// activate device and use block data update mode
d.Power(true)
}
// Power is for turn on/off the HTS221 device
func (d *Device) Power(status bool) {
data := []byte{0}
if status {
data[0] = 0x84
}
d.bus.WriteRegister(d.Address, HTS221_CTRL1_REG, data)
}
// ReadHumidity returns the relative humidity in percent * 100.
// Returns an error if the device is not turned on.
func (d *Device) ReadHumidity() (humidity int32, err error) {
err = d.waitForOneShot(0x02)
if err != nil {
return
}
// read data and calibrate
data := []byte{0, 0}
d.bus.ReadRegister(d.Address, HTS221_HUMID_OUT_REG, data[:1])
d.bus.ReadRegister(d.Address, HTS221_HUMID_OUT_REG+1, data[1:])
hValue := readInt(data[1], data[0])
hValueCalib := float32(hValue)*d.humiditySlope + d.humidityZero
return int32(hValueCalib * 100), nil
}
// ReadTemperature returns the temperature in celsius milli degrees (°C/1000).
// Returns an error if the device is not turned on.
func (d *Device) ReadTemperature() (temperature int32, err error) {
err = d.waitForOneShot(0x01)
if err != nil {
return
}
// read data and calibrate
data := []byte{0, 0}
d.bus.ReadRegister(d.Address, HTS221_TEMP_OUT_REG, data[:1])
d.bus.ReadRegister(d.Address, HTS221_TEMP_OUT_REG+1, data[1:])
tValue := readInt(data[1], data[0])
tValueCalib := float32(tValue)*d.temperatureSlope + d.temperatureZero
return int32(tValueCalib * 1000), nil
}
// Resolution sets the HTS221's resolution mode.
// The higher resolutions are more accurate but comsume more power (see datasheet).
// The number of averaged samples will be (h + 2) ^ 2, (t + 1) ^ 2
//
func (d *Device) Resolution(h uint8, t uint8) {
if h > 7 {
h = 3 // default
}
if t > 7 {
t = 3 // default
}
d.bus.WriteRegister(d.Address, HTS221_AV_CONF_REG, []byte{h<<3 | t})
}
// private functions
// read factory calibration data
func (d *Device) calibration() {
h0rH, h1rH := []byte{0}, []byte{0}
t0degC, t1degC := []byte{0}, []byte{0}
t1t0msb := []byte{0}
h0t0Out, h1t0Out := []byte{0, 0}, []byte{0, 0}
t0Out, t1Out := []byte{0, 0}, []byte{0, 0}
d.bus.ReadRegister(d.Address, HTS221_H0_rH_x2_REG, h0rH)
d.bus.ReadRegister(d.Address, HTS221_H1_rH_x2_REG, h1rH)
d.bus.ReadRegister(d.Address, HTS221_T0_degC_x8_REG, t0degC)
d.bus.ReadRegister(d.Address, HTS221_T1_degC_x8_REG, t1degC)
d.bus.ReadRegister(d.Address, HTS221_T1_T0_MSB_REG, t1t0msb)
d.bus.ReadRegister(d.Address, HTS221_H0_T0_OUT_REG, h0t0Out[:1])
d.bus.ReadRegister(d.Address, HTS221_H0_T0_OUT_REG+1, h0t0Out[1:])
d.bus.ReadRegister(d.Address, HTS221_H1_T0_OUT_REG, h1t0Out[:1])
d.bus.ReadRegister(d.Address, HTS221_H1_T0_OUT_REG+1, h1t0Out[1:])
d.bus.ReadRegister(d.Address, HTS221_T0_OUT_REG, t0Out[:1])
d.bus.ReadRegister(d.Address, HTS221_T0_OUT_REG+1, t0Out[1:])
d.bus.ReadRegister(d.Address, HTS221_T1_OUT_REG, t1Out[:1])
d.bus.ReadRegister(d.Address, HTS221_T1_OUT_REG+1, t1Out[1:])
h0rH_v := float32(h0rH[0]) / 2.0
h1rH_v := float32(h1rH[0]) / 2.0
t0degC_v := float32(readUint(t1t0msb[0]&0x03, t0degC[0])) / 8.0
t1degC_v := float32(readUint(t1t0msb[0]&0x0C>>2, t1degC[0])) / 8.0
h0t0Out_v := float32(readInt(h0t0Out[1], h0t0Out[0]))
h1t0Out_v := float32(readInt(h1t0Out[1], h1t0Out[0]))
t0Out_v := float32(readInt(t0Out[1], t0Out[0]))
t1Out_v := float32(readInt(t1Out[1], t1Out[0]))
d.humiditySlope = (h1rH_v - h0rH_v) / (h1t0Out_v - h0t0Out_v)
d.humidityZero = h0rH_v - d.humiditySlope*h0t0Out_v
d.temperatureSlope = (t1degC_v - t0degC_v) / (t1Out_v - t0Out_v)
d.temperatureZero = t0degC_v - d.temperatureSlope*t0Out_v
}
// wait and trigger one shot in block update
func (d *Device) waitForOneShot(filter uint8) error {
data := []byte{0}
// check if the device is on
d.bus.ReadRegister(d.Address, HTS221_CTRL1_REG, data)
if data[0]&0x80 == 0 {
return errors.New("device is off, unable to query")
}
// wait until one shot (one conversion) is ready to go
data[0] = 1
for {
d.bus.ReadRegister(d.Address, HTS221_CTRL2_REG, data)
if data[0]&0x01 == 0 {
break
}
}
// trigger one shot
d.bus.WriteRegister(d.Address, HTS221_CTRL2_REG, []byte{0x01})
// wait until conversion completed
data[0] = 0
for {
d.bus.ReadRegister(d.Address, HTS221_STATUS_REG, data)
if data[0]&filter == filter {
break
}
}
return nil
}
func readUint(msb byte, lsb byte) uint16 {
return uint16(msb)<<8 | uint16(lsb)
}
func readInt(msb byte, lsb byte) int16 {
return int16(uint16(msb)<<8 | uint16(lsb))
}
+13
View File
@@ -0,0 +1,13 @@
// +build !nano_33_ble
package hts221
import "tinygo.org/x/drivers"
// New creates a new HTS221 connection. The I2C bus must already be
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus drivers.I2C) Device {
return Device{bus: bus, Address: HTS221_ADDRESS}
}
+28
View File
@@ -0,0 +1,28 @@
// +build nano_33_ble
package hts221
import (
"machine"
"time"
"tinygo.org/x/drivers"
)
// New creates a new HTS221 connection. The I2C bus must already be
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus drivers.I2C) Device {
// turn on internal power pin (machine.P0_22) and I2C1 pullups power pin (machine.P1_00)
// and wait a moment.
ENV := machine.P0_22
ENV.Configure(machine.PinConfig{Mode: machine.PinOutput})
ENV.High()
R := machine.P1_00
R.Configure(machine.PinConfig{Mode: machine.PinOutput})
R.High()
time.Sleep(time.Millisecond * 10)
return Device{bus: bus, Address: HTS221_ADDRESS}
}
+27
View File
@@ -0,0 +1,27 @@
package hts221
const (
// I2C address
HTS221_ADDRESS = 0x5F
// control/status registers
HTS221_WHO_AM_I_REG = 0x0F
HTS221_AV_CONF_REG = 0x10
HTS221_CTRL1_REG = 0x20
HTS221_CTRL2_REG = 0x21
HTS221_STATUS_REG = 0x27
HTS221_HUMID_OUT_REG = 0x28
HTS221_TEMP_OUT_REG = 0x2A
// calibration registers
HTS221_H0_rH_x2_REG = 0x30
HTS221_H1_rH_x2_REG = 0x31
HTS221_T0_degC_x8_REG = 0x32
HTS221_T1_degC_x8_REG = 0x33
HTS221_T1_T0_MSB_REG = 0x35
HTS221_H0_T0_OUT_REG = 0x36
HTS221_H1_T0_OUT_REG = 0x3A
HTS221_T0_OUT_REG = 0x3C
HTS221_T1_OUT_REG = 0x3E
)