Tasmota/tasmota/tasmota_xsns_sensor/xsns_99_luxv30b.ino

218 lines
10 KiB
Arduino
Raw Normal View History

/*
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 <http://www.gnu.org/licenses/>.
*/
#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] | TIMEms | 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(uint8_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(uint8_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(uint8_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