-
-
Notifications
You must be signed in to change notification settings - Fork 1k
/
yl40_driver.go
324 lines (285 loc) · 9.19 KB
/
yl40_driver.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
package i2c
import (
"fmt"
"log"
"strings"
"time"
"gobot.io/x/gobot"
"gobot.io/x/gobot/drivers/aio"
)
// All address pins are connected to ground.
const yl40DefaultAddress = 0x48
const yl40Debug = false
type YL40Pin string
const (
// brightness sensor, high brightness - low raw value, scaled to 0..1000 (high brightness - high value)
YL40Bri YL40Pin = "brightness"
// temperature sensor, high temperature - low raw value, scaled to °C
YL40Temp YL40Pin = "temperature"
// wired to AOUT, scaled to voltage 3.3V
YL40AIN2 YL40Pin = "analog input AIN2"
// adjustable resistor, turn clockwise will lower the raw value, scaled to -100..+100% (clockwise)
YL40Poti YL40Pin = "potentiometer"
YL40AOUT YL40Pin = "analog output"
)
const (
// the LED light is visible above ~1.7V
yl40LedDefaultVal = 1.7
// default refresh rate, set to zero (cyclic reading deactivated)
yl40DefaultRefresh = 0
)
type yl40Sensor struct {
interval time.Duration
scaler func(input int) (value float64)
}
type yl40Config struct {
sensors map[YL40Pin]*yl40Sensor
aOutScaler func(input float64) (value int)
}
var yl40Pins = map[YL40Pin]string{
YL40Bri: "s.0",
YL40Temp: "s.1",
YL40AIN2: "s.2",
YL40Poti: "s.3",
YL40AOUT: "aOut.0",
}
// YL40Driver is a Gobot i2c bus driver for the YL-40 module with light dependent resistor (LDR), thermistor (NTC)
// and an potentiometer, one additional analog input and one analog output with an connected LED.
// The module is based on PCF8591 with 4xADC, 1xDAC. For detailed documentation refer to PCF8591Driver.
//
// All values are linear scaled to 3.3V by default. This can be changed, see example "tinkerboard_yl40.go".
//
// This driver was tested with Tinkerboard and this board with temperature & brightness sensor:
// https://www.makershop.de/download/YL_40_yl40.pdf
//
type YL40Driver struct {
*PCF8591Driver
conf yl40Config
aBri *aio.AnalogSensorDriver
aTemp *aio.TemperatureSensorDriver
aAIN2 *aio.AnalogSensorDriver
aPoti *aio.AnalogSensorDriver
aOut *aio.AnalogActuatorDriver
}
// NewYL40Driver creates a new driver with specified i2c interface
// Params:
// conn Connector - the Adaptor to use with this Driver
//
// Optional parameters:
// refer to PCF8591Driver for i2c specific options
// refer to TemperatureSensorDriver for temperature sensor specific options
// refer to AnalogSensorDriver for analog input specific options
// refer to AnalogActuatorDriver for analog output specific options
//
func NewYL40Driver(a Connector, options ...func(Config)) *YL40Driver {
options = append(options, WithAddress(yl40DefaultAddress))
pcf := NewPCF8591Driver(a, options...)
ntc := aio.TemperatureSensorNtcConf{TC0: 25, R0: 10000.0, B: 3950} //Ohm, R25=10k, B=3950
defTempScaler := aio.TemperatureSensorNtcScaler(255, 1000, true, ntc)
defConf := yl40Config{
sensors: map[YL40Pin]*yl40Sensor{
YL40Bri: {
interval: yl40DefaultRefresh,
scaler: aio.AnalogSensorLinearScaler(0, 255, 1000, 0),
},
YL40Temp: {
interval: yl40DefaultRefresh,
scaler: defTempScaler,
},
YL40AIN2: {
interval: yl40DefaultRefresh,
scaler: aio.AnalogSensorLinearScaler(0, 255, 0, 3.3),
},
YL40Poti: {
interval: yl40DefaultRefresh,
scaler: aio.AnalogSensorLinearScaler(0, 255, 100, -100),
},
},
aOutScaler: aio.AnalogActuatorLinearScaler(0, 3.3, 0, 255),
}
y := &YL40Driver{
PCF8591Driver: pcf,
conf: defConf,
}
y.SetName(gobot.DefaultName("YL-40"))
for _, option := range options {
option(y)
}
// initialize analog drivers
y.aBri = aio.NewAnalogSensorDriver(pcf, yl40Pins[YL40Bri], y.conf.sensors[YL40Bri].interval)
y.aTemp = aio.NewTemperatureSensorDriver(pcf, yl40Pins[YL40Temp], y.conf.sensors[YL40Temp].interval)
y.aAIN2 = aio.NewAnalogSensorDriver(pcf, yl40Pins[YL40AIN2], y.conf.sensors[YL40AIN2].interval)
y.aPoti = aio.NewAnalogSensorDriver(pcf, yl40Pins[YL40Poti], y.conf.sensors[YL40Poti].interval)
y.aOut = aio.NewAnalogActuatorDriver(pcf, yl40Pins[YL40AOUT])
// set input scalers
y.aBri.SetScaler(y.conf.sensors[YL40Bri].scaler)
y.aTemp.SetScaler(y.conf.sensors[YL40Temp].scaler)
y.aAIN2.SetScaler(y.conf.sensors[YL40AIN2].scaler)
y.aPoti.SetScaler(y.conf.sensors[YL40Poti].scaler)
// set output scaler
y.aOut.SetScaler(y.conf.aOutScaler)
return y
}
// WithYL40Interval option sets the interval for refresh of given pin in YL40 driver
func WithYL40Interval(pin YL40Pin, val time.Duration) func(Config) {
return func(c Config) {
y, ok := c.(*YL40Driver)
if ok {
if sensor, ok := y.conf.sensors[pin]; ok {
sensor.interval = val
}
} else if yl40Debug {
log.Printf("trying to set interval for '%s' refresh for non-YL40Driver %v", pin, c)
}
}
}
// WithYL40InputScaler option sets the input scaler of given input pin in YL40 driver
func WithYL40InputScaler(pin YL40Pin, scaler func(input int) (value float64)) func(Config) {
return func(c Config) {
y, ok := c.(*YL40Driver)
if ok {
if sensor, ok := y.conf.sensors[pin]; ok {
sensor.scaler = scaler
}
} else if yl40Debug {
log.Printf("trying to set input scaler for '%s' for non-YL40Driver %v", pin, c)
}
}
}
// WithYL40OutputScaler option sets the output scaler in YL40 driver
func WithYL40OutputScaler(scaler func(input float64) (value int)) func(Config) {
return func(c Config) {
y, ok := c.(*YL40Driver)
if ok {
y.conf.aOutScaler = scaler
} else if yl40Debug {
log.Printf("trying to set output scaler for '%s' for non-YL40Driver %v", YL40AOUT, c)
}
}
}
// Start initializes the driver
func (y *YL40Driver) Start() (err error) {
// must be the first one
if err := y.PCF8591Driver.Start(); err != nil {
return err
}
if err := y.aBri.Start(); err != nil {
return err
}
if err := y.aTemp.Start(); err != nil {
return err
}
if err := y.aAIN2.Start(); err != nil {
return err
}
if err := y.aPoti.Start(); err != nil {
return err
}
if err := y.aOut.Start(); err != nil {
return err
}
return y.Write(yl40LedDefaultVal)
}
// Halt stops the driver
func (y *YL40Driver) Halt() (err error) {
// we try halt on each device, not stopping on the first error
var errors []string
if err := y.aBri.Halt(); err != nil {
errors = append(errors, err.Error())
}
if err := y.aTemp.Halt(); err != nil {
errors = append(errors, err.Error())
}
if err := y.aAIN2.Halt(); err != nil {
errors = append(errors, err.Error())
}
if err := y.aPoti.Halt(); err != nil {
errors = append(errors, err.Error())
}
if err := y.aOut.Halt(); err != nil {
errors = append(errors, err.Error())
}
// must be the last one
if err := y.PCF8591Driver.Halt(); err != nil {
errors = append(errors, err.Error())
}
if len(errors) > 0 {
return fmt.Errorf("Halt the driver %s", strings.Join(errors, ", "))
}
return nil
}
// Read returns the current reading from the given pin of the driver
// For the analog output pin the last written value is returned
func (y *YL40Driver) Read(pin YL40Pin) (val float64, err error) {
switch pin {
case YL40Bri:
return y.aBri.ReadValue()
case YL40Temp:
return y.aTemp.ReadValue()
case YL40AIN2:
return y.aAIN2.ReadValue()
case YL40Poti:
return y.aPoti.ReadValue()
case YL40AOUT:
return y.aOut.Value(), nil
default:
return 0, fmt.Errorf("Analog reading from pin '%s' not supported", pin)
}
}
// ReadBrightness returns the current reading from brightness pin of the driver
func (y *YL40Driver) ReadBrightness() (val float64, err error) {
return y.Read(YL40Bri)
}
// ReadTemperature returns the current reading from temperature pin of the driver
func (y *YL40Driver) ReadTemperature() (val float64, err error) {
return y.Read(YL40Temp)
}
// ReadAIN2 returns the current reading from analog input pin 2 pin of the driver
func (y *YL40Driver) ReadAIN2() (val float64, err error) {
return y.Read(YL40AIN2)
}
// ReadPotentiometer returns the current reading from potentiometer pin of the driver
func (y *YL40Driver) ReadPotentiometer() (val float64, err error) {
return y.Read(YL40Poti)
}
// Value returns the last read or written value from the given pin of the driver
func (y *YL40Driver) Value(pin YL40Pin) (val float64, err error) {
switch pin {
case YL40Bri:
return y.aBri.Value(), nil
case YL40Temp:
return y.aTemp.Value(), nil
case YL40AIN2:
return y.aAIN2.Value(), nil
case YL40Poti:
return y.aPoti.Value(), nil
case YL40AOUT:
return y.aOut.Value(), nil
default:
return 0, fmt.Errorf("Get analog value from pin '%s' not supported", pin)
}
}
// Brightness returns the last read brightness of the driver
func (y *YL40Driver) Brightness() (val float64, err error) {
return y.Value(YL40Bri)
}
// Temperature returns the last read temperature of the driver
func (y *YL40Driver) Temperature() (val float64, err error) {
return y.Value(YL40Temp)
}
// AIN2 returns the last read analog input value of the driver
func (y *YL40Driver) AIN2() (val float64, err error) {
return y.Value(YL40AIN2)
}
// Potentiometer returns the last read potentiometer value of the driver
func (y *YL40Driver) Potentiometer() (val float64, err error) {
return y.Value(YL40Poti)
}
// AOUT returns the last written value of the driver
func (y *YL40Driver) AOUT() (val float64, err error) {
return y.Value(YL40AOUT)
}
// Write writes the given value to the analog output
func (y *YL40Driver) Write(val float64) (err error) {
return y.aOut.Write(val)
}