summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dht11/dht11.go218
-rw-r--r--main.go43
2 files changed, 261 insertions, 0 deletions
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 @@
1package dht11
2
3import (
4 "errors"
5 "log"
6 "time"
7
8 "github.com/stianeikeland/go-rpio"
9)
10
11type tempState int
12
13const (
14 initPullDown tempState = iota
15 initPullUp
16 dataFirstPullDown
17 dataPullUp
18 dataPullDown
19)
20
21type SensorData struct {
22 TempC int
23 TempF float32
24 Humidity int
25}
26
27type Device struct {
28 StartLowTime time.Duration
29 LowTime time.Duration
30 HighTime time.Duration
31 ReadDelay time.Duration
32}
33
34var RaspberryPi Device
35
36func init() {
37 // These were determined experimentally using a logic analyser a Raspberry
38 // Pi 3 B+. They may vary for other device types and processor speeds.
39 RaspberryPi = Device{
40 StartLowTime: 4 * time.Microsecond,
41 LowTime: 15 * time.Millisecond,
42 HighTime: 50 * time.Millisecond,
43 ReadDelay: 1 * time.Microsecond,
44 }
45}
46
47type DHT11 struct {
48 Device *Device
49 Pin *rpio.Pin
50}
51
52func NewRaspberryPiDHT11(pin *rpio.Pin) *DHT11 {
53 return &DHT11{
54 Device: &RaspberryPi,
55 Pin: pin,
56 }
57}
58
59func (d *DHT11) sendDataRequest() {
60 d.Pin.Output()
61
62 d.Pin.Low()
63 time.Sleep(d.Device.StartLowTime)
64 d.Pin.High()
65 time.Sleep(d.Device.HighTime)
66 d.Pin.Low()
67 time.Sleep(d.Device.LowTime)
68
69 d.Pin.Input()
70 d.Pin.PullUp()
71}
72
73func (d *DHT11) gatherRawData() []rpio.State {
74 var i int
75 var last rpio.State
76 data := make([]rpio.State, 0)
77
78 for i = 0; i < 500; i++ {
79 current := d.Pin.Read()
80 time.Sleep(d.Device.ReadDelay)
81 data = append(data, current)
82 if last != current {
83 i = 0
84 last = current
85 }
86 }
87
88 return data
89}
90
91func (d *DHT11) parseSignals(input []rpio.State) ([]int, error) {
92 state := initPullDown
93 lengths := make([]int, 0, 40)
94 currentLength := 0
95
96 for _, current := range input {
97 currentLength++
98
99 switch state {
100 case initPullDown:
101 if current == rpio.Low {
102 state = initPullUp
103 }
104 case initPullUp:
105 if current == rpio.High {
106 state = dataFirstPullDown
107 }
108 case dataFirstPullDown:
109 if current == rpio.Low {
110 state = dataPullUp
111 }
112 case dataPullUp:
113 if current == rpio.High {
114 currentLength = 0
115 state = dataPullDown
116 }
117 case dataPullDown:
118 if current == rpio.Low {
119 lengths = append(lengths, currentLength)
120 state = dataPullUp
121 }
122 }
123 }
124
125 if len(lengths) != 40 {
126 return nil, errors.New("not enough data returned")
127 }
128
129 return lengths, nil
130}
131
132func (d *DHT11) signalsToBytes(lengths []int) ([]int, error) {
133 bytes := make([]int, 0, 5)
134
135 longestUp, shortestUp := 0, 1000
136 for _, length := range lengths {
137 if length < shortestUp {
138 shortestUp = length
139 }
140
141 if length > longestUp {
142 longestUp = length
143 }
144 }
145
146 halfway := shortestUp + (longestUp-shortestUp)/2
147
148 bits := make([]bool, 0, len(lengths))
149 for _, length := range lengths {
150 bit := false
151 if length > halfway {
152 bit = true
153 }
154 bits = append(bits, bit)
155 }
156
157 currentByte := 0
158 for i, bit := range bits {
159 currentByte <<= 1
160 if bit {
161 currentByte |= 1
162 } else {
163 currentByte |= 0
164 }
165
166 if (i+1)%8 == 0 {
167 bytes = append(bytes, currentByte)
168 currentByte = 0
169 }
170 }
171
172 checksum := (bytes[0] + bytes[1] + bytes[2] + bytes[3]) & 255
173 if bytes[4] != checksum {
174 return nil, errors.New("CRC error")
175 }
176
177 return bytes, nil
178}
179
180func (d *DHT11) GetSensorData() (*SensorData, error) {
181 d.sendDataRequest()
182
183 rawData := d.gatherRawData()
184 signals, err := d.parseSignals(rawData)
185 if err != nil {
186 return nil, err
187 }
188
189 dataBytes, err := d.signalsToBytes(signals)
190 if err != nil {
191 return nil, err
192 }
193
194 return &SensorData{
195 TempC: dataBytes[2],
196 TempF: celsiusToFarenheit(dataBytes[2]),
197 Humidity: dataBytes[0],
198 }, nil
199}
200
201func (d *DHT11) GetSensorDataWithRetry(rc int) (data *SensorData, err error) {
202 for i := 0; i < rc; i++ {
203 data, err = d.GetSensorData()
204 if err == nil {
205 return
206 } else {
207 log.Printf("error")
208 time.Sleep(1 * time.Second)
209 continue
210 }
211 }
212
213 return
214}
215
216func celsiusToFarenheit(c int) float32 {
217 return float32(c)*1.8 + 32
218}
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..6aac0c7
--- /dev/null
+++ b/main.go
@@ -0,0 +1,43 @@
1package main
2
3import (
4 "log"
5 "time"
6
7 "dht11"
8
9 "github.com/stianeikeland/go-rpio"
10)
11
12const (
13 RELAY_PIN = 27
14 TEMP_PIN = 17
15)
16
17func toggleRelay(pin *rpio.Pin) {
18 pin.Output()
19 pin.PullDown()
20
21 pin.High()
22 time.Sleep(10 * time.Second)
23 pin.Low()
24}
25
26func main() {
27 err := rpio.Open()
28 if err != nil {
29 log.Fatal(err)
30 }
31 defer rpio.Close()
32
33 tempPin := rpio.Pin(TEMP_PIN)
34 tempSensor := dht11.NewRaspberryPiDHT11(&tempPin)
35
36 if temp, err := tempSensor.GetSensorDataWithRetry(5); err == nil {
37 log.Printf("temp: %d\n", temp.TempC)
38 log.Printf("temp: %f\n", temp.TempF)
39 log.Printf("humidity: %d\n", temp.Humidity)
40 } else {
41 log.Printf("Failed to read sensor")
42 }
43}