Adafruit_VEML6046/Adafruit_VEML6046.cpp
ladyada 77715e7a5d Add calculateLux function and improve test coverage
- Add calculateLux() function using datasheet resolution tables
- Support both 1/2 PD and 2/2 PD configurations
- Add RGB gain and interrupt persistence tests to example
- Add green threshold setter/getter tests
- Optimize loop delay based on integration time + 5ms buffer
- Add lux calculation display in test output

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-17 14:23:15 -04:00

557 lines
No EOL
18 KiB
C++

/*!
* @file Adafruit_VEML6046.cpp
*
* @mainpage Adafruit VEML6046 RGBIR color sensor library
*
* @section intro_sec Introduction
*
* This is the documentation for Adafruit's VEML6046 driver for the
* Arduino platform. It is designed specifically to work with the
* Adafruit VEML6046 sensor: https://www.adafruit.com/product/xxxx
*
* These sensors use I2C to communicate, 2 pins are required to
* interface.
*
* Adafruit invests time and resources providing this open source code,
* please support Adafruit and open-source hardware by purchasing
* products from Adafruit!
*
* @section author Author
*
* Written by Limor 'ladyada' Fried with assistance from Claude Code for
* Adafruit Industries.
*
* @section license License
*
* MIT license, all text here must be included in any redistribution.
*/
#include "Adafruit_VEML6046.h"
/*!
* @brief Instantiates a new VEML6046 class
*/
Adafruit_VEML6046::Adafruit_VEML6046() {
i2c_dev = nullptr;
}
/*!
* @brief Cleans up the VEML6046
*/
Adafruit_VEML6046::~Adafruit_VEML6046() {
if (i2c_dev) {
delete i2c_dev;
}
}
/*!
* @brief Sets up the hardware and initializes I2C
* @param i2c_addr
* The I2C address to be used.
* @param wire
* The Wire object to be used for I2C connections.
* @return True if initialization was successful, otherwise false.
*/
bool Adafruit_VEML6046::begin(uint8_t i2c_addr, TwoWire* wire) {
if (i2c_dev) {
delete i2c_dev;
}
i2c_dev = new Adafruit_I2CDevice(i2c_addr, wire);
if (!i2c_dev->begin()) {
return false;
}
// Check device ID
Adafruit_BusIO_Register id_reg =
Adafruit_BusIO_Register(i2c_dev, VEML6046_REG_ID_L, 2, LSBFIRST);
uint16_t chip_id = id_reg.read();
if (chip_id != 0x0001) {
return false;
}
// Enable the sensor
if (!enable()) {
return false;
}
// Enable RGB calibration
if (!setRGBCalibration(true)) {
return false;
}
return true;
}
/*!
* @brief Sets the integration time for RGB measurements
* @param it Integration time setting
* @return True if successful, false if I2C write failed
*/
bool Adafruit_VEML6046::setIntegrationTime(veml6046_integration_time_t it) {
Adafruit_BusIO_Register rgb_conf0_reg =
Adafruit_BusIO_Register(i2c_dev, VEML6046_REG_RGB_CONF_0, 1);
Adafruit_BusIO_RegisterBits rgb_it_bits =
Adafruit_BusIO_RegisterBits(&rgb_conf0_reg, 3, 4);
return rgb_it_bits.write(it);
}
/*!
* @brief Gets the current integration time setting
* @return Current integration time setting
*/
veml6046_integration_time_t Adafruit_VEML6046::getIntegrationTime(void) {
Adafruit_BusIO_Register rgb_conf0_reg =
Adafruit_BusIO_Register(i2c_dev, VEML6046_REG_RGB_CONF_0, 1);
Adafruit_BusIO_RegisterBits rgb_it_bits =
Adafruit_BusIO_RegisterBits(&rgb_conf0_reg, 3, 4);
return (veml6046_integration_time_t)rgb_it_bits.read();
}
/*!
* @brief Sets the RGB measurement mode (auto vs forced)
* @param forced True for active force mode, false for auto mode
* @return True if successful, false if I2C write failed
*/
bool Adafruit_VEML6046::setRGBModeForced(bool forced) {
Adafruit_BusIO_Register rgb_conf0_reg =
Adafruit_BusIO_Register(i2c_dev, VEML6046_REG_RGB_CONF_0, 1);
Adafruit_BusIO_RegisterBits rgb_mode_bits =
Adafruit_BusIO_RegisterBits(&rgb_conf0_reg, 1, 3);
return rgb_mode_bits.write(forced ? 1 : 0);
}
/*!
* @brief Gets the current RGB measurement mode
* @return True if in active force mode, false if in auto mode
*/
bool Adafruit_VEML6046::getRGBModeForced(void) {
Adafruit_BusIO_Register rgb_conf0_reg =
Adafruit_BusIO_Register(i2c_dev, VEML6046_REG_RGB_CONF_0, 1);
Adafruit_BusIO_RegisterBits rgb_mode_bits =
Adafruit_BusIO_RegisterBits(&rgb_conf0_reg, 1, 3);
return rgb_mode_bits.read() == 1;
}
/*!
* @brief Triggers a measurement in active force mode
* @return True if successful, false if I2C write failed
*/
bool Adafruit_VEML6046::RGBTrigger(void) {
Adafruit_BusIO_Register rgb_conf0_reg =
Adafruit_BusIO_Register(i2c_dev, VEML6046_REG_RGB_CONF_0, 1);
Adafruit_BusIO_RegisterBits rgb_trig_bits =
Adafruit_BusIO_RegisterBits(&rgb_conf0_reg, 1, 2);
return rgb_trig_bits.write(1);
}
/*!
* @brief Checks if measurement is still in progress (trigger bit set)
* @return True if measurement in progress, false if complete
*/
bool Adafruit_VEML6046::isTriggered(void) {
Adafruit_BusIO_Register rgb_conf0_reg =
Adafruit_BusIO_Register(i2c_dev, VEML6046_REG_RGB_CONF_0, 1);
Adafruit_BusIO_RegisterBits rgb_trig_bits =
Adafruit_BusIO_RegisterBits(&rgb_conf0_reg, 1, 2);
return rgb_trig_bits.read() == 1;
}
/*!
* @brief Enables or disables green channel interrupt
* @param enabled True to enable interrupt, false to disable
* @return True if successful, false if I2C write failed
*/
bool Adafruit_VEML6046::setGreenIntEnabled(bool enabled) {
Adafruit_BusIO_Register rgb_conf0_reg =
Adafruit_BusIO_Register(i2c_dev, VEML6046_REG_RGB_CONF_0, 1);
Adafruit_BusIO_RegisterBits g_int_bits =
Adafruit_BusIO_RegisterBits(&rgb_conf0_reg, 1, 1);
return g_int_bits.write(enabled ? 1 : 0);
}
/*!
* @brief Gets the green channel interrupt enable status
* @return True if interrupt enabled, false if disabled
*/
bool Adafruit_VEML6046::getGreenIntEnabled(void) {
Adafruit_BusIO_Register rgb_conf0_reg =
Adafruit_BusIO_Register(i2c_dev, VEML6046_REG_RGB_CONF_0, 1);
Adafruit_BusIO_RegisterBits g_int_bits =
Adafruit_BusIO_RegisterBits(&rgb_conf0_reg, 1, 1);
return g_int_bits.read() == 1;
}
/*!
* @brief Enables the VEML6046 sensor by clearing both RGB_ON bits
* @return True if successful, false if I2C write failed
*/
bool Adafruit_VEML6046::enable(void) {
Adafruit_BusIO_Register rgb_conf_reg =
Adafruit_BusIO_Register(i2c_dev, VEML6046_REG_RGB_CONF_0, 2, MSBFIRST);
Adafruit_BusIO_RegisterBits rgb_on_bits =
Adafruit_BusIO_RegisterBits(&rgb_conf_reg, 2, 7);
return rgb_on_bits.write(0x00);
}
/*!
* @brief Checks if the VEML6046 sensor is enabled
* @return True if enabled, false if in shutdown
*/
bool Adafruit_VEML6046::isEnabled(void) {
Adafruit_BusIO_Register rgb_conf_reg =
Adafruit_BusIO_Register(i2c_dev, VEML6046_REG_RGB_CONF_0, 2, MSBFIRST);
Adafruit_BusIO_RegisterBits rgb_on_bits =
Adafruit_BusIO_RegisterBits(&rgb_conf_reg, 2, 7);
return rgb_on_bits.read() == 0x00;
}
/*!
* @brief Sets the photodiode size (1/2 PD or 2/2 PD)
* @param half_size True for 1/2 PD, false for 2/2 PD
* @return True if successful, false if I2C write failed
*/
bool Adafruit_VEML6046::setPhotoDiodeHalfSize(bool half_size) {
Adafruit_BusIO_Register rgb_conf1_reg =
Adafruit_BusIO_Register(i2c_dev, VEML6046_REG_RGB_CONF_1, 1);
Adafruit_BusIO_RegisterBits pddiv_bits =
Adafruit_BusIO_RegisterBits(&rgb_conf1_reg, 1, 6);
return pddiv_bits.write(half_size ? 1 : 0);
}
/*!
* @brief Gets the current photodiode size setting
* @return True if 1/2 PD, false if 2/2 PD
*/
bool Adafruit_VEML6046::getPhotoDiodeHalfSize(void) {
Adafruit_BusIO_Register rgb_conf1_reg =
Adafruit_BusIO_Register(i2c_dev, VEML6046_REG_RGB_CONF_1, 1);
Adafruit_BusIO_RegisterBits pddiv_bits =
Adafruit_BusIO_RegisterBits(&rgb_conf1_reg, 1, 6);
return pddiv_bits.read() == 1;
}
/*!
* @brief Sets the RGB gain
* @param gain RGB gain setting
* @return True if successful, false if I2C write failed
*/
bool Adafruit_VEML6046::setRGBGain(veml6046_gain_t gain) {
Adafruit_BusIO_Register rgb_conf1_reg =
Adafruit_BusIO_Register(i2c_dev, VEML6046_REG_RGB_CONF_1, 1);
Adafruit_BusIO_RegisterBits gain_bits =
Adafruit_BusIO_RegisterBits(&rgb_conf1_reg, 2, 3);
return gain_bits.write(gain);
}
/*!
* @brief Gets the current RGB gain setting
* @return Current RGB gain setting
*/
veml6046_gain_t Adafruit_VEML6046::getRGBGain(void) {
Adafruit_BusIO_Register rgb_conf1_reg =
Adafruit_BusIO_Register(i2c_dev, VEML6046_REG_RGB_CONF_1, 1);
Adafruit_BusIO_RegisterBits gain_bits =
Adafruit_BusIO_RegisterBits(&rgb_conf1_reg, 2, 3);
return (veml6046_gain_t)gain_bits.read();
}
/*!
* @brief Sets the interrupt persistence
* @param pers Persistence setting
* @return True if successful, false if I2C write failed
*/
bool Adafruit_VEML6046::setIntPersistence(veml6046_persistence_t pers) {
Adafruit_BusIO_Register rgb_conf1_reg =
Adafruit_BusIO_Register(i2c_dev, VEML6046_REG_RGB_CONF_1, 1);
Adafruit_BusIO_RegisterBits pers_bits =
Adafruit_BusIO_RegisterBits(&rgb_conf1_reg, 2, 1);
return pers_bits.write(pers);
}
/*!
* @brief Gets the current interrupt persistence setting
* @return Current persistence setting
*/
veml6046_persistence_t Adafruit_VEML6046::getIntPersistence(void) {
Adafruit_BusIO_Register rgb_conf1_reg =
Adafruit_BusIO_Register(i2c_dev, VEML6046_REG_RGB_CONF_1, 1);
Adafruit_BusIO_RegisterBits pers_bits =
Adafruit_BusIO_RegisterBits(&rgb_conf1_reg, 2, 1);
return (veml6046_persistence_t)pers_bits.read();
}
/*!
* @brief Sets the RGB calibration enable
* @param enabled True to enable calibration, false to disable
* @return True if successful, false if I2C write failed
*/
bool Adafruit_VEML6046::setRGBCalibration(bool enabled) {
Adafruit_BusIO_Register rgb_conf1_reg =
Adafruit_BusIO_Register(i2c_dev, VEML6046_REG_RGB_CONF_1, 1);
Adafruit_BusIO_RegisterBits cal_bits =
Adafruit_BusIO_RegisterBits(&rgb_conf1_reg, 1, 0);
return cal_bits.write(enabled ? 1 : 0);
}
/*!
* @brief Gets the RGB calibration enable status
* @return True if calibration enabled, false if disabled
*/
bool Adafruit_VEML6046::getRGBCalibration(void) {
Adafruit_BusIO_Register rgb_conf1_reg =
Adafruit_BusIO_Register(i2c_dev, VEML6046_REG_RGB_CONF_1, 1);
Adafruit_BusIO_RegisterBits cal_bits =
Adafruit_BusIO_RegisterBits(&rgb_conf1_reg, 1, 0);
return cal_bits.read() == 1;
}
/*!
* @brief Sets the green channel high threshold
* @param threshold 16-bit threshold value
* @return True if successful, false if I2C write failed
*/
bool Adafruit_VEML6046::setGreenThresholdHigh(uint16_t threshold) {
Adafruit_BusIO_Register thdh_reg =
Adafruit_BusIO_Register(i2c_dev, VEML6046_REG_G_THDH_L, 2, LSBFIRST);
return thdh_reg.write(threshold);
}
/*!
* @brief Gets the green channel high threshold
* @return Current high threshold value
*/
uint16_t Adafruit_VEML6046::getGreenThresholdHigh(void) {
Adafruit_BusIO_Register thdh_reg =
Adafruit_BusIO_Register(i2c_dev, VEML6046_REG_G_THDH_L, 2, LSBFIRST);
return thdh_reg.read();
}
/*!
* @brief Sets the green channel low threshold
* @param threshold 16-bit threshold value
* @return True if successful, false if I2C write failed
*/
bool Adafruit_VEML6046::setGreenThresholdLow(uint16_t threshold) {
Adafruit_BusIO_Register thdl_reg =
Adafruit_BusIO_Register(i2c_dev, VEML6046_REG_G_THDL_L, 2, LSBFIRST);
return thdl_reg.write(threshold);
}
/*!
* @brief Gets the green channel low threshold
* @return Current low threshold value
*/
uint16_t Adafruit_VEML6046::getGreenThresholdLow(void) {
Adafruit_BusIO_Register thdl_reg =
Adafruit_BusIO_Register(i2c_dev, VEML6046_REG_G_THDL_L, 2, LSBFIRST);
return thdl_reg.read();
}
/*!
* @brief Reads all RGBIR channel data in one operation
* @param r Pointer to store red channel data
* @param g Pointer to store green channel data
* @param b Pointer to store blue channel data
* @param ir Pointer to store IR channel data
* @return True if successful, false if I2C read failed
*/
bool Adafruit_VEML6046::getData(uint16_t* r, uint16_t* g, uint16_t* b,
uint16_t* ir) {
if (!r || !g || !b || !ir) {
return false;
}
uint8_t buffer[8];
uint8_t reg_addr = VEML6046_REG_R_DATA_L;
if (!i2c_dev->write_then_read(&reg_addr, 1, buffer, 8)) {
return false;
}
*r = buffer[0] | (buffer[1] << 8);
*g = buffer[2] | (buffer[3] << 8);
*b = buffer[4] | (buffer[5] << 8);
*ir = buffer[6] | (buffer[7] << 8);
return true;
}
/*!
* @brief Calculates lux value from green channel count
* @param green_count Raw green channel reading
* @return Calculated lux value
*/
float Adafruit_VEML6046::calculateLux(uint16_t green_count) {
// Get current settings to determine resolution factor
veml6046_integration_time_t it = getIntegrationTime();
veml6046_gain_t gain = getRGBGain();
bool half_pd = getPhotoDiodeHalfSize();
float resolution = 0.0;
// Resolution table from datasheet (lx/cnt)
// TABLE 13 - 2/2 PD USED, TABLE 14 - 1/2 PD USED
if (half_pd) {
// 1/2 PD used - TABLE 14
switch (it) {
case VEML6046_IT_400MS:
switch (gain) {
case VEML6046_GAIN_2X: resolution = 0.0105; break;
case VEML6046_GAIN_1X: resolution = 0.0210; break;
case VEML6046_GAIN_0_66X: resolution = 0.0318; break;
case VEML6046_GAIN_0_5X: resolution = 0.0420; break;
}
break;
case VEML6046_IT_200MS:
switch (gain) {
case VEML6046_GAIN_2X: resolution = 0.0210; break;
case VEML6046_GAIN_1X: resolution = 0.0420; break;
case VEML6046_GAIN_0_66X: resolution = 0.0636; break;
case VEML6046_GAIN_0_5X: resolution = 0.0840; break;
}
break;
case VEML6046_IT_100MS:
switch (gain) {
case VEML6046_GAIN_2X: resolution = 0.0420; break;
case VEML6046_GAIN_1X: resolution = 0.0840; break;
case VEML6046_GAIN_0_66X: resolution = 0.1273; break;
case VEML6046_GAIN_0_5X: resolution = 0.1680; break;
}
break;
case VEML6046_IT_50MS:
switch (gain) {
case VEML6046_GAIN_2X: resolution = 0.0840; break;
case VEML6046_GAIN_1X: resolution = 0.1680; break;
case VEML6046_GAIN_0_66X: resolution = 0.2545; break;
case VEML6046_GAIN_0_5X: resolution = 0.3360; break;
}
break;
case VEML6046_IT_25MS:
switch (gain) {
case VEML6046_GAIN_2X: resolution = 0.1680; break;
case VEML6046_GAIN_1X: resolution = 0.3360; break;
case VEML6046_GAIN_0_66X: resolution = 0.5091; break;
case VEML6046_GAIN_0_5X: resolution = 0.6720; break;
}
break;
case VEML6046_IT_12_5MS:
switch (gain) {
case VEML6046_GAIN_2X: resolution = 0.3360; break;
case VEML6046_GAIN_1X: resolution = 0.6720; break;
case VEML6046_GAIN_0_66X: resolution = 1.0182; break;
case VEML6046_GAIN_0_5X: resolution = 1.3440; break;
}
break;
case VEML6046_IT_6_25MS:
switch (gain) {
case VEML6046_GAIN_2X: resolution = 0.6720; break;
case VEML6046_GAIN_1X: resolution = 1.3440; break;
case VEML6046_GAIN_0_66X: resolution = 2.0364; break;
case VEML6046_GAIN_0_5X: resolution = 2.6880; break;
}
break;
case VEML6046_IT_3_125MS:
switch (gain) {
case VEML6046_GAIN_2X: resolution = 1.3440; break;
case VEML6046_GAIN_1X: resolution = 2.6880; break;
case VEML6046_GAIN_0_66X: resolution = 4.0727; break;
case VEML6046_GAIN_0_5X: resolution = 5.3760; break;
}
break;
}
} else {
// 2/2 PD used - TABLE 13
switch (it) {
case VEML6046_IT_400MS:
switch (gain) {
case VEML6046_GAIN_2X: resolution = 0.0053; break;
case VEML6046_GAIN_1X: resolution = 0.0105; break;
case VEML6046_GAIN_0_66X: resolution = 0.0159; break;
case VEML6046_GAIN_0_5X: resolution = 0.0210; break;
}
break;
case VEML6046_IT_200MS:
switch (gain) {
case VEML6046_GAIN_2X: resolution = 0.0105; break;
case VEML6046_GAIN_1X: resolution = 0.0210; break;
case VEML6046_GAIN_0_66X: resolution = 0.0318; break;
case VEML6046_GAIN_0_5X: resolution = 0.0420; break;
}
break;
case VEML6046_IT_100MS:
switch (gain) {
case VEML6046_GAIN_2X: resolution = 0.0210; break;
case VEML6046_GAIN_1X: resolution = 0.0420; break;
case VEML6046_GAIN_0_66X: resolution = 0.0636; break;
case VEML6046_GAIN_0_5X: resolution = 0.0840; break;
}
break;
case VEML6046_IT_50MS:
switch (gain) {
case VEML6046_GAIN_2X: resolution = 0.0420; break;
case VEML6046_GAIN_1X: resolution = 0.0840; break;
case VEML6046_GAIN_0_66X: resolution = 0.1273; break;
case VEML6046_GAIN_0_5X: resolution = 0.1680; break;
}
break;
case VEML6046_IT_25MS:
switch (gain) {
case VEML6046_GAIN_2X: resolution = 0.0840; break;
case VEML6046_GAIN_1X: resolution = 0.1680; break;
case VEML6046_GAIN_0_66X: resolution = 0.2545; break;
case VEML6046_GAIN_0_5X: resolution = 0.3360; break;
}
break;
case VEML6046_IT_12_5MS:
switch (gain) {
case VEML6046_GAIN_2X: resolution = 0.1680; break;
case VEML6046_GAIN_1X: resolution = 0.3360; break;
case VEML6046_GAIN_0_66X: resolution = 0.5091; break;
case VEML6046_GAIN_0_5X: resolution = 0.6720; break;
}
break;
case VEML6046_IT_6_25MS:
switch (gain) {
case VEML6046_GAIN_2X: resolution = 0.3360; break;
case VEML6046_GAIN_1X: resolution = 0.6720; break;
case VEML6046_GAIN_0_66X: resolution = 1.0182; break;
case VEML6046_GAIN_0_5X: resolution = 1.3440; break;
}
break;
case VEML6046_IT_3_125MS:
switch (gain) {
case VEML6046_GAIN_2X: resolution = 0.6720; break;
case VEML6046_GAIN_1X: resolution = 1.3440; break;
case VEML6046_GAIN_0_66X: resolution = 2.0364; break;
case VEML6046_GAIN_0_5X: resolution = 2.6880; break;
}
break;
}
}
return green_count * resolution;
}