flucky/vendor/github.com/MichaelS11/go-dht/dhtNotWindows.go

153 lines
4.2 KiB
Go

// +build !windows
package dht
import (
"fmt"
"runtime/debug"
"strings"
"time"
"periph.io/x/periph/conn/gpio"
"periph.io/x/periph/conn/gpio/gpioreg"
)
// NewDHT to create a new DHT struct.
// sensorType is dht11 for DHT11, anything else for AM2302 / DHT22.
func NewDHT(pinName string, temperatureUnit TemperatureUnit, sensorType string) (*DHT, error) {
dht := &DHT{temperatureUnit: temperatureUnit}
// set sensorType
sensorType = strings.ToLower(sensorType)
if sensorType == "dht11" {
dht.sensorType = "dht11"
}
// get pin
dht.pin = gpioreg.ByName(pinName)
if dht.pin == nil {
return nil, fmt.Errorf("pin is nill")
}
// set pin to high so ready for first read
err := dht.pin.Out(gpio.High)
if err != nil {
return nil, fmt.Errorf("pin out high error: %v", err)
}
// set lastRead a second before to give the pin a second to warm up
dht.lastRead = time.Now().Add(-1 * time.Second)
return dht, nil
}
// readBits will get the bits for humidity and temperature
func (dht *DHT) readBits() ([]int, error) {
// create variables ahead of time before critical timing part
var i int
var startTime time.Time
var levelPrevious gpio.Level
var level gpio.Level
levels := make([]gpio.Level, 0, 84)
durations := make([]time.Duration, 0, 84)
// set lastRead so do not read more than once every 2 seconds
dht.lastRead = time.Now()
// disable garbage collection during critical timing part
gcPercent := debug.SetGCPercent(-1)
// send start low
err := dht.pin.Out(gpio.Low)
if err != nil {
dht.pin.Out(gpio.High)
return nil, fmt.Errorf("pin out low error: %v", err)
}
time.Sleep(time.Millisecond)
// send start high
err = dht.pin.In(gpio.PullUp, gpio.NoEdge)
if err != nil {
dht.pin.Out(gpio.High)
return nil, fmt.Errorf("pin in error: %v", err)
}
// read levels and durations with busy read
// hope there is a better way in the future
// tried to use WaitForEdge but seems to miss edges and/or take too long to detect them
// note that pin read takes around .2 microsecond (us) on Raspberry PI 3
// note that 1000 microsecond (us) = 1 millisecond (ms)
levelPrevious = dht.pin.Read()
level = levelPrevious
for i = 0; i < 84; i++ {
startTime = time.Now()
for levelPrevious == level && time.Since(startTime) < time.Millisecond {
level = dht.pin.Read()
}
durations = append(durations, time.Since(startTime))
levels = append(levels, levelPrevious)
levelPrevious = level
}
// enable garbage collection, done with critical part
debug.SetGCPercent(gcPercent)
// set pin to high so ready for next time
err = dht.pin.Out(gpio.High)
if err != nil {
return nil, fmt.Errorf("pin out high error: %v", err)
}
// get last low reading so know start of data
var endNumber int
for i = len(levels) - 1; ; i-- {
if levels[i] == gpio.Low {
endNumber = i
break
}
if i < 80 {
// not enough readings, i = 79 means endNumber is 78 or less
return nil, fmt.Errorf("missing some readings - low level not found")
}
}
startNumber := endNumber - 79
// covert pulses into bits and check high levels
bits := make([]int, 40)
index := 0
for i = startNumber; i < endNumber; i += 2 {
// check high levels
if levels[i] != gpio.High {
return nil, fmt.Errorf("missing some readings - level not high")
}
// high should not be longer then 90 microseconds
if durations[i] > 90*time.Microsecond {
return nil, fmt.Errorf("missing some readings - high level duration too long: %v", durations[i])
}
// bit is 0 if less than or equal to 30 microseconds
if durations[i] > 30*time.Microsecond {
// bit is 1 if more than 30 microseconds
bits[index] = 1
}
index++
}
// check low levels
for i = startNumber + 1; i < endNumber+1; i += 2 {
// check low levels
if levels[i] != gpio.Low {
return nil, fmt.Errorf("missing some readings - level not low")
}
// low should not be longer then 70 microseconds
if durations[i] > 70*time.Microsecond {
return nil, fmt.Errorf("missing some readings - low level duration too long: %v", durations[i])
}
// low should not be shorter then 35 microseconds
if durations[i] < 35*time.Microsecond {
return nil, fmt.Errorf("missing some readings - low level duration too short: %v", durations[i])
}
}
return bits, nil
}