forked from mstahl/tsl2591
-
Notifications
You must be signed in to change notification settings - Fork 1
/
tsl2591.go
250 lines (200 loc) · 5.43 KB
/
tsl2591.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
//Package tsl2591 interacts with TSL2591 lux sensors
//
// Heavily inspired by https://github.com/mstahl/tsl2591
// and https://github.com/adafruit/Adafruit_TSL2591_Library/
// as well as https://github.com/adafruit/Adafruit_TSL2591_Library/blob/master/Adafruit_TSL2591.cpp
package tsl2591
import (
"encoding/binary"
"errors"
"fmt"
"math"
"periph.io/x/periph/conn/i2c"
"periph.io/x/periph/conn/i2c/i2creg"
"periph.io/x/periph/host"
)
// Opts holds various configuration options for the sensor
type Opts struct {
Gain byte
Timing byte
}
// TSL2591 holds board setup detail
type TSL2591 struct {
enabled bool
timing byte
gain byte
dev i2c.Dev
}
// NewTSL2591 sets up a TSL2591 chip via the I2C protocol, sets its gain and timing
// attributes, and returns an error if any occurred in that process or if the
// TSL2591 was not found
func NewTSL2591(opts *Opts) (*TSL2591, error) {
// Make sure periph is initialized.
if _, err := host.Init(); err != nil {
return nil, err
}
// Open the first available I²C bus:
bus, err := i2creg.Open("")
if err != nil {
return nil, err
}
// Address the device with address TSL2591_ADDR on the I²C bus:
dev := i2c.Dev{Addr: Addr, Bus: bus}
tsl := &TSL2591{
dev: dev,
}
// Read the device ID from the TSL2591. It should be 0x50
write := []byte{CommandBit | RegisterDeviceID}
read := make([]byte, 1)
if err := tsl.dev.Tx(write, read); err != nil {
return nil, err
}
if read[0] != 0x50 {
fmt.Printf("%v\n", read)
return nil, errors.New("can't find a TSL2591 on I2C bus")
}
if err = tsl.SetTiming(opts.Timing); err != nil {
return nil, err
}
if err = tsl.SetGain(opts.Gain); err != nil {
return nil, err
}
if err = tsl.Enable(); err != nil {
return nil, err
}
return tsl, nil
}
// Enable enables the TSL2591 chip
func (tsl *TSL2591) Enable() error {
write := []byte{CommandBit | RegisterEnable |
EnablePowerOn | EnableAEN | EnableAIEN | EnableNPIEN}
if _, err := tsl.dev.Write(write); err != nil {
return err
}
tsl.enabled = true
return nil
}
// Disable disables the TSL2591 chip
func (tsl *TSL2591) Disable() error {
write := []byte{CommandBit | RegisterEnable | EnablePowerOff}
if _, err := tsl.dev.Write(write); err != nil {
return err
}
tsl.enabled = false
return nil
}
// SetGain sets TSL2591 gain. Chip is enabled, gain set, then disabled
func (tsl *TSL2591) SetGain(gain byte) error {
if err := tsl.Enable(); err != nil {
return err
}
write := []byte{CommandBit | RegisterEnable | tsl.timing | gain}
if _, err := tsl.dev.Write(write); err != nil {
return err
}
if err := tsl.Disable(); err != nil {
return err
}
tsl.gain = gain
return nil
}
// SetTiming sets TSL2591 timing. Chip is enabled, timing set, then disabled
func (tsl *TSL2591) SetTiming(timing byte) error {
if err := tsl.Enable(); err != nil {
return err
}
write := []byte{CommandBit | RegisterEnable | tsl.gain | timing}
if _, err := tsl.dev.Write(write); err != nil {
return err
}
if err := tsl.Disable(); err != nil {
return err
}
tsl.timing = timing
return nil
}
// readU16 reads a 16-bit little-endian unsigned value from the specified 8-bit address
func (tsl *TSL2591) readU16(address byte) (uint16, error) {
readBuffer := make([]byte, 2)
cmd := []byte{CommandBit | address}
if err := tsl.dev.Tx(cmd, readBuffer); err != nil {
return 0, err
}
return binary.LittleEndian.Uint16(readBuffer), nil
}
// RawLuminosity reads from the sensor
func (tsl *TSL2591) RawLuminosity() (uint16, uint16, error) {
// The first value is IR + visible luminosity (channel 0)
// and the second is the IR only (channel 1). Both values
// are 16-bit unsigned numbers (0-65535)
c0, err := tsl.readU16(RegisterChan0Low)
if err != nil {
return 0, 0, err
}
c1, err := tsl.readU16(RegisterChan1Low)
if err != nil {
return 0, 0, err
}
return c0, c1, nil
}
// FullSpectrum returns the full spectrum value
func (tsl *TSL2591) FullSpectrum() (uint32, error) {
// Full spectrum (IR + visible) light and return its value
// as a 32-bit unsigned number
c0, c1, err := tsl.RawLuminosity()
if err != nil {
return 0, nil
}
return uint32(c1)<<16 | uint32(c0), nil
}
// Infrared returns infrared value
func (tsl *TSL2591) Infrared() (uint16, error) {
_, c1, err := tsl.RawLuminosity()
if err != nil {
return 0, nil
}
return c1, nil
}
// Visible returns visible value
func (tsl *TSL2591) Visible() (uint32, error) {
_, c1, err := tsl.RawLuminosity()
if err != nil {
return 0, nil
}
full := uint32(c1)<<16 | uint32(c1)
return full - uint32(c1), nil
}
// Lux calculates a lux value from both the infrared and visible channels
func (tsl *TSL2591) Lux() (float64, error) {
c0, c1, err := tsl.RawLuminosity()
if err != nil {
return 0, nil
}
// Compute the atime in milliseconds
atime := 100*uint16(tsl.timing) + 100
// Set the maximum sensor counts based on the integration time (atime) setting
var maxCounts uint16
if tsl.timing == Integrationtime100MS {
maxCounts = MaxCount100ms
} else {
maxCounts = MaxCount
}
// Handle overflow.
if c0 >= maxCounts || c1 >= maxCounts {
return 0, errors.New("overflow reading light channels")
}
// Calculate lux
again := uint16(1)
switch tsl.gain {
case GainMed:
again = 25
case GainHigh:
again = 428
case GainMax:
again = 9876
}
cpl := float64((atime * again)) / LuxDF
lux1 := (float64(c0) - (LuxCoefB * float64(c1))) / cpl
lux2 := ((LuxCoefC * float64(c0)) - (LuxCoefD * float64(c1))) / cpl
return math.Max(lux1, lux2), nil
}