From f034d35a786fc3c292dc4c24cacb2304995e14ac Mon Sep 17 00:00:00 2001 From: Mike Crute Date: Tue, 21 May 2019 13:01:28 +0000 Subject: Initial checkin of working prototype --- dht11/dht11.go | 218 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 43 ++++++++++++ 2 files changed, 261 insertions(+) create mode 100644 dht11/dht11.go create mode 100644 main.go diff --git a/dht11/dht11.go b/dht11/dht11.go new file mode 100644 index 0000000..4d54aff --- /dev/null +++ b/dht11/dht11.go @@ -0,0 +1,218 @@ +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 +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..6aac0c7 --- /dev/null +++ b/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "log" + "time" + + "dht11" + + "github.com/stianeikeland/go-rpio" +) + +const ( + RELAY_PIN = 27 + TEMP_PIN = 17 +) + +func toggleRelay(pin *rpio.Pin) { + pin.Output() + pin.PullDown() + + pin.High() + time.Sleep(10 * time.Second) + pin.Low() +} + +func main() { + err := rpio.Open() + if err != nil { + log.Fatal(err) + } + defer rpio.Close() + + tempPin := rpio.Pin(TEMP_PIN) + tempSensor := dht11.NewRaspberryPiDHT11(&tempPin) + + if temp, err := tempSensor.GetSensorDataWithRetry(5); err == nil { + log.Printf("temp: %d\n", temp.TempC) + log.Printf("temp: %f\n", temp.TempF) + log.Printf("humidity: %d\n", temp.Humidity) + } else { + log.Printf("Failed to read sensor") + } +} -- cgit v1.2.3