/* xsns_99_luxv30b.ino - Driver for DFRobot V30B lux sensor Copyright (C) 2022 Marius Bezuidenhout This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifdef USE_I2C #ifdef USE_LUXV30B /************************************************************************************************** * DFRobot SEN0390 V30B ambient light sensor * * https://wiki.dfrobot.com/Ambient_Light_Sensor_0_200klx_SKU_SEN0390 * https://github.com/DFRobot/DFRobot_B_LUX_V30B/ * * I2C Address: 0x4A * ================================================================================================ * This driver use the I2C mode. Uses address 0x94 and 0x95. Its address cannot be changed. * * Supply Voltage: 2.7-6V * Operating Current: 0.7mA * Detection Range: 0-200klx * Accuracy: 0.054lx * Operating Temperature Range: -40°C~+85°C * I2C Address: 0x4A * * Pin assignments: * * ------------------------------------------ * | Num | Label | Description | * 1 VCC 2.7-6V + * 2 SCL I2C Serial Clock Line * 3 SDA I2C Serial Data * 4 GND Ground * 5 EN Sensor Chip-select Enable/Disable port, High to enable, Low to disable sensor * * * You can write the desired configuration to the configuration register(address:0x04), setting * different acquisition accuracy. * You can read the light intensity data from the data register(address:0x00~0x03). * ------------------------------------------------------------------------------------------ * | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | * ------------------------------------------------------------------------------------------ * | 0 | MANUAL | 0 | 0 | CDR | TIM | * ------------------------------------------------------------------------------------------ * MANUAL :Manual configuration register. * 0 represents the default automatic mode.In this mode ,CDR and TIM are automatically assigned. * 1 represents the configuration of manual mode.In this mode,CDR and TIM can be set by the user. * CDR :Shunt ratio register. * 0 represents the default of not dividing,all the current of the photodiode into the ADC * 1 represents the division of 8,as long as 1/8 of the current of the photodiode changes to ADC. * This mode is used in high brightness situations. * TIM[2:0]:Acquisition time. * ------------------------------------------------------------------------------------------------ * TIM[2:0] | TIME(ms) | Introduction | * ------------------------------------------------------------------------------------------------ * 000 | 800 | Preferred mode in low light environment | * ------------------------------------------------------------------------------------------------ * 001 | 400 | --- | * ------------------------------------------------------------------------------------------------ * 010 | 200 | --- | * ------------------------------------------------------------------------------------------------ * 011 | 100 | In the strong light environment, select the mode preferentially | * ------------------------------------------------------------------------------------------------ * 100 | 50 | Manual mode only | * ------------------------------------------------------------------------------------------------ * 101 | 250 | Manual mode only | * ------------------------------------------------------------------------------------------------ * 110 | 12.5 | Manual mode only | * ------------------------------------------------------------------------------------------------ * 111 | 6.25 | Manual mode only | * ------------------------------------------------------------------------------------------------ * Accuracy that can be set in manual mode: * ------------------------------------------------------------------------------------------------------------- * | Light conditions | | TIM & CDR | * ------------------------------------------------------------------------------------------------------------- * | Minimum accuracy | Maximum accuracy | Maximum | Acquisition time(ms) | TIM | CDR | * —------------------------------------------------------------------------------------------------------------ * 0.054 11.52 2938 800 000 0 * 0.09 23.04 5875 400 001 0 * 0.18 46.08 11750 200 010 0 * 0.36 92.16 23501 100 011 0 * 0.36 92.16 23501 800 000 1 * 0.72 184.32 47002 50 100 0 * 0.72 184.32 47002 400 001 1 * 1.44 368.64 94003 25 101 0 * 1.44 368.64 94003 200 010 1 * 2.88 737.28 200000 12.5 110 0 * 2.88 737.28 200000 100 011 1 * 5.76 737.28 200000 6.25 111 0 * 5.76 737.28 200000 50 100 1 * 11.52 737.28 200000 25 101 1 * 23.04 737.28 200000 12.5 110 1 * 46.08 737.28 200000 6.25 111 1 * —------------------------------------------------------------------------------------------------------------ * *************************************************************************************************/ #define XSNS_99 99 #define XI2C_70 70 // See I2CDEVICES.md #define LUXV30B_ADDR 0x4A // Two wire library uses 7-bit addresses throughout #define LUXV30B_DATAREG 0x00 // Address of the data register #define LUXV30B_CONFREG 0x04 // Address of the configuration register class LuxV30b { public: LuxV30b(); void Detect(); bool Found(); void Read(); void Show(uint32_t function); private: float Lux(); bool _found; uint32_t _lux; uint32_t _lux_last; }; LuxV30b::LuxV30b() { _found = false; _lux = 0; } void LuxV30b::Detect() { if (!I2cSetDevice(LUXV30B_ADDR)) { return; } _found = true; I2cSetActiveFound(LUXV30B_ADDR, "LUXV30B"); } bool LuxV30b::Found() { return _found; } void LuxV30b::Read() { uint32_t lux = I2cRead8(LUXV30B_ADDR, 0); // delay(8); lux |= I2cRead8(LUXV30B_ADDR, 1) << 8; // delay(8); lux |= I2cRead8(LUXV30B_ADDR, 2) << 16; // delay(8); lux |= I2cRead8(LUXV30B_ADDR, 3) << 24; _lux = (lux > (_lux_last << 4)) ? _lux_last : lux; // Filter large deviations due to misreads _lux_last = lux; // AddLog(LOG_LEVEL_DEBUG, PSTR("SCD: Raw %d/%d"), lux, _lux); } float LuxV30b::Lux() { return ((float)_lux * 1.4) / 1000; } void LuxV30b::Show(uint32_t function) { // if (0 < Lux()) { if (_lux) { char lux[FLOATSZ]; dtostrfd(Lux(), 2, lux); if (FUNC_JSON_APPEND == function) { ResponseAppend_P(PSTR(",\"LUXV30B\":{\"" D_JSON_ILLUMINANCE "\":%s}"), lux); #ifdef USE_DOMOTICZ // Instead of below code use a rule like 'on tele-luxv30b#illuminance do dzsend1 9988,%value% endon' // where 9988 is the domoticz sensor Idx // if (0 == TasmotaGlobal.tele_period) { DomoticzSensor(DZ_ILLUMINANCE, lux); } #endif // USE_DOMOTICZ #ifdef USE_WEBSERVER } else { WSContentSend_PD(PSTR("{s}LUXV30B " D_ILLUMINANCE "{m}%s " D_UNIT_LUX "{e}"), lux); #endif // USE_WEBSERVER } } } LuxV30b Luxv30b; /*********************************************************************************************\ * Interface \*********************************************************************************************/ bool Xsns99(uint32_t function) { if (!I2cEnabled(XI2C_70)) { return false; } bool result = false; if (FUNC_INIT == function) { Luxv30b.Detect(); } else if (Luxv30b.Found()) { switch (function) { case FUNC_EVERY_SECOND: Luxv30b.Read(); break; case FUNC_JSON_APPEND: #ifdef USE_WEBSERVER case FUNC_WEB_SENSOR: #endif // USE_WEBSERVER Luxv30b.Show(function); break; } } return result; } #endif // USE_LUXV30B #endif // USE_I2C