-
Notifications
You must be signed in to change notification settings - Fork 30
/
_P015_TLS2561.ino
360 lines (316 loc) · 11.6 KB
/
_P015_TLS2561.ino
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
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
//#######################################################################################################
//######################## Plugin 015 TSL2561 I2C Lux Sensor ############################################
//#######################################################################################################
// 13-10-2015 Charles-Henri Hallard, see my projects and blog at https://hallard.me
#define PLUGIN_015
#define PLUGIN_ID_015 15
#define PLUGIN_NAME_015 "Luminosity - TLS2561"
#define PLUGIN_VALUENAME1_015 "Lux"
boolean Plugin_015_init = false;
// ======================================
// TSL2561 luminosity sensor
// ======================================
#define TSL2561_I2C_ADDRESS 0x39 // I2C address for the sensor
#define TSL2561_CONTROL 0x80
#define TSL2561_TIMING 0x81
#define TSL2561_INTERRUPT 0x86
#define TSL2561_CHANNEL_0L 0x8C
#define TSL2561_CHANNEL_0H 0x8D
#define TSL2561_CHANNEL_1L 0x8E
#define TSL2561_CHANNEL_1H 0x8F
// Control register bits
#define TSL2561_POWER_UP 0x03
#define TSL2561_POWER_DOWN 0x00
// Timing register bits
#define TSL2561_TIMING_13MS 0x00
#define TSL2561_TIMING_101MS 0x01
#define TSL2561_TIMING_402MS 0x02
#define TSL2561_TIMING_CUSTOM_STOP 0x03
#define TSL2561_TIMING_CUSTOM_START 0x0B
#define TSL2561_LUX_SCALE 14 // scale by 2^14
#define TSL2561_RATIO_SCALE 9 // scale ratio by 2^9
#define TSL2561_CH_SCALE 10 // scale channel values by 2^10
#define TSL2561_CHSCALE_TINT_13MS 0x7517 // 322/11 * 2^CH_SCALE (13ms)
#define TSL2561_CHSCALE_TINT_60MS 0x1800 // 322/48 * 2^CH_SCALE (60ms)
#define TSL2561_CHSCALE_TINT_101MS 0x0fe7 // 322/81 * 2^CH_SCALE (101ms)
#define TSL2561_CHSCALE_TINT_120MS 0x0D6B // 322/96 * 2^CH_SCALE (120ms)
#define TSL2561_CHSCALE_TINT_402MS (1 << TSL2561_CH_SCALE) // default No scaling
// Clipping thresholds
#define TSL2561_CLIPPING_13MS (4900)
#define TSL2561_CLIPPING_101MS (37000)
#define TSL2561_CLIPPING_402MS (65000)
#define TSL2561_K1T 0x0040 // 0.125 * 2^RATIO_SCALE
#define TSL2561_B1T 0x01f2 // 0.0304 * 2^LUX_SCALE
#define TSL2561_M1T 0x01be // 0.0272 * 2^LUX_SCALE
#define TSL2561_K2T 0x0080 // 0.250 * 2^RATIO_SCA
#define TSL2561_B2T 0x0214 // 0.0325 * 2^LUX_SCALE
#define TSL2561_M2T 0x02d1 // 0.0440 * 2^LUX_SCALE
#define TSL2561_K3T 0x00c0 // 0.375 * 2^RATIO_SCALE
#define TSL2561_B3T 0x023f // 0.0351 * 2^LUX_SCALE
#define TSL2561_M3T 0x037b // 0.0544 * 2^LUX_SCALE
#define TSL2561_K4T 0x0100 // 0.50 * 2^RATIO_SCALE
#define TSL2561_B4T 0x0270 // 0.0381 * 2^LUX_SCALE
#define TSL2561_M4T 0x03fe // 0.0624 * 2^LUX_SCALE
#define TSL2561_K5T 0x0138 // 0.61 * 2^RATIO_SCALE
#define TSL2561_B5T 0x016f // 0.0224 * 2^LUX_SCALE
#define TSL2561_M5T 0x01fc // 0.0310 * 2^LUX_SCALE
#define TSL2561_K6T 0x019a // 0.80 * 2^RATIO_SCALE
#define TSL2561_B6T 0x00d2 // 0.0128 * 2^LUX_SCALE
#define TSL2561_M6T 0x00fb // 0.0153 * 2^LUX_SCALE
#define TSL2561_K7T 0x029a // 1.3 * 2^RATIO_SCALE
#define TSL2561_B7T 0x0018 // 0.00146 * 2^LUX_SCALE
#define TSL2561_M7T 0x0012 // 0.00112 * 2^LUX_SCALE
#define TSL2561_K8T 0x029a // 1.3 * 2^RATIO_SCALE
#define TSL2561_B8T 0x0000 // 0.000 * 2^LUX_SCALE
#define TSL2561_M8T 0x0000 // 0.000 * 2^LUX_SCALE
uint16_t tsl2561_lux; // latest lux value read
boolean Plugin_015(byte function, struct EventStruct *event, String& string)
{
boolean success = false;
switch (function)
{
case PLUGIN_DEVICE_ADD:
{
Device[++deviceCount].Number = PLUGIN_ID_015;
Device[deviceCount].Type = DEVICE_TYPE_I2C;
Device[deviceCount].VType = SENSOR_TYPE_SINGLE;
Device[deviceCount].Ports = 0;
Device[deviceCount].PullUpOption = false;
Device[deviceCount].InverseLogicOption = false;
Device[deviceCount].FormulaOption = true;
Device[deviceCount].ValueCount = 1;
Device[deviceCount].SendDataOption = true;
Device[deviceCount].TimerOption = true;
Device[deviceCount].GlobalSyncOption = true;
break;
}
case PLUGIN_GET_DEVICENAME:
{
string = F(PLUGIN_NAME_015);
break;
}
case PLUGIN_GET_DEVICEVALUENAMES:
{
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_015));
break;
}
case PLUGIN_WEBFORM_LOAD:
{
#define TLS2561_INTEGRATION_OPTION 3
byte choice = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
String options[TLS2561_INTEGRATION_OPTION];
int optionValues[TLS2561_INTEGRATION_OPTION];
optionValues[0] = TSL2561_TIMING_13MS;
options[0] = F("13 ms");
optionValues[1] = TSL2561_TIMING_101MS;
options[1] = F("101 ms");
optionValues[2] = TSL2561_TIMING_402MS;
options[2] = F("402 ms");
string += F("<TR><TD>Integration time:<TD><select name='plugin_015_integration'>");
for (byte x = 0; x < TLS2561_INTEGRATION_OPTION; x++)
{
string += F("<option value='");
string += optionValues[x];
string += "'";
if (choice == optionValues[x])
string += F(" selected");
string += ">";
string += options[x];
string += F("</option>");
}
string += F("</select>");
success = true;
break;
}
case PLUGIN_WEBFORM_SAVE:
{
String plugin1 = WebServer.arg(F("plugin_015_integration"));
Settings.TaskDevicePluginConfig[event->TaskIndex][0] = plugin1.toInt();
Plugin_015_init = false; // Force device setup next time
success = true;
break;
}
case PLUGIN_READ:
{
// Get sensor resolution configuration
uint8_t integration = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
uint8_t ret;
if (!Plugin_015_init) {
Plugin_015_init = Plugin_015_tls2561_begin(integration);
}
// Read values if init ok
if (Plugin_015_init) {
ret = Plugin_015_tsl2561_calcLux(integration);
if (ret == 0) {
UserVar[event->BaseVarIndex] = tsl2561_lux;
success = true;
String log = F("TLS2561 : Lux: ");
log += UserVar[event->BaseVarIndex];
addLog(LOG_LEVEL_INFO,log);
} else {
String log = F("TLS2561 : Read Error #");
log += String(ret,DEC);
addLog(LOG_LEVEL_INFO,log);
}
}
break;
}
}
return success;
}
/* ======================================================================
Function: Plugin_015_tls2561_begin
Purpose : read the user register from the sensor
Input : integration time
Output : true if okay
Comments: -
====================================================================== */
boolean Plugin_015_tls2561_begin(uint8_t integration)
{
uint8_t ret;
// Power UP device
ret = Plugin_015_tsl2561_writeRegister(TSL2561_CONTROL, TSL2561_POWER_UP);
if ( ret == 0 )
{
// I noticed 1st calculation after power up could be hazardous; so
// do a 1st dummy reading, with speed integration time, here 13ms
Plugin_015_tsl2561_writeRegister(TSL2561_TIMING, TSL2561_TIMING_13MS);
delay(15);
ret = true;
} else {
String log = F("TLS2561 : integration=0x");
log += String(integration,HEX);
log += F(" => Error 0x");
log += String(ret,HEX);
addLog(LOG_LEVEL_INFO,log);
ret = false;
}
return ret;
}
/* ======================================================================
Function: Plugin_015_tsl2561_readRegister
Purpose : read a register from the sensor
Input : register address
register value filled by function
Output : 0 if okay
Comments: -
====================================================================== */
uint8_t Plugin_015_tsl2561_readRegister(uint8_t reg, uint8_t * value)
{
Wire.beginTransmission(TSL2561_I2C_ADDRESS);
Wire.write(reg);
// all was fine ?
if ( Wire.endTransmission()==0 ) {
// request 1 byte and have it ?
if (Wire.requestFrom(TSL2561_I2C_ADDRESS, 1)==1) {
// return value
*value = Wire.read();
return 0;
}
}
return 1;
}
/* ======================================================================
Function: Plugin_015_tsl2561_writeRegister
Purpose : read a register from the sensor
Input : register address
Output : register value
Comments: 0 if okay
====================================================================== */
uint8_t Plugin_015_tsl2561_writeRegister(uint8_t reg, uint8_t value)
{
Wire.beginTransmission(TSL2561_I2C_ADDRESS);
Wire.write(reg);
Wire.write(value);
return (Wire.endTransmission());
}
/* ======================================================================
Function: Plugin_015_tsl2561_calcLux
Purpose : start a conversion and return calculated lux
Input : integration time
Output : 0 if calculated value ok and updated
Comments: global lux value is updated
====================================================================== */
int8_t Plugin_015_tsl2561_calcLux(uint8_t integration)
{
unsigned long chScale;
unsigned long channel0, channel1;
unsigned long ratio, ratio1;
unsigned long lux;
unsigned int b, m;
uint16_t ch0,ch1;
uint16_t clipThreshold;
uint8_t msb, lsb;
uint8_t err = 0;
// do start calculation with speed integration time,
Plugin_015_tsl2561_writeRegister(TSL2561_TIMING, integration);
if (integration == TSL2561_TIMING_402MS ) {
chScale = TSL2561_CHSCALE_TINT_402MS ;
clipThreshold = TSL2561_CLIPPING_402MS ;
delay(405);
} else if (integration == TSL2561_TIMING_101MS ) {
chScale = TSL2561_CHSCALE_TINT_101MS ;
clipThreshold = TSL2561_CLIPPING_101MS ;
delay(103);
} else {
chScale = TSL2561_CHSCALE_TINT_13MS ;
clipThreshold = TSL2561_CLIPPING_13MS ;
delay(15);
}
// don't try to change reading order of LOW/HIGH, it will not work !!!!
// you must read LOW then HIGH
err |= Plugin_015_tsl2561_readRegister(TSL2561_CHANNEL_0L, &lsb);
err |= Plugin_015_tsl2561_readRegister(TSL2561_CHANNEL_0H, &msb);
ch0 = word(msb,lsb);
err |= Plugin_015_tsl2561_readRegister(TSL2561_CHANNEL_1L, &lsb);
err |= Plugin_015_tsl2561_readRegister(TSL2561_CHANNEL_1H, &msb);
ch1 = word(msb,lsb);;
// I2C error ?
if( err )
return -2;
/* Sensor saturated the lux is not valid in this situation */
if ((ch0 > clipThreshold) || (ch1 > clipThreshold))
{
return -1;
}
// gain is 1 so put it to 16X
chScale <<= 4;
// scale the channel values
channel0 = (ch0 * chScale) >> TSL2561_CH_SCALE;
channel1 = (ch1 * chScale) >> TSL2561_CH_SCALE;
ratio1 = 0;
if (channel0!= 0)
ratio1 = (channel1 << (TSL2561_RATIO_SCALE+1))/channel0;
// round the ratio value
ratio = (ratio1 + 1) >> 1;
// ULPNode have T package
// Adjust constant depending on calculated ratio
if ((ratio >= 0) && (ratio <= TSL2561_K1T))
{b=TSL2561_B1T; m=TSL2561_M1T;}
else if (ratio <= TSL2561_K2T)
{b=TSL2561_B2T; m=TSL2561_M2T;}
else if (ratio <= TSL2561_K3T)
{b=TSL2561_B3T; m=TSL2561_M3T;}
else if (ratio <= TSL2561_K4T)
{b=TSL2561_B4T; m=TSL2561_M4T;}
else if (ratio <= TSL2561_K5T)
{b=TSL2561_B5T; m=TSL2561_M5T;}
else if (ratio <= TSL2561_K6T)
{b=TSL2561_B6T; m=TSL2561_M6T;}
else if (ratio <= TSL2561_K7T)
{b=TSL2561_B7T; m=TSL2561_M7T;}
else if (ratio > TSL2561_K8T)
{b=TSL2561_B8T; m=TSL2561_M8T;}
// datasheet formula
lux=((channel0*b)-(channel1*m));
// do not allow negative lux value
if(lux<0)
lux=0;
// round lsb (2^(LUX_SCALE−1))
lux += (1<<(TSL2561_LUX_SCALE-1));
// strip off fractional portion
lux >>= TSL2561_LUX_SCALE;
// strip off fractional portion
tsl2561_lux = (uint16_t) (lux);
return 0;
}