package dht11 import ( "errors" "log" "time" "github.com/stianeikeland/go-rpio" ) type tempState int const ( initPullDown tempState = iota initPullUp dataFirstPullDown dataPullUp dataPullDown ) type SensorData struct { TempC int TempF float32 Humidity int } type Device struct { StartLowTime time.Duration LowTime time.Duration HighTime time.Duration ReadDelay time.Duration } var RaspberryPi Device func init() { // These were determined experimentally using a logic analyser a Raspberry // Pi 3 B+. They may vary for other device types and processor speeds. RaspberryPi = Device{ StartLowTime: 4 * time.Microsecond, LowTime: 15 * time.Millisecond, HighTime: 50 * time.Millisecond, ReadDelay: 1 * time.Microsecond, } } type DHT11 struct { Device *Device Pin *rpio.Pin } func NewRaspberryPiDHT11(pin *rpio.Pin) *DHT11 { return &DHT11{ Device: &RaspberryPi, Pin: pin, } } func (d *DHT11) sendDataRequest() { d.Pin.Output() d.Pin.Low() time.Sleep(d.Device.StartLowTime) d.Pin.High() time.Sleep(d.Device.HighTime) d.Pin.Low() time.Sleep(d.Device.LowTime) d.Pin.Input() d.Pin.PullUp() } func (d *DHT11) gatherRawData() []rpio.State { var i int var last rpio.State data := make([]rpio.State, 0) for i = 0; i < 500; i++ { current := d.Pin.Read() time.Sleep(d.Device.ReadDelay) data = append(data, current) if last != current { i = 0 last = current } } return data } func (d *DHT11) parseSignals(input []rpio.State) ([]int, error) { state := initPullDown lengths := make([]int, 0, 40) currentLength := 0 for _, current := range input { currentLength++ switch state { case initPullDown: if current == rpio.Low { state = initPullUp } case initPullUp: if current == rpio.High { state = dataFirstPullDown } case dataFirstPullDown: if current == rpio.Low { state = dataPullUp } case dataPullUp: if current == rpio.High { currentLength = 0 state = dataPullDown } case dataPullDown: if current == rpio.Low { lengths = append(lengths, currentLength) state = dataPullUp } } } if len(lengths) != 40 { return nil, errors.New("not enough data returned") } return lengths, nil } func (d *DHT11) signalsToBytes(lengths []int) ([]int, error) { bytes := make([]int, 0, 5) longestUp, shortestUp := 0, 1000 for _, length := range lengths { if length < shortestUp { shortestUp = length } if length > longestUp { longestUp = length } } halfway := shortestUp + (longestUp-shortestUp)/2 bits := make([]bool, 0, len(lengths)) for _, length := range lengths { bit := false if length > halfway { bit = true } bits = append(bits, bit) } currentByte := 0 for i, bit := range bits { currentByte <<= 1 if bit { currentByte |= 1 } else { currentByte |= 0 } if (i+1)%8 == 0 { bytes = append(bytes, currentByte) currentByte = 0 } } checksum := (bytes[0] + bytes[1] + bytes[2] + bytes[3]) & 255 if bytes[4] != checksum { return nil, errors.New("CRC error") } return bytes, nil } func (d *DHT11) GetSensorData() (*SensorData, error) { d.sendDataRequest() rawData := d.gatherRawData() signals, err := d.parseSignals(rawData) if err != nil { return nil, err } dataBytes, err := d.signalsToBytes(signals) if err != nil { return nil, err } return &SensorData{ TempC: dataBytes[2], TempF: celsiusToFarenheit(dataBytes[2]), Humidity: dataBytes[0], }, nil } func (d *DHT11) GetSensorDataWithRetry(rc int) (data *SensorData, err error) { for i := 0; i < rc; i++ { data, err = d.GetSensorData() if err == nil { return } else { log.Printf("error") time.Sleep(1 * time.Second) continue } } return } func celsiusToFarenheit(c int) float32 { return float32(c)*1.8 + 32 }