muwerk mupplet Sensor Library
muwerk applets; mupplets: functional units that support specific hardware or reusable applications for sensors
Loading...
Searching...
No Matches
mup_illuminance_tsl2561.h
1// mup_illuminance_tsl2561.h
2#pragma once
3
4#ifndef tiny_twi_h
5#include <Wire.h>
6#else
7typedef TinyWire TwoWire;
8#endif
9
10#include "scheduler.h"
11#include "sensors.h"
12#include "helper/mup_i2c_registers.h"
13
14namespace ustd {
15
16// clang-format off
114// clang-format on
116 private:
117 String TSL2561_VERSION = "0.1.0";
118 Scheduler *pSched;
119 TwoWire *pWire;
120 I2CRegisters *pI2C;
121
122 int tID;
123 String name;
124
125 public:
126 unsigned long basePollRateUs = 50000; // 50ms;
127 uint32_t pollRateMs = 2000; // 2000ms;
128 uint32_t lastPollMs = 0;
129 enum FilterMode { FAST,
130 MEDIUM,
131 LONGTERM };
132 enum IntegrationMode { FAST13ms,
133 MEDIUM101ms,
134 LONGTERM402ms };
135 enum GainMode { LOW1x,
136 HIGH16x };
137 String firmwareVersion;
138 FilterMode filterMode;
139 IntegrationMode integrationMode;
140 GainMode gainMode;
141 uint8_t i2cAddress;
142
143 double illuminanceValue, unitIlluminanceValue, lightCh0Value, IRCh1Value;
144 double unitIlluminanceSensitivity;
145 ustd::sensorprocessor illuminanceSensor = ustd::sensorprocessor(4, 600, 0.005);
146 ustd::sensorprocessor unitIlluminanceSensor = ustd::sensorprocessor(4, 600, 0.005);
147 ustd::sensorprocessor lightCh0Sensor = ustd::sensorprocessor(4, 600, 0.005);
148 ustd::sensorprocessor IRCh1Sensor = ustd::sensorprocessor(4, 600, 0.005);
149 bool bActive = false;
150
151 IlluminanceTSL2561(String name, FilterMode filterMode = FilterMode::FAST, IntegrationMode integrationMode = IntegrationMode::LONGTERM402ms,
152 GainMode gainMode = GainMode::LOW1x, uint8_t i2cAddress = 0x39)
153 : name(name), filterMode(filterMode), integrationMode(integrationMode), gainMode(gainMode), i2cAddress(i2cAddress) {
159 unitIlluminanceSensitivity = 0.2;
160 pI2C = nullptr;
161 setFilterMode(filterMode, true);
162 }
163
165 if (pI2C) {
166 delete pI2C;
167 }
168 }
169
170 double getIlluminance() {
174 return illuminanceValue;
175 }
176
181 return unitIlluminanceValue;
182 }
183
191 return unitIlluminanceSensitivity;
192 }
193
194 void setUnitIlluminanceSensitivity(double sensitivity) {
205 if (sensitivity <= 0.001) sensitivity = 0.2;
206 unitIlluminanceSensitivity = sensitivity;
207 }
208
209 void begin(Scheduler *_pSched, TwoWire *_pWire = &Wire, uint32_t _pollRateMs = 2000) {
210 pSched = _pSched;
211 pWire = _pWire;
212 pollRateMs = _pollRateMs;
213
214 pWire->begin(); // required!
215 pI2C = new I2CRegisters(pWire, i2cAddress);
216
217 auto ft = [=]() { this->loop(); };
218 tID = pSched->add(ft, name, basePollRateUs); // 1s
219
220 auto fnall = [=](String topic, String msg, String originator) {
221 this->subsMsg(topic, msg, originator);
222 };
223 pSched->subscribe(tID, name + "/sensor/#", fnall);
224
225 pI2C->lastError = pI2C->checkAddress(i2cAddress);
226 if (pI2C->lastError == I2CRegisters::I2CError::OK) {
227 uint8_t id, rev;
228 if (TSLSensorGetRevID(&id, &rev)) {
229 if (id == 5 || id == 1) { // TSL2561 id should be either 5 (reality) or 1 (datasheet)
230 if (TSLSensorPower(true)) {
231 bActive = true;
232#ifdef USE_SERIAL_DBG
233 Serial.println("TSL2561: Powered on, revision: " + String(rev));
234#endif
235 if (TSLSensorGainIntegrationSet()) {
236#ifdef USE_SERIAL_DBG
237 Serial.println("TSL2561: Integration- and Gain-Mode set.");
238#endif
239 } else {
240#ifdef USE_SERIAL_DBG
241 Serial.println("TSL2561: Integration- and Gain-Mode set ERROR.");
242#endif
243 pI2C->lastError = I2CRegisters::I2C_WRITE_ERR_OTHER;
244 }
245 } else {
246#ifdef USE_SERIAL_DBG
247 Serial.println("TSL2561: Power on failed");
248#endif
249 pI2C->lastError = I2CRegisters::I2CError::I2C_WRITE_ERR_OTHER;
250 }
251 } else {
252#ifdef USE_SERIAL_DBG
253 Serial.println("TSL2561: Bad sensor ID: " + String(id) + ", expected: 1, revision: " + String(rev));
254#endif
255 pI2C->lastError = I2CRegisters::I2CError::I2C_WRONG_HARDWARE_AT_ADDRESS;
256 }
257 } else {
258#ifdef USE_SERIAL_DBG
259 Serial.println("TSL2561: Failed to get sensor ID, wrong hardware?");
260#endif
261 pI2C->lastError = I2CRegisters::I2CError::I2C_WRONG_HARDWARE_AT_ADDRESS;
262 }
263 } else {
264#ifdef USE_SERIAL_DBG
265 Serial.println("TSL2561: Failed to check I2C address, wrong address?");
266#endif
267 pI2C->lastError = I2CRegisters::I2CError::I2C_DEVICE_NOT_AT_ADDRESS;
268 }
269 }
270
271 void setFilterMode(FilterMode mode, bool silent = false) {
272 switch (mode) {
273 case FAST:
274 filterMode = FAST;
275 illuminanceSensor.update(1, 2, 0.05); // requires muwerk 0.6.3 API
276 unitIlluminanceSensor.update(1, 2, 0.1);
277 lightCh0Sensor.update(1, 2, 0.1);
278 IRCh1Sensor.update(1, 2, 0.1);
279 break;
280 case MEDIUM:
281 filterMode = MEDIUM;
282 illuminanceSensor.update(4, 30, 0.1);
283 unitIlluminanceSensor.update(4, 30, 0.5);
284 lightCh0Sensor.update(4, 30, 0.5);
285 IRCh1Sensor.update(4, 30, 0.5);
286 break;
287 case LONGTERM:
288 default:
289 filterMode = LONGTERM;
290 illuminanceSensor.update(10, 600, 0.1);
291 unitIlluminanceSensor.update(50, 600, 0.5);
292 lightCh0Sensor.update(50, 600, 0.5);
293 IRCh1Sensor.update(50, 600, 0.5);
294 break;
295 }
296 if (!silent)
297 publishFilterMode();
298 }
299
300 void setGainIntegrationMode(GainMode gMode, IntegrationMode iMode, bool silent = false) {
301 gainMode = gMode;
302 integrationMode = iMode;
303 TSLSensorGainIntegrationSet();
304 if (!silent) {
305 publishGainMode();
306 publishIntegrationMode();
307 }
308 }
309
310 void setIntegrationMode(IntegrationMode mode, bool silent = false) {
311 integrationMode = mode;
312 TSLSensorGainIntegrationSet();
313 if (!silent)
314 publishIntegrationMode();
315 }
316
317 void setGainMode(GainMode mode, bool silent = false) {
318 gainMode = mode;
319 TSLSensorGainIntegrationSet();
320 if (!silent)
321 publishGainMode();
322 }
323
324 private:
325 void publishLightCh0() {
326 char buf[32];
327 sprintf(buf, "%6.3f", lightCh0Value);
328 pSched->publish(name + "/sensor/lightch0", buf);
329 }
330
331 void publishIRCh1() {
332 char buf[32];
333 sprintf(buf, "%6.3f", IRCh1Value);
334 pSched->publish(name + "/sensor/irch1", buf);
335 }
336
337 void publishIlluminance() {
338 char buf[32];
339 sprintf(buf, "%6.3f", illuminanceValue);
340 pSched->publish(name + "/sensor/illuminance", buf);
341 }
342
343 void publishUnitIlluminance() {
344 char buf[32];
345 sprintf(buf, "%6.3f", unitIlluminanceValue);
346 pSched->publish(name + "/sensor/unitilluminance", buf);
347 }
348
349 void publishUnitIlluminanceSensitivity() {
350 char buf[32];
351 sprintf(buf, "%6.3f", unitIlluminanceSensitivity);
352 pSched->publish(name + "/sensor/unitilluminancesensitivity", buf);
353 }
354
355 void publishError(String errMsg) {
356 pSched->publish(name + "/sensor/error", errMsg);
357 }
358
359 void publishFilterMode() {
360 switch (filterMode) {
361 case FilterMode::FAST:
362 pSched->publish(name + "/sensor/mode", "FAST");
363 break;
364 case FilterMode::MEDIUM:
365 pSched->publish(name + "/sensor/mode", "MEDIUM");
366 break;
367 case FilterMode::LONGTERM:
368 pSched->publish(name + "/sensor/mode", "LONGTERM");
369 break;
370 }
371 }
372
373 void publishIntegrationMode() {
374 switch (integrationMode) {
375 case IntegrationMode::FAST13ms:
376 pSched->publish(name + "/sensor/integration", "FAST");
377 break;
378 case IntegrationMode::MEDIUM101ms:
379 pSched->publish(name + "/sensor/integration", "MEDIUM");
380 break;
381 case IntegrationMode::LONGTERM402ms:
382 pSched->publish(name + "/sensor/integration", "LONGTERM");
383 break;
384 }
385 }
386
387 void publishGainMode() {
388 switch (gainMode) {
389 case GainMode::LOW1x:
390 pSched->publish(name + "sensor/gain", "LOW");
391 break;
392 case GainMode::HIGH16x:
393 pSched->publish(name + "sensor/gain", "HIGH");
394 break;
395 }
396 }
397
398 bool TSLSensorPower(bool powerOn) {
399 if (powerOn) {
400 if (!pI2C->writeRegisterByte(0x80, 0x03, true)) return false;
401 } else {
402 if (!pI2C->writeRegisterByte(0x80, 0x00, true)) return false;
403 }
404 return true;
405 }
406
407 bool TSLSensorGetRevID(uint8_t *pId, uint8_t *pRev) {
413 *pId = 0;
414 *pRev = 0;
415 uint8_t idbyte = 0;
416 if (!pI2C->readRegisterByte(0x8a, &idbyte)) return false;
417 *pId = idbyte >> 4;
418 *pRev = idbyte & 0x0f;
419 return true;
420 }
421
422 bool TSLSensorGainIntegrationSet() {
423 uint8_t mx = 0;
424 switch (gainMode) {
425 case GainMode::LOW1x:
426 mx = 0x00;
427 break;
428 case GainMode::HIGH16x:
429 mx = 0x10;
430 }
431 switch (integrationMode) {
432 case IntegrationMode::FAST13ms:
433 mx |= 0x00;
434 break;
435 case IntegrationMode::MEDIUM101ms:
436 mx |= 0x01;
437 break;
438 case IntegrationMode::LONGTERM402ms:
439 mx |= 0x02;
440 }
441 return pI2C->writeRegisterByte(0x81, mx, true);
442 }
443
444 double calculateLux(uint16_t ch0, uint16_t ch1) {
450 if (ch0 == 0) return 0;
451 if (ch0 > 65000 || ch1 > 65000) return 10000.0; // overflow of sensor.
452 double lux;
453 double ratio = (double)ch1 / (double)ch0;
454 if (ratio <= 0.5) {
455 lux = 0.0304 * (double)ch0 - 0.062 * (double)ch0 * pow(ratio, 1.4);
456 } else if (ratio <= 0.61) {
457 lux = 0.0224 * (double)ch0 - 0.031 * (double)ch1;
458 } else if (ratio <= 0.80) {
459 lux = 0.0128 * (double)ch0 - 0.0153 * (double)ch1;
460 } else if (ratio <= 1.30) {
461 lux = 0.00146 * (double)ch0 - 0.00112 * (double)ch1;
462 } else {
463 lux = 0;
464 }
465 return lux;
466 }
467
468 bool readTSLSensorMeasurement(uint8_t reg, double *pMeas) {
469 uint16_t data;
470 *pMeas = 0.0;
471 if (!pI2C->readRegisterWordLE(reg, &data, true)) {
472#ifdef USE_SERIAL_DBG
473 Serial.print("Failed to read TSL2561 at address 0x");
474 Serial.print(i2cAddress, HEX);
475 Serial.print(" data: ");
476 Serial.print(data, HEX);
477 Serial.print(" lasterr: ");
478 Serial.println(pI2C->lastError, HEX);
479#endif
480 return false;
481 } else {
482 *pMeas = (double)data;
483#ifdef USE_SERIAL_DBG
484 Serial.println("TSL2561 Measurement: " + String(data));
485#endif
486 return true;
487 }
488 }
489
490 bool readTSLSensor(double *pIlluminanceCh0, double *pIlluminanceCh1,
491 double *pLux, double *pUnitIlluminance) {
492 *pIlluminanceCh0 = 0.0;
493 *pIlluminanceCh1 = 0.0;
494 *pLux = 0.0;
495 *pUnitIlluminance = 0.0;
496 if (!readTSLSensorMeasurement(0xac, pIlluminanceCh0)) return false;
497 if (!readTSLSensorMeasurement(0xae, pIlluminanceCh1)) return false;
498 *pLux = calculateLux(*pIlluminanceCh0, *pIlluminanceCh1);
499 double ui;
500 if (*pLux > 1.0) {
501 ui = log(*pLux) * unitIlluminanceSensitivity;
502 } else {
503 ui = 0.0;
504 }
505 if (ui < 0.0) ui = 0.0;
506 if (ui > 1.0) ui = 1.0;
507 *pUnitIlluminance = ui;
508 return true;
509 }
510
511 void loop() {
512 double illuminanceVal, unitIlluminanceVal, lightCh0Val, IRCh1Val;
513 if (timeDiff(lastPollMs, millis()) > pollRateMs) {
514 lastPollMs = millis();
515 if (bActive) {
516 if (readTSLSensor(&lightCh0Val, &IRCh1Val, &illuminanceVal, &unitIlluminanceVal)) {
517 if (lightCh0Sensor.filter(&lightCh0Val)) {
518 lightCh0Value = lightCh0Val;
519 publishLightCh0();
520 }
521 if (IRCh1Sensor.filter(&IRCh1Val)) {
522 IRCh1Value = IRCh1Val;
523 publishIRCh1();
524 }
525 if (illuminanceSensor.filter(&illuminanceVal)) {
526 illuminanceValue = illuminanceVal;
527 publishIlluminance();
528 }
529 if (unitIlluminanceSensor.filter(&unitIlluminanceVal)) {
530 unitIlluminanceValue = unitIlluminanceVal;
531 publishUnitIlluminance();
532 }
533 }
534 }
535 }
536 }
537
538 void subsMsg(String topic, String msg, String originator) {
539 if (topic == name + "/sensor/illuminance/get") {
540 publishIlluminance();
541 } else if (topic == name + "/sensor/unitilluminance/get") {
542 publishUnitIlluminance();
543 } else if (topic == name + "/sensor/lightch0/get") {
544 publishLightCh0();
545 } else if (topic == name + "/sensor/irch1/get") {
546 publishIRCh1();
547 } else if (topic == name + "/sensor/mode/get") {
548 publishFilterMode();
549 } else if (topic == name + "/sensor/mode/set") {
550 if (msg == "fast" || msg == "FAST") {
551 setFilterMode(FilterMode::FAST);
552 } else {
553 if (msg == "medium" || msg == "MEDIUM") {
554 setFilterMode(FilterMode::MEDIUM);
555 } else {
556 setFilterMode(FilterMode::LONGTERM);
557 }
558 }
559 } else if (topic == name + "/sensor/integration/get") {
560 publishIntegrationMode();
561 } else if (topic == name + "/sensor/integration/set") {
562 if (msg == "fast" || msg == "FAST") {
563 setIntegrationMode(IntegrationMode::FAST13ms);
564 } else {
565 if (msg == "medium" || msg == "MEDIUM") {
566 setIntegrationMode(IntegrationMode::MEDIUM101ms);
567 } else {
568 setIntegrationMode(IntegrationMode::LONGTERM402ms);
569 }
570 }
571 } else if (topic == name + "/sensor/gain/get") {
572 publishGainMode();
573 } else if (topic == name + "/sensor/gain/set") {
574 if (msg == "low" || msg == "LOW") {
575 setGainMode(GainMode::LOW1x);
576 } else {
577 setGainMode(GainMode::HIGH16x);
578 }
579 } else if (topic == name + "/sensor/unitilluminancesensitivity/set") {
580 setUnitIlluminanceSensitivity(msg.toFloat());
581 } else if (topic == name + "/sensor/unitilluminancesensitivity/get") {
582 publishUnitIlluminanceSensitivity();
583 }
584 }
585
586}; // IlluminanceTSL2561
587
588} // namespace ustd
mupplet-sensor luminance with TSL2561
Definition: mup_illuminance_tsl2561.h:115
double getUnitIlluminanceSensitivity()
Definition: mup_illuminance_tsl2561.h:184
IlluminanceTSL2561(String name, FilterMode filterMode=FilterMode::FAST, IntegrationMode integrationMode=IntegrationMode::LONGTERM402ms, GainMode gainMode=GainMode::LOW1x, uint8_t i2cAddress=0x39)
Definition: mup_illuminance_tsl2561.h:151
void setUnitIlluminanceSensitivity(double sensitivity)
Definition: mup_illuminance_tsl2561.h:194
double getUnitIlluminance()
Definition: mup_illuminance_tsl2561.h:177
double getIlluminance()
Definition: mup_illuminance_tsl2561.h:170