-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgrovepi.go
340 lines (298 loc) · 8.13 KB
/
grovepi.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
package gogrove
import (
"encoding/binary"
"fmt"
"math"
"sync"
"time"
"periph.io/x/periph/conn/i2c"
"periph.io/x/periph/conn/i2c/i2creg"
"periph.io/x/periph/host"
)
const (
digitalRead uint8 = 1
digitalWrite uint8 = 2
analogRead uint8 = 3
analogWrite uint8 = 4
pinMode uint8 = 5
// dustSensorReadInt uint8 = 6
ultraSonic uint8 = 7
firmwareVersion uint8 = 8
// dustSensorInt uint8 = 9
// dustSensorRead uint8 = 10
// encoderRead uint8 = 11
// flowRead uint8 = 12
// flowDis uint8 = 13
// dustSensorEn uint8 = 14
// dustSensorDis uint8 = 15
// encoderEn uint8 = 16
// encoderDis uint8 = 17
// flowEn uint8 = 18
// irRead uint8 = 21
// irRecvPin uint8 = 22
dataNotAvailable uint8 = 23
// irReadIsData uint8 = 24
dhtTemp uint8 = 40
// ledBarInit uint8 = 50
// ledBarSetGreenToRed uint8 = 51
// ledBarSetLevel uint8 = 52
// ledBarSetLed uint8 = 53
// ledBarToggelLed uint8 = 54
// ledBarSetBits uint8 = 55
// ledBarGetBits uint8 = 56
// fourDigitInit uint8 = 70
// fourDigitSetBright uint8 = 71
// fourDigitRAn0s uint8 = 72
// fourDigitRAw0s uint8 = 73
// fourDigitSetDigit uint8 = 74
// fourDigitSetSegment uint8 = 75
// fourDigitSetValsWithColon uint8 = 76
// fourDigitDisAReadNSec uint8 = 77
// fourDigitDispOn uint8 = 78
// fourDigitDispOff uint8 = 79
// chainLedStorColor uint8 = 90
// chainLedInit uint8 = 91
// chainLedInitWithColor uint8 = 92
// chainLedSetLedsStorPattern uint8 = 93
// chainLedSetLedsStorModulo uint8 = 94
// chainLedSetBarGraph uint8 = 95
// PortA0 is the value for GrovePi A0
PortA0 uint8 = 0
// PortA1 is the value for GrovePi A1
PortA1 uint8 = 1
// PortA2 is the value for GrovePi A2
PortA2 uint8 = 2
// PortD2 is the value for GrovePi D2
PortD2 uint8 = 2
// PortD3 is the value for GrovePi D3
PortD3 uint8 = 3
// PortD4 is the value for GrovePi D4
PortD4 uint8 = 4
// PortD5 is the value for GrovePi D5
PortD5 uint8 = 5
// PortD6 is the value for GrovePi D6
PortD6 uint8 = 6
// PortD7 is the value for GrovePi D7
PortD7 uint8 = 7
// PortD8 is the value for GrovePi D8
PortD8 uint8 = 8
// ModeInput is used for SetPortMode to input
ModeInput uint8 = 0
// ModeOutput is used for SetPortMode to output
ModeOutput uint8 = 1
// BlueDHTSensor is the DHT sensor that comes with base kit (DHT11)
BlueDHTSensor uint8 = 0
// WhiteDHTSensor is the separate white DHT sensor (DHT22)
WhiteDHTSensor uint8 = 1
// DHT21Sensor DHT21
DHT21Sensor uint8 = 2
// AM2301Sensor AM2301
AM2301Sensor uint8 = 3
)
// Session holds session info for interacting with GrovePi.
type Session struct {
sync.Mutex
d *i2c.Dev
b i2c.BusCloser
}
// New initializes new session with default GrovePi address.
func New() (*Session, error) {
if _, err := host.Init(); err != nil {
return nil, err
}
b, err := i2creg.Open("")
if err != nil {
return nil, err
}
return &Session{
d: &i2c.Dev{Addr: 0x4, Bus: b},
b: b,
}, nil
}
// NewWithAddress initializes new session with given GrovePi address.
func NewWithAddress(address uint16) (*Session, error) {
if _, err := host.Init(); err != nil {
return nil, err
}
b, err := i2creg.Open("")
if err != nil {
return nil, err
}
return &Session{
d: &i2c.Dev{Addr: address, Bus: b},
b: b,
}, nil
}
// Close closes GrovePi session.
func (s *Session) Close() error { return s.b.Close() }
// SetPortMode sets port to mode, for example:
// SetPortMode(gogrove.PortA0, gogrove.ModeOutput),
// SetPortMode(gogrove.PortD3, gogrove.ModeInput).
func (s *Session) SetPortMode(port, mode uint8) error {
s.Lock()
defer s.Unlock()
write := []byte{0, pinMode, port, mode, 0}
return s.d.Tx(write, nil)
}
// GetFirmwareVersion returns the GrovePi firmware version.
func (s *Session) GetFirmwareVersion() (string, error) {
s.Lock()
defer s.Unlock()
write := []byte{0, firmwareVersion, 0, 0, 0}
read := make([]byte, 5)
if err := s.d.Tx(write, read); err != nil {
return "", err
}
if read[0] != firmwareVersion {
return "", fmt.Errorf("command error response: %v", read[0])
}
return fmt.Sprintf("%v.%v.%v", read[1], read[2], read[3]), nil
}
// DigitalRead returns the value from a digital port.
// On success, this will be either 0 or 1.
func (s *Session) DigitalRead(port uint8) (uint8, error) {
s.Lock()
defer s.Unlock()
write := []byte{0, digitalRead, port, 0, 0}
read := make([]byte, 5)
if err := s.d.Tx(write, read); err != nil {
return 0, err
}
return read[1], nil
}
// IsOn is shorthand for DigitalRead on a digital port,
// returning true if the port is 1
// and false if the port is 0.
// This ignores errors from DigitalRead for easier inlining.
func (s *Session) IsOn(port uint8) bool {
state, _ := s.DigitalRead(port)
if state == 0 {
return false
}
return true
}
// DigitalWrite sets the value for the given port.
// The value must be 0 or 1.
func (s *Session) DigitalWrite(port, value uint8) error {
s.Lock()
defer s.Unlock()
if value != 0 && value != 1 {
return fmt.Errorf("invalid digital write value")
}
write := []byte{0, digitalWrite, port, value, 0}
return s.d.Tx(write, nil)
}
// TurnOn is shorthand for DigitalWrite(port, 1).
func (s *Session) TurnOn(port uint8) error {
return s.DigitalWrite(port, 1)
}
// TurnOff is shorthand for DigitalWrite(port, 0).
func (s *Session) TurnOff(port uint8) error {
return s.DigitalWrite(port, 0)
}
// AnalogRead reads analog value from port.
// This is only valid for PortA0, PortA1, or PortA2.
// The returned value will be between 0-1023 inclusive.
func (s *Session) AnalogRead(port uint8) (uint16, error) {
s.Lock()
defer s.Unlock()
if !(port == PortA0 || port == PortA1 || port == PortA2) {
return 0, fmt.Errorf("invalid port for analog read")
}
write := []byte{0, analogRead, port, 0, 0}
read := make([]byte, 5)
if err := s.d.Tx(write, read); err != nil {
return 0, err
}
i := 0
for {
if read[0] != dataNotAvailable || i == 100 {
break
}
time.Sleep(1 * time.Millisecond)
if err := s.d.Tx(nil, read); err != nil {
return 0, err
}
i++
}
if read[0] != analogRead {
return 0, fmt.Errorf("command error response: %v", read[0])
}
return binary.BigEndian.Uint16(read[1:][:2]), nil
}
// AnalogWrite writes value 0-255 inclusive to given port.
// This appears to only be valid for PortD3, PortD5, and PortD6
// using PWM write.
func (s *Session) AnalogWrite(port, value uint8) error {
s.Lock()
defer s.Unlock()
// D3, D5, D6 PWM writes
if !(port == PortD3 || port == PortD5 || port == PortD6) {
return fmt.Errorf("invalid port for analog write")
}
write := []byte{0, analogWrite, port, value, 0}
return s.d.Tx(write, nil)
}
// ReadDHT returns temp (C), humidity (%).
// Must pass the sensort type,
// one of: gogrove.BlueDHTSensor gogrove.WhiteDHTSensor.
func (s *Session) ReadDHT(port, sensor uint8) (float32, float32, error) {
s.Lock()
defer s.Unlock()
write := []byte{0, dhtTemp, port, sensor, 0}
read := make([]byte, 9)
if err := s.d.Tx(write, read); err != nil {
return 0, 0, err
}
i := 0
for {
if read[0] == dhtTemp || i == 100 {
break
}
time.Sleep(10 * time.Millisecond)
if err := s.d.Tx(nil, read); err != nil {
return 0, 0, err
}
i++
}
if read[0] != dhtTemp {
return 0, 0, fmt.Errorf("invalid command response: %v", read[0])
}
temp := float32frombytes(read[1:][:4])
humidity := float32frombytes(read[5:][:4])
if temp > -100.0 && temp < 150.0 && humidity >= 0.0 && humidity <= 100.0 {
return temp, humidity, nil
}
return 0, 0, fmt.Errorf("value out of bounds")
}
func float32frombytes(bytes []byte) float32 {
bits := binary.LittleEndian.Uint32(bytes)
float := math.Float32frombits(bits)
return float
}
// ReadUltraSonic returns distance in cm.
// Sensor spec: measuring range 2-350cm, resolution 1cm.
func (s *Session) ReadUltraSonic(port uint8) (uint16, error) {
s.Lock()
defer s.Unlock()
write := []byte{0, ultraSonic, port, 0, 0}
read := make([]byte, 3)
if err := s.d.Tx(write, read); err != nil {
return 0, err
}
i := 0
for {
if read[0] == ultraSonic || i == 100 {
break
}
time.Sleep(10 * time.Millisecond)
if err := s.d.Tx(nil, read); err != nil {
return 0, err
}
i++
}
if read[0] != ultraSonic {
return 0, fmt.Errorf("invalid command response: %v", read[0])
}
return binary.BigEndian.Uint16(read[1:][:2]), nil
}