diff --git a/drivers/sensor/vishay/CMakeLists.txt b/drivers/sensor/vishay/CMakeLists.txt
index c0462dba8620d..56ff0b09bbaac 100644
--- a/drivers/sensor/vishay/CMakeLists.txt
+++ b/drivers/sensor/vishay/CMakeLists.txt
@@ -5,5 +5,6 @@
add_subdirectory_ifdef(CONFIG_VCNL36825T vcnl36825t)
add_subdirectory_ifdef(CONFIG_VCNL4040 vcnl4040)
add_subdirectory_ifdef(CONFIG_VEML6031 veml6031)
+add_subdirectory_ifdef(CONFIG_VEML6046 veml6046)
add_subdirectory_ifdef(CONFIG_VEML7700 veml7700)
# zephyr-keep-sorted-stop
diff --git a/drivers/sensor/vishay/Kconfig b/drivers/sensor/vishay/Kconfig
index 564a9740c9c8e..a203cadd9947b 100644
--- a/drivers/sensor/vishay/Kconfig
+++ b/drivers/sensor/vishay/Kconfig
@@ -5,5 +5,6 @@
source "drivers/sensor/vishay/vcnl36825t/Kconfig"
source "drivers/sensor/vishay/vcnl4040/Kconfig"
source "drivers/sensor/vishay/veml6031/Kconfig"
+source "drivers/sensor/vishay/veml6046/Kconfig"
source "drivers/sensor/vishay/veml7700/Kconfig"
# zephyr-keep-sorted-stop
diff --git a/drivers/sensor/vishay/veml6031/veml6031.c b/drivers/sensor/vishay/veml6031/veml6031.c
index b36b0a4c8d909..b2511b2586f79 100644
--- a/drivers/sensor/vishay/veml6031/veml6031.c
+++ b/drivers/sensor/vishay/veml6031/veml6031.c
@@ -57,33 +57,6 @@ LOG_MODULE_REGISTER(VEML6031, CONFIG_SENSOR_LOG_LEVEL);
#define VEML6031_CMDCODE_ID_H 0x15
#define VEML6031_CMDCODE_ALS_INT 0x17
-/*
- * ALS integration time struct.
- */
-struct veml6031_it_data {
- enum veml6031_it num;
- uint8_t val;
- int us;
-};
-
-/*
- * ALS integration time setting values.
- *
- * The enumerators of enum veml6031_it provide
- * indices into this array to get the related value for the
- * ALS_IT configuration bits.
- */
-static const struct veml6031_it_data veml6031_it_values[] = {
- {VEML6031_IT_3_125, 0x00, 3125}, /* 3.125 - 0b0000 */
- {VEML6031_IT_6_25, 0x01, 6250}, /* 6.25 - 0b0001 */
- {VEML6031_IT_12_5, 0x02, 12500}, /* 12.5 - 0b0010 */
- {VEML6031_IT_25, 0x03, 25000}, /* 25 - 0b0011 */
- {VEML6031_IT_50, 0x04, 50000}, /* 50 - 0b0100 */
- {VEML6031_IT_100, 0x05, 100000}, /* 100 - 0b0101 */
- {VEML6031_IT_200, 0x06, 200000}, /* 200 - 0b0110 */
- {VEML6031_IT_400, 0x07, 400000}, /* 400 - 0b0111 */
-};
-
/*
* Resolution matrix for values to convert between data provided
* by the sensor ("counts") and lux.
@@ -93,7 +66,7 @@ static const struct veml6031_it_data veml6031_it_values[] = {
* and enum veml6031_als_it are used for indices into this matrix.
*/
static const float
- veml6031_resolution[VEML6031_DIV4_COUNT][VEML6031_GAIN_COUNT][VEML6031_IT_COUNT] = {
+ veml6031_resolution[VEML6031_DIV4_COUNT][VEML60XX_GAIN_COUNT][VEML60XX_IT_COUNT] = {
/*3.125ms 6.25ms 12.5ms 25ms 50ms 100ms 200ms 400ms IT */
/* size 4/4 */
{
@@ -131,9 +104,9 @@ struct veml6031_data {
uint8_t ir_sd; /* ALS and IR channel shutdown */
uint8_t cal; /* Power on ready */
enum veml6031_div4 div4; /* effective photodiode size */
- enum veml6031_gain gain; /* gain selection */
- enum veml6031_it itim; /* ALS integration time */
- enum veml6031_pers pers; /* ALS persistens protect */
+ enum veml60xx_gain gain; /* gain selection */
+ enum veml60xx_it itim; /* ALS integration time */
+ enum veml60xx_pers pers; /* ALS persistence protect */
uint16_t thresh_high;
uint16_t thresh_low;
uint16_t als_data;
@@ -142,30 +115,15 @@ struct veml6031_data {
uint32_t int_flags;
};
-static bool veml6031_gain_in_range(int32_t gain)
-{
- return (gain >= VEML6031_GAIN_1) && (gain <= VEML6031_GAIN_0_5);
-}
-
-static bool veml6031_itim_in_range(int32_t itim)
-{
- return (itim >= VEML6031_IT_3_125) && (itim <= VEML6031_IT_400);
-}
-
static bool veml6031_div4_in_range(int32_t div4)
{
return (div4 >= VEML6031_SIZE_4_4) && (div4 <= VEML6031_SIZE_1_4);
}
-static bool veml6031_pers_in_range(int32_t pers)
-{
- return (pers >= VEML6031_PERS_1) && (pers <= VEML6031_PERS_8);
-}
-
static void veml6031_sleep_by_integration_time(const struct veml6031_data *data)
{
- if (veml6031_itim_in_range(data->itim)) {
- k_sleep(K_USEC(veml6031_it_values[data->itim].us));
+ if (veml60xx_it_in_range(data->itim)) {
+ k_sleep(K_USEC(veml60xx_it_values[data->itim].us));
} else {
LOG_WRN_ONCE("Wrong settings: itim:%d. Most likely an application bug!",
data->itim);
@@ -174,28 +132,8 @@ static void veml6031_sleep_by_integration_time(const struct veml6031_data *data)
static int veml6031_check_settings(const struct veml6031_data *data)
{
- return veml6031_div4_in_range(data->div4) && veml6031_gain_in_range(data->gain) &&
- veml6031_itim_in_range(data->itim);
-}
-
-static int veml6031_check_gain(const struct sensor_value *val)
-{
- return veml6031_gain_in_range(val->val1);
-}
-
-static int veml6031_check_it(const struct sensor_value *val)
-{
- return veml6031_itim_in_range(val->val1);
-}
-
-static int veml6031_check_div4(const struct sensor_value *val)
-{
- return veml6031_div4_in_range(val->val1);
-}
-
-static int veml6031_check_pers(const struct sensor_value *val)
-{
- return veml6031_pers_in_range(val->val1);
+ return veml6031_div4_in_range(data->div4) && veml60xx_gain_in_range(data->gain) &&
+ veml60xx_it_in_range(data->itim);
}
static int veml6031_read(const struct device *dev, uint8_t cmd, uint8_t *data)
@@ -368,29 +306,29 @@ static int veml6031_attr_set(const struct device *dev, enum sensor_channel chan,
/* SENSOR_ATTR_.*_THRESH are not in enum sensor_attribute_veml6031 */
switch ((int)attr) {
case SENSOR_ATTR_VEML6031_IT:
- if (veml6031_check_it(val)) {
- data->itim = (enum veml6031_it)val->val1;
+ if (veml60xx_it_in_range(val->val1)) {
+ data->itim = (enum veml60xx_it)val->val1;
} else {
return -EINVAL;
}
break;
case SENSOR_ATTR_VEML6031_DIV4:
- if (veml6031_check_div4(val)) {
+ if (veml6031_div4_in_range(val->val1)) {
data->div4 = (enum veml6031_div4)val->val1;
} else {
return -EINVAL;
}
break;
case SENSOR_ATTR_VEML6031_GAIN:
- if (veml6031_check_gain(val)) {
- data->gain = (enum veml6031_gain)val->val1;
+ if (veml60xx_gain_in_range(val->val1)) {
+ data->gain = (enum veml60xx_gain)val->val1;
} else {
return -EINVAL;
}
break;
case SENSOR_ATTR_VEML6031_PERS:
- if (veml6031_check_pers(val)) {
- data->pers = (enum veml6031_pers)val->val1;
+ if (veml60xx_pers_in_range(val->val1)) {
+ data->pers = (enum veml60xx_pers)val->val1;
} else {
return -EINVAL;
}
@@ -651,9 +589,9 @@ static DEVICE_API(sensor, veml6031_api) = {
static struct veml6031_data veml6031_data_##n = {.trig = 1, \
.af = 1, \
.div4 = VEML6031_SIZE_4_4, \
- .gain = VEML6031_GAIN_1, \
- .itim = VEML6031_IT_100, \
- .pers = VEML6031_PERS_1, \
+ .gain = VEML60XX_GAIN_1, \
+ .itim = VEML60XX_IT_100, \
+ .pers = VEML60XX_PERS_1, \
.thresh_high = 0xFFFF}; \
\
static const struct veml6031_config veml6031_config_##n = { \
diff --git a/drivers/sensor/vishay/veml6046/CMakeLists.txt b/drivers/sensor/vishay/veml6046/CMakeLists.txt
new file mode 100644
index 0000000000000..c31cfff011a18
--- /dev/null
+++ b/drivers/sensor/vishay/veml6046/CMakeLists.txt
@@ -0,0 +1,5 @@
+# Copyright (c) 2025 Andreas Klinger
+# SPDX-License-Identifier: Apache-2.0
+
+zephyr_library()
+zephyr_library_sources(veml6046.c)
diff --git a/drivers/sensor/vishay/veml6046/Kconfig b/drivers/sensor/vishay/veml6046/Kconfig
new file mode 100644
index 0000000000000..06e057e07ec21
--- /dev/null
+++ b/drivers/sensor/vishay/veml6046/Kconfig
@@ -0,0 +1,12 @@
+# Copyright (c) 2025 Andreas Klinger
+# SPDX-License-Identifier: Apache-2.0
+
+# Vishay VEML6046 RGBIR color sensor driver options.
+
+config VEML6046
+ bool "Vishay VEML6046 RGBIR color sensor"
+ default y
+ depends on DT_HAS_VISHAY_VEML6046_ENABLED
+ select I2C
+ help
+ Enable Vishay VEML6046 RGBIR color sensor driver.
diff --git a/drivers/sensor/vishay/veml6046/veml6046.c b/drivers/sensor/vishay/veml6046/veml6046.c
new file mode 100644
index 0000000000000..89faea0e2d141
--- /dev/null
+++ b/drivers/sensor/vishay/veml6046/veml6046.c
@@ -0,0 +1,590 @@
+/*
+ * Copyright (c) 2025 Andreas Klinger
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT vishay_veml6046
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+LOG_MODULE_REGISTER(VEML6046, CONFIG_SENSOR_LOG_LEVEL);
+
+/*
+ * ID code of device
+ */
+#define VEML6046_DEFAULT_ID 0x01
+
+/*
+ * Bit mask to check for data ready in single measurement.
+ */
+#define VEML6046_AF_DATA_READY BIT(3)
+
+/*
+ * Maximum value of RGBIR data which also means that the sensor is in
+ * saturation and that the measured value might be wrong.
+ * In such a case the user program should reduce one or more of the following
+ * attributes to get a relyable value:
+ * gain
+ * integration time
+ * effective photodiode size divider
+ */
+#define VEML6046_DATA_OVERFLOW 0xFFFF
+
+/*
+ * 16-bit command register addresses
+ */
+#define VEML6046_CMDCODE_RGB_CONF_0 0x00
+#define VEML6046_CMDCODE_RGB_CONF_1 0x01
+#define VEML6046_CMDCODE_G_THDH_L 0x04
+#define VEML6046_CMDCODE_G_THDH_H 0x05
+#define VEML6046_CMDCODE_G_THDL_L 0x06
+#define VEML6046_CMDCODE_G_THDL_H 0x07
+#define VEML6046_CMDCODE_R_DATA_L 0x10
+#define VEML6046_CMDCODE_R_DATA_H 0x11
+#define VEML6046_CMDCODE_G_DATA_L 0x12
+#define VEML6046_CMDCODE_G_DATA_H 0x13
+#define VEML6046_CMDCODE_B_DATA_L 0x14
+#define VEML6046_CMDCODE_B_DATA_H 0x15
+#define VEML6046_CMDCODE_IR_DATA_L 0x16
+#define VEML6046_CMDCODE_IR_DATA_H 0x17
+#define VEML6046_CMDCODE_ID_L 0x18
+#define VEML6046_CMDCODE_ID_H 0x19
+#define VEML6046_CMDCODE_INT_L 0x1A
+#define VEML6046_CMDCODE_INT_H 0x1B
+
+/*
+ * Resolution matrix for values to convert between data provided
+ * by the sensor ("counts") and lux.
+ *
+ * These values depend on the current size, gain and integration time settings.
+ * The enumerators of enum veml6046_pdd, enum veml60xx_gain and enum
+ * veml6046_als_it are used for indices into this matrix.
+ */
+static const float
+ veml6046_resolution[VEML6046_PDD_COUNT][VEML60XX_GAIN_COUNT][VEML60XX_IT_COUNT] = {
+ /*3.125ms 6.25ms 12.5ms 25ms 50ms 100ms 200ms 400ms IT */
+ /* size 2/2 */
+ {
+ {1.3440f, 0.6720f, 0.3360f, 0.1680f, 0.0840f, 0.0420f, 0.0210f,
+ 0.0105f}, /* Gain 1 */
+ {0.6720f, 0.3360f, 0.1680f, 0.0840f, 0.0420f, 0.0210f, 0.0105f,
+ 0.0053f}, /* Gain 2 */
+ {2.0364f, 1.0182f, 0.5091f, 0.2545f, 0.1273f, 0.0636f, 0.0318f,
+ 0.0159f}, /* Gain 0.66 */
+ {2.6880f, 1.3440f, 0.6720f, 0.3360f, 0.1680f, 0.0840f, 0.0420f,
+ 0.0210f}, /* Gain 0.5 */
+ },
+ {
+ /* size 1/2 */
+ {2.6880f, 1.3440f, 0.6720f, 0.3360f, 0.1680f, 0.0840f, 0.0420f,
+ 0.0210f}, /* Gain 1 */
+ {1.3440f, 0.6720f, 0.3360f, 0.1680f, 0.0840f, 0.0420f, 0.0210f,
+ 0.0105f}, /* Gain 2 */
+ {4.0727f, 2.0364f, 1.0182f, 0.5091f, 0.2545f, 0.1273f, 0.0636f,
+ 0.0318f}, /* Gain 0.66 */
+ {5.3760f, 2.6880f, 1.3440f, 0.6720f, 0.3360f, 0.1680f, 0.0840f,
+ 0.0420f}, /* Gain 0.5 */
+ },
+};
+
+struct veml6046_config {
+ struct i2c_dt_spec bus;
+};
+
+struct veml6046_data {
+ uint8_t sd; /* Band gap and LDO shutdown */
+ uint8_t int_en; /* ALS interrupt enable */
+ uint8_t trig; /* ALS active force trigger */
+ enum veml6046_pdd pdd; /* effective photodiode size divider */
+ enum veml60xx_gain gain; /* gain selection */
+ enum veml60xx_it itim; /* ALS integration time */
+ enum veml60xx_pers pers; /* ALS persistens protect */
+ uint16_t thresh_high;
+ uint16_t thresh_low;
+ uint16_t red_data;
+ uint16_t green_data;
+ uint16_t blue_data;
+ uint16_t ir_data;
+ uint32_t red_lux;
+ uint32_t green_lux;
+ uint32_t blue_lux;
+ uint32_t ir_lux;
+};
+
+static bool veml6046_pdd_in_range(int32_t pdd)
+{
+ return pdd >= VEML6046_SIZE_2_2 && pdd <= VEML6046_SIZE_1_2;
+}
+
+static int veml6046_read16(const struct device *dev, uint8_t cmd, uint16_t *data)
+{
+ const struct veml6046_config *conf = dev->config;
+ int ret;
+
+ ret = i2c_burst_read_dt(&conf->bus, cmd, (uint8_t *)data, 2);
+ if (ret < 0) {
+ return ret;
+ }
+
+ *data = sys_le16_to_cpu(*data);
+
+ return 0;
+}
+
+/*
+ * This function excepts an array of uint8_t data[2] with the two corresponding
+ * values set accordingly to the register map of the sensor.
+ */
+static int veml6046_write16(const struct device *dev, uint8_t cmd, uint8_t *data)
+{
+ const struct veml6046_config *conf = dev->config;
+
+ return i2c_burst_write_dt(&conf->bus, cmd, data, 2);
+}
+
+static int veml6046_write_conf(const struct device *dev)
+{
+ int ret;
+ struct veml6046_data *data = dev->data;
+ uint8_t conf[2] = {0, 0};
+
+ /* Bits 7 -> RGB_ON_1 */
+ if (data->sd) {
+ conf[1] |= BIT(7);
+ }
+ /* Bits 6 -> Effective photodiode size */
+ conf[1] |= data->pdd << 6;
+ /* Bit 5 -> reserved */
+ /* Bits 4:3 -> Gain selection */
+ conf[1] |= data->gain << 3;
+ /* Bits 2:1 -> ALS persistence protect number */
+ conf[1] |= data->pers << 1;
+ /* Bit 0 -> Calibration should always be 1 when using the sensor */
+ conf[1] |= BIT(0);
+ /* Bit 7 -> reserved, have to be 0 */
+ /* Bits 6:4 -> integration time (ALS_IT) */
+ conf[0] |= data->itim << 4;
+ /* Bit 3 -> Active force mode is always enabled
+ * Auto mode would continuously deliver data which is not what we want
+ * in this driver
+ */
+ conf[0] |= BIT(3);
+ /* Bit 2 -> ALS active force trigger */
+ if (data->trig) {
+ conf[0] |= BIT(2);
+ }
+ /* Bit 1 -> ALS interrupt enable */
+ if (data->int_en) {
+ conf[0] |= BIT(1);
+ }
+ /* Bit 0 -> shut down setting (SD) */
+ if (data->sd) {
+ conf[0] |= BIT(0);
+ }
+
+ ret = veml6046_write16(dev, VEML6046_CMDCODE_RGB_CONF_0, conf);
+ if (ret) {
+ LOG_ERR("Error while writing conf[0] ret: %d", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int veml6046_write_thresh_high(const struct device *dev)
+{
+ int ret;
+ const struct veml6046_data *data = dev->data;
+ uint8_t val[2];
+
+ val[0] = data->thresh_high & 0xFF;
+ val[1] = data->thresh_high >> 8;
+
+ LOG_DBG("Writing high threshold counts: %d", data->thresh_high);
+ ret = veml6046_write16(dev, VEML6046_CMDCODE_G_THDH_L, val);
+ if (ret) {
+ return ret;
+ }
+
+ return 0;
+}
+
+static int veml6046_write_thresh_low(const struct device *dev)
+{
+ int ret;
+ const struct veml6046_data *data = dev->data;
+ uint8_t val[2];
+
+ val[0] = data->thresh_low & 0xFF;
+ val[1] = data->thresh_low >> 8;
+
+ LOG_DBG("Writing low threshold counts: %d", data->thresh_low);
+ ret = veml6046_write16(dev, VEML6046_CMDCODE_G_THDL_L, val);
+ if (ret) {
+ return ret;
+ }
+
+ return 0;
+}
+
+static int veml6046_fetch(const struct device *dev)
+{
+ struct veml6046_data *data = dev->data;
+ int ret;
+
+ ret = veml6046_read16(dev, VEML6046_CMDCODE_R_DATA_L, &data->red_data);
+ if (ret) {
+ return ret;
+ }
+
+ ret = veml6046_read16(dev, VEML6046_CMDCODE_G_DATA_L, &data->green_data);
+ if (ret) {
+ return ret;
+ }
+
+ ret = veml6046_read16(dev, VEML6046_CMDCODE_B_DATA_L, &data->blue_data);
+ if (ret) {
+ return ret;
+ }
+
+ ret = veml6046_read16(dev, VEML6046_CMDCODE_IR_DATA_L, &data->ir_data);
+ if (ret) {
+ return ret;
+ }
+
+ data->red_lux = data->red_data * veml6046_resolution[data->pdd][data->gain][data->itim];
+ data->green_lux = data->green_data * veml6046_resolution[data->pdd][data->gain][data->itim];
+ data->blue_lux = data->blue_data * veml6046_resolution[data->pdd][data->gain][data->itim];
+ data->ir_lux = data->ir_data * veml6046_resolution[data->pdd][data->gain][data->itim];
+
+ LOG_DBG("Read (R/G/B/IR): counts=%d/%d/%d/%d, lux=%d/%d/%d/%d",
+ data->red_data, data->green_data, data->blue_data, data->ir_data,
+ data->red_lux, data->green_lux, data->blue_lux, data->ir_lux);
+
+ if ((data->red_data == VEML6046_DATA_OVERFLOW) ||
+ (data->green_data == VEML6046_DATA_OVERFLOW) ||
+ (data->blue_data == VEML6046_DATA_OVERFLOW) ||
+ (data->ir_data == VEML6046_DATA_OVERFLOW)) {
+ return -E2BIG;
+ }
+
+ return 0;
+}
+
+static int veml6046_attr_set(const struct device *dev, enum sensor_channel chan,
+ enum sensor_attribute attr, const struct sensor_value *val)
+{
+ struct veml6046_data *data = dev->data;
+
+ if (chan != SENSOR_CHAN_LIGHT) {
+ return -ENOTSUP;
+ }
+
+ /* SENSOR_ATTR_.*_THRESH are not in enum sensor_attribute_veml6046 */
+ switch ((int)attr) {
+ case SENSOR_ATTR_VEML6046_IT:
+ if (veml60xx_it_in_range(val->val1)) {
+ data->itim = (enum veml60xx_it)val->val1;
+ } else {
+ return -EINVAL;
+ }
+ break;
+ case SENSOR_ATTR_VEML6046_PDD:
+ if (veml6046_pdd_in_range(val->val1)) {
+ data->pdd = (enum veml6046_pdd)val->val1;
+ } else {
+ return -EINVAL;
+ }
+ break;
+ case SENSOR_ATTR_VEML6046_GAIN:
+ if (veml60xx_gain_in_range(val->val1)) {
+ data->gain = (enum veml60xx_gain)val->val1;
+ } else {
+ return -EINVAL;
+ }
+ break;
+ case SENSOR_ATTR_VEML6046_PERS:
+ if (veml60xx_pers_in_range(val->val1)) {
+ data->pers = (enum veml60xx_pers)val->val1;
+ } else {
+ return -EINVAL;
+ }
+ break;
+ case SENSOR_ATTR_LOWER_THRESH:
+ data->thresh_low =
+ val->val1 / veml6046_resolution[data->pdd][data->gain][data->itim];
+ return veml6046_write_thresh_low(dev);
+ case SENSOR_ATTR_UPPER_THRESH:
+ data->thresh_high =
+ val->val1 / veml6046_resolution[data->pdd][data->gain][data->itim];
+ return veml6046_write_thresh_high(dev);
+ default:
+ return -ENOTSUP;
+ }
+
+ return 0;
+}
+
+static int veml6046_attr_get(const struct device *dev, enum sensor_channel chan,
+ enum sensor_attribute attr, struct sensor_value *val)
+{
+ struct veml6046_data *data = dev->data;
+
+ if (chan != SENSOR_CHAN_LIGHT) {
+ return -ENOTSUP;
+ }
+
+ /* SENSOR_ATTR_.*_THRESH are not in enum sensor_attribute_veml6046 */
+ switch ((int)attr) {
+ case SENSOR_ATTR_VEML6046_IT:
+ val->val1 = data->itim;
+ break;
+ case SENSOR_ATTR_VEML6046_PDD:
+ val->val1 = data->pdd;
+ break;
+ case SENSOR_ATTR_VEML6046_GAIN:
+ val->val1 = data->gain;
+ break;
+ case SENSOR_ATTR_VEML6046_PERS:
+ val->val1 = data->pers;
+ break;
+ case SENSOR_ATTR_LOWER_THRESH:
+ val->val1 = data->thresh_low
+ * veml6046_resolution[data->pdd][data->gain][data->itim];
+ break;
+ case SENSOR_ATTR_UPPER_THRESH:
+ val->val1 = data->thresh_high
+ * veml6046_resolution[data->pdd][data->gain][data->itim];
+ break;
+ default:
+ return -ENOTSUP;
+ }
+
+ val->val2 = 0;
+
+ return 0;
+}
+
+static int veml6046_perform_single_measurement(const struct device *dev)
+{
+ struct veml6046_data *data = dev->data;
+ int ret;
+ uint16_t val;
+ int cnt = 0;
+
+ data->trig = 1;
+ data->int_en = 0;
+ data->sd = 0;
+
+ ret = veml6046_write_conf(dev);
+ if (ret) {
+ return ret;
+ }
+
+ ret = veml6046_read16(dev, VEML6046_CMDCODE_INT_L, &val);
+ if (ret) {
+ return ret;
+ }
+
+ k_sleep(K_USEC(veml60xx_it_values[data->itim].us));
+
+ while (1) {
+ ret = veml6046_read16(dev, VEML6046_CMDCODE_INT_L, &val);
+ if (ret) {
+ return ret;
+ }
+
+ if ((val >> 8) & VEML6046_AF_DATA_READY) {
+ break;
+ }
+
+ if (cnt > 10) {
+ return -EAGAIN;
+ }
+
+ k_sleep(K_USEC(veml60xx_it_values[data->itim].us / 10));
+
+ cnt++;
+ }
+
+ LOG_DBG("read VEML6046_CMDCODE_INT_H: %02X (%d)", val >> 8, cnt);
+
+ return 0;
+}
+
+static int veml6046_sample_fetch(const struct device *dev, enum sensor_channel chan)
+{
+ int ret;
+
+ /* Start sensor for new measurement */
+ if (chan == SENSOR_CHAN_RED || chan == SENSOR_CHAN_GREEN ||
+ chan == SENSOR_CHAN_BLUE || chan == SENSOR_CHAN_IR ||
+ chan == SENSOR_CHAN_ALL) {
+ ret = veml6046_perform_single_measurement(dev);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return veml6046_fetch(dev);
+ } else {
+ return -ENOTSUP;
+ }
+
+ return 0;
+}
+
+static int veml6046_channel_get(const struct device *dev, enum sensor_channel chan,
+ struct sensor_value *val)
+{
+ struct veml6046_data *data = dev->data;
+
+ switch ((int)chan) {
+ case SENSOR_CHAN_RED:
+ val->val1 = data->red_lux;
+ break;
+ case SENSOR_CHAN_GREEN:
+ val->val1 = data->green_lux;
+ break;
+ case SENSOR_CHAN_BLUE:
+ val->val1 = data->blue_lux;
+ break;
+ case SENSOR_CHAN_IR:
+ val->val1 = data->ir_lux;
+ break;
+ case SENSOR_CHAN_VEML6046_RED_RAW_COUNTS:
+ val->val1 = data->red_data;
+ break;
+ case SENSOR_CHAN_VEML6046_GREEN_RAW_COUNTS:
+ val->val1 = data->green_data;
+ break;
+ case SENSOR_CHAN_VEML6046_BLUE_RAW_COUNTS:
+ val->val1 = data->blue_data;
+ break;
+ case SENSOR_CHAN_VEML6046_IR_RAW_COUNTS:
+ val->val1 = data->ir_data;
+ break;
+ default:
+ return -ENOTSUP;
+ }
+
+ val->val2 = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_DEVICE
+
+static int veml6046_set_shutdown_flag(const struct device *dev, uint8_t new_val)
+{
+ struct veml6046_data *data = dev->data;
+ uint8_t prev_sd;
+ int ret;
+
+ prev_sd = data->sd;
+ data->sd = new_val;
+
+ ret = veml6046_write_conf(dev);
+ if (ret < 0) {
+ data->sd = prev_sd;
+ }
+ return ret;
+}
+
+static int veml6046_pm_action(const struct device *dev, enum pm_device_action action)
+{
+ switch (action) {
+ case PM_DEVICE_ACTION_SUSPEND:
+ return veml6046_set_shutdown_flag(dev, 1);
+
+ case PM_DEVICE_ACTION_RESUME:
+ return veml6046_set_shutdown_flag(dev, 0);
+
+ default:
+ return -ENOTSUP;
+ }
+ return 0;
+}
+
+#endif /* CONFIG_PM_DEVICE */
+
+static int veml6046_init(const struct device *dev)
+{
+ const struct veml6046_config *conf = dev->config;
+ int ret;
+ uint16_t val;
+
+ if (!i2c_is_ready_dt(&conf->bus)) {
+ LOG_ERR("VEML device not ready");
+ return -ENODEV;
+ }
+
+ ret = veml6046_read16(dev, VEML6046_CMDCODE_ID_L, &val);
+ if (ret) {
+ LOG_ERR("Error while reading ID. ret: %d", ret);
+ return ret;
+ }
+ if ((val & 0x00FF) != VEML6046_DEFAULT_ID) {
+ LOG_ERR("Device ID wrong: %d", val & 0x00FF);
+ return -EIO;
+ }
+
+ LOG_DBG("veml6046 found package: %02d address: %02X version: %3s",
+ val >> 14, val >> 12 & 0x03 ? 0x10 : 0x29,
+ val >> 8 & 0x0F ? "XXX" : "A01");
+
+ /* Initialize sensor configuration */
+ ret = veml6046_write_thresh_low(dev);
+ if (ret < 0) {
+ LOG_ERR("Error while writing thresh low. ret: %d", ret);
+ return ret;
+ }
+
+ ret = veml6046_write_thresh_high(dev);
+ if (ret < 0) {
+ LOG_ERR("Error while writing thresh high. ret: %d", ret);
+ return ret;
+ }
+
+ ret = veml6046_write_conf(dev);
+ if (ret < 0) {
+ LOG_ERR("Error while writing config. ret: %d", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static DEVICE_API(sensor, veml6046_api) = {
+ .sample_fetch = veml6046_sample_fetch,
+ .channel_get = veml6046_channel_get,
+ .attr_set = veml6046_attr_set,
+ .attr_get = veml6046_attr_get,
+};
+
+#define VEML6046_INIT(n) \
+ static struct veml6046_data veml6046_data_##n = {.trig = 0, \
+ .pdd = VEML6046_SIZE_2_2, \
+ .gain = VEML60XX_GAIN_1, \
+ .itim = VEML60XX_IT_100, \
+ .pers = VEML60XX_PERS_1, \
+ .thresh_high = 0xFFFF}; \
+ \
+ static const struct veml6046_config veml6046_config_##n = { \
+ .bus = I2C_DT_SPEC_INST_GET(n)}; \
+ \
+ PM_DEVICE_DT_INST_DEFINE(n, veml6046_pm_action); \
+ \
+ SENSOR_DEVICE_DT_INST_DEFINE(n, veml6046_init, PM_DEVICE_DT_INST_GET(n), \
+ &veml6046_data_##n, &veml6046_config_##n, POST_KERNEL, \
+ CONFIG_SENSOR_INIT_PRIORITY, &veml6046_api);
+
+DT_INST_FOREACH_STATUS_OKAY(VEML6046_INIT)
diff --git a/dts/bindings/sensor/vishay,veml6046.yaml b/dts/bindings/sensor/vishay,veml6046.yaml
new file mode 100644
index 0000000000000..473c131c62792
--- /dev/null
+++ b/dts/bindings/sensor/vishay,veml6046.yaml
@@ -0,0 +1,10 @@
+# Copyright (c) 2025 Andreas Klinger
+# SPDX-License-Identifier: Apache-2.0
+
+description: |
+ Vishay VEML6046 RGBIR color sensor with I2C interface.
+ See: https://www.vishay.com/docs/80173/veml6046x00.pdf
+
+compatible: "vishay,veml6046"
+
+include: [sensor-device.yaml, i2c-device.yaml]
diff --git a/include/zephyr/drivers/sensor/veml6031.h b/include/zephyr/drivers/sensor/veml6031.h
index 8d9ee6874f01e..65808d5a79739 100644
--- a/include/zephyr/drivers/sensor/veml6031.h
+++ b/include/zephyr/drivers/sensor/veml6031.h
@@ -4,6 +4,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
+#include
+
/**
* @file
* @brief Header file for extended sensor API of VEML6031 sensor
@@ -24,25 +26,6 @@
extern "C" {
#endif
-/**
- * @brief VEML6031 integration time options for ambient light measurements.
- *
- * Possible values for @ref SENSOR_ATTR_VEML6031_IT custom attribute.
- */
-enum veml6031_it {
- VEML6031_IT_3_125, /**< 3.125 ms */
- VEML6031_IT_6_25, /**< 6.25 ms */
- VEML6031_IT_12_5, /**< 12.5 ms */
- VEML6031_IT_25, /**< 25 ms */
- VEML6031_IT_50, /**< 50 ms */
- VEML6031_IT_100, /**< 100 ms */
- VEML6031_IT_200, /**< 200 ms */
- VEML6031_IT_400, /**< 400 ms */
- /** @cond INTERNAL_HIDDEN */
- VEML6031_IT_COUNT,
- /** @endcond */
-};
-
/**
* @brief VEML6031 size options for ambient light measurements.
*
@@ -56,31 +39,6 @@ enum veml6031_div4 {
/** @endcond */
};
-/**
- * @brief VEML6031 gain options for ambient light measurements.
- */
-enum veml6031_gain {
- VEML6031_GAIN_1 = 0x00, /**< 1x gain */
- VEML6031_GAIN_2 = 0x01, /**< 2x gain */
- VEML6031_GAIN_0_66 = 0x02, /**< 0.66x gain */
- VEML6031_GAIN_0_5 = 0x03, /**< 0.5x gain */
- /** @cond INTERNAL_HIDDEN */
- VEML6031_GAIN_COUNT = 4,
- /** @endcond */
-};
-
-/**
- * @brief VEML6031 ALS interrupt persistence protect number options.
- *
- * Possible values for @ref SENSOR_ATTR_VEML6031_PERS custom attribute.
- */
-enum veml6031_pers {
- VEML6031_PERS_1 = 0x00, /**< 1 measurement */
- VEML6031_PERS_2 = 0x01, /**< 2 measurements */
- VEML6031_PERS_4 = 0x02, /**< 4 measurements */
- VEML6031_PERS_8 = 0x03, /**< 8 measurements */
-};
-
/**
* @brief Custom sensor attributes for VEML6031 sensor.
*
diff --git a/include/zephyr/drivers/sensor/veml6046.h b/include/zephyr/drivers/sensor/veml6046.h
new file mode 100644
index 0000000000000..65b273b2fe3bd
--- /dev/null
+++ b/include/zephyr/drivers/sensor/veml6046.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2025 Andreas Klinger
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include
+
+/**
+ * @file
+ * @brief Header file for extended sensor API of VEML6046 sensor
+ * @ingroup veml6046_interface
+ */
+
+#ifndef ZEPHYR_INCLUDE_DRIVERS_SENSOR_VEML6046_H_
+#define ZEPHYR_INCLUDE_DRIVERS_SENSOR_VEML6046_H_
+
+/**
+ * @defgroup veml6046_interface VEML6046
+ * @ingroup sensor_interface_ext
+ * @brief Vishay VEML6046 RGBIR Sensor
+ * @{
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief VEML6046 size options for light measurements.
+ *
+ * Possible values for @ref SENSOR_ATTR_VEML6046_PDD custom attribute.
+ */
+enum veml6046_pdd {
+ VEML6046_SIZE_2_2 = 0x00, /**< 2/2 photodiode size */
+ VEML6046_SIZE_1_2 = 0x01, /**< 1/2 photodiode size */
+ /** @cond INTERNAL_HIDDEN */
+ VEML6046_PDD_COUNT = 2,
+ /** @endcond */
+};
+
+/**
+ * @brief VEML6046 specific sensor attributes.
+ *
+ * For high and low threshold window settings (G_THDH_L, G_THDH_H, G_THDL_L and
+ * G_THDL_H) use the generic attributes @ref SENSOR_ATTR_UPPER_THRESH and
+ * @ref SENSOR_ATTR_LOWER_THRESH with 16-bit unsigned integer values. Both
+ * threshold settings are in lux and converted by the driver to a value
+ * compatible with the sensor. This conversion depends on the current gain,
+ * integration time and effective photodiode size settings. So a change in
+ * gain, integration time or effective photodiode size usually requires an
+ * update of threshold window settings. To get the correct threshold values
+ * into the sensor update the thresholds -after- a change of gain or
+ * integration time.
+ *
+ * When the sensor goes into saturation @c -E2BIG is returned. This happens
+ * when the maximum value @c 0xFFFF is returned as raw ALS value. In this case
+ * it's up to the user to reduce one or more of the following attributes to
+ * come back into the optimal measurement range of the sensor:
+ * @ref SENSOR_ATTR_VEML6046_GAIN (gain)
+ * @ref SENSOR_ATTR_VEML6046_IT (integration time)
+ * @ref SENSOR_ATTR_VEML6046_PDD (effective photodiode size)
+ */
+enum sensor_attribute_veml6046 {
+ /**
+ * @brief Integration time setting for measurements (IT).
+ *
+ * Use enum veml6046_it for attribute values.
+ */
+ SENSOR_ATTR_VEML6046_IT = SENSOR_ATTR_PRIV_START,
+ /**
+ * @brief Effective photodiode size (PDD)
+ *
+ * Use enum veml6046_pdd for attribute values.
+ */
+ SENSOR_ATTR_VEML6046_PDD,
+ /**
+ * @brief Gain setting for measurements (GAIN).
+ *
+ * Use enum veml6046_gain for attribute values.
+ */
+ SENSOR_ATTR_VEML6046_GAIN,
+ /**
+ * @brief Persistence protect number setting (PERS).
+ *
+ * Use enum veml6046_pers for attribute values.
+ */
+ SENSOR_ATTR_VEML6046_PERS,
+};
+
+/**
+ * @brief VEML6046 specific sensor channels.
+ */
+enum sensor_channel_veml6046 {
+ /**
+ * @brief Channel for raw red sensor values.
+ *
+ * This channel represents the raw measurement counts provided by the
+ * sensor register. It is useful for estimating good values for
+ * integration time, effective photodiode size and gain attributes in
+ * fetch and get mode.
+ *
+ * For future implementations with triggers it can also be used to
+ * estimate the threshold window attributes for the sensor interrupt
+ * handling.
+ *
+ * It cannot be fetched directly. Instead, this channel's value is
+ * fetched implicitly using @ref SENSOR_CHAN_RED.
+ * Trying to call sensor_channel_fetch_chan with this enumerator
+ * as an argument will result in a @c -ENOTSUP.
+ */
+ SENSOR_CHAN_VEML6046_RED_RAW_COUNTS = SENSOR_CHAN_PRIV_START,
+
+ /**
+ * @brief Channel for green sensor values.
+ *
+ * This channel is the raw green channel count output of the sensor.
+ * About fetching the same as for
+ * @ref SENSOR_CHAN_VEML6046_RED_RAW_COUNTS applies.
+ */
+ SENSOR_CHAN_VEML6046_GREEN_RAW_COUNTS,
+
+ /**
+ * @brief Channel for blue sensor values.
+ *
+ * This channel is the raw blue channel count output of the sensor.
+ * About fetching the same as for
+ * @ref SENSOR_CHAN_VEML6046_RED_RAW_COUNTS applies.
+ */
+ SENSOR_CHAN_VEML6046_BLUE_RAW_COUNTS,
+
+ /**
+ * @brief Channel for IR sensor values.
+ *
+ * This channel is the raw IR channel count output of the sensor. About
+ * fetching the same as for
+ * @ref SENSOR_CHAN_VEML6046_RED_RAW_COUNTS applies.
+ */
+ SENSOR_CHAN_VEML6046_IR_RAW_COUNTS,
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* ZEPHYR_INCLUDE_DRIVERS_SENSOR_VEML6046_H_ */
diff --git a/include/zephyr/drivers/sensor/veml60xx-common.h b/include/zephyr/drivers/sensor/veml60xx-common.h
new file mode 100644
index 0000000000000..0a9aea8dea99f
--- /dev/null
+++ b/include/zephyr/drivers/sensor/veml60xx-common.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2025 Andreas Klinger
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @file
+ * @brief Header file for extended sensor API of VEML60xx sensor family
+ * @ingroup veml60xx_interface
+ */
+
+#ifndef ZEPHYR_INCLUDE_DRIVERS_SENSOR_VEML60XX_H_
+#define ZEPHYR_INCLUDE_DRIVERS_SENSOR_VEML60XX_H_
+
+/**
+ * @defgroup veml60xx_interface VEML60XX
+ * @ingroup sensor_interface_ext
+ * @brief Vishay VEML60xx sensor family common attributes
+ * @{
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief VEML60xx integration time options for ambient light measurements.
+ *
+ * Possible values for @ref SENSOR_ATTR_VEML6031_IT and
+ * @ref SENSOR_ATTR_VEML6046_IT custom attribute.
+ */
+enum veml60xx_it {
+ VEML60XX_IT_3_125, /**< 3.125 ms */
+ VEML60XX_IT_6_25, /**< 6.25 ms */
+ VEML60XX_IT_12_5, /**< 12.5 ms */
+ VEML60XX_IT_25, /**< 25 ms */
+ VEML60XX_IT_50, /**< 50 ms */
+ VEML60XX_IT_100, /**< 100 ms */
+ VEML60XX_IT_200, /**< 200 ms */
+ VEML60XX_IT_400, /**< 400 ms */
+ /** @cond INTERNAL_HIDDEN */
+ VEML60XX_IT_COUNT,
+ /** @endcond */
+};
+
+/*
+ * @brief VEML60xx integration time struct.
+ */
+struct veml60xx_it_data {
+ enum veml60xx_it num;
+ uint8_t val;
+ int us;
+};
+
+/*
+ * @brief VEML60xx integration time setting values.
+ *
+ * The enumerators of enum veml60xx_it provide indices into this array to get
+ * the related value for the ALS_IT configuration bits.
+ */
+static const struct veml60xx_it_data veml60xx_it_values[VEML60XX_IT_COUNT] = {
+ {VEML60XX_IT_3_125, 0x00, 3125}, /* 3.125 - 0b0000 */
+ {VEML60XX_IT_6_25, 0x01, 6250}, /* 6.25 - 0b0001 */
+ {VEML60XX_IT_12_5, 0x02, 12500}, /* 12.5 - 0b0010 */
+ {VEML60XX_IT_25, 0x03, 25000}, /* 25 - 0b0011 */
+ {VEML60XX_IT_50, 0x04, 50000}, /* 50 - 0b0100 */
+ {VEML60XX_IT_100, 0x05, 100000}, /* 100 - 0b0101 */
+ {VEML60XX_IT_200, 0x06, 200000}, /* 200 - 0b0110 */
+ {VEML60XX_IT_400, 0x07, 400000}, /* 400 - 0b0111 */
+};
+/**
+ * @brief VEML60xx gain options for ambient light measurements.
+ */
+enum veml60xx_gain {
+ VEML60XX_GAIN_1 = 0x00, /**< 1x gain */
+ VEML60XX_GAIN_2 = 0x01, /**< 2x gain */
+ VEML60XX_GAIN_0_66 = 0x02, /**< 0.66x gain */
+ VEML60XX_GAIN_0_5 = 0x03, /**< 0.5x gain */
+ /** @cond INTERNAL_HIDDEN */
+ VEML60XX_GAIN_COUNT = 4,
+ /** @endcond */
+};
+
+/**
+ * @brief VEML60xx ALS interrupt persistence protect number options.
+ *
+ * Possible values for @ref SENSOR_ATTR_VEML6031_PERS and
+ * @ref SENSOR_ATTR_VEML6046_PERS custom attribute.
+ */
+enum veml60xx_pers {
+ VEML60XX_PERS_1 = 0x00, /**< 1 measurement */
+ VEML60XX_PERS_2 = 0x01, /**< 2 measurements */
+ VEML60XX_PERS_4 = 0x02, /**< 4 measurements */
+ VEML60XX_PERS_8 = 0x03, /**< 8 measurements */
+};
+
+
+static inline bool veml60xx_gain_in_range(int32_t gain)
+{
+ return (gain >= VEML60XX_GAIN_1) && (gain <= VEML60XX_GAIN_0_5);
+}
+
+static inline bool veml60xx_it_in_range(int32_t it)
+{
+ return (it >= VEML60XX_IT_3_125) && (it <= VEML60XX_IT_400);
+}
+
+static inline bool veml60xx_pers_in_range(int32_t pers)
+{
+ return (pers >= VEML60XX_PERS_1) && (pers <= VEML60XX_PERS_8);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* ZEPHYR_INCLUDE_DRIVERS_SENSOR_VEML60XX_H_ */
diff --git a/samples/sensor/veml6031/src/main.c b/samples/sensor/veml6031/src/main.c
index 97239aa32a05d..06168918f9bf9 100644
--- a/samples/sensor/veml6031/src/main.c
+++ b/samples/sensor/veml6031/src/main.c
@@ -65,9 +65,9 @@ static void read_with_all_attr(const struct device *dev)
{
int it, div4, gain;
- for (it = VEML6031_IT_3_125; it <= VEML6031_IT_400; it++) {
+ for (it = VEML60XX_IT_3_125; it <= VEML60XX_IT_400; it++) {
for (div4 = VEML6031_SIZE_4_4; div4 <= VEML6031_SIZE_1_4; div4++) {
- for (gain = VEML6031_GAIN_1; gain <= VEML6031_GAIN_0_5; gain++) {
+ for (gain = VEML60XX_GAIN_1; gain <= VEML60XX_GAIN_0_5; gain++) {
read_with_attr(dev, it, div4, gain);
}
}
diff --git a/samples/sensor/veml6046/CMakeLists.txt b/samples/sensor/veml6046/CMakeLists.txt
new file mode 100644
index 0000000000000..4aa3e0a7b7408
--- /dev/null
+++ b/samples/sensor/veml6046/CMakeLists.txt
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: Apache-2.0
+
+cmake_minimum_required(VERSION 3.20.0)
+find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
+project(veml6046)
+
+target_sources(app PRIVATE src/main.c)
diff --git a/samples/sensor/veml6046/README.rst b/samples/sensor/veml6046/README.rst
new file mode 100644
index 0000000000000..12f34edfc1759
--- /dev/null
+++ b/samples/sensor/veml6046/README.rst
@@ -0,0 +1,61 @@
+.. zephyr:code-sample:: veml6046
+ :name: VEML6046 RGBIR Color Sensor
+ :relevant-api: sensor_interface
+
+ Get red, green, blue and IR light data from a VEML6046 sensor (polling
+ mode).
+
+Overview
+********
+
+ This sample measures the red, green, blue and IR light for all possible
+ combinations of sensor attributes. They are:
+
+ - integration time
+ - effective photodiode size
+ - gain
+
+ These attributes can be used to put the sensor in an optimal working area.
+ When the light value reaches the maximum raw value (0xFFFF), an error is
+ returned to indicate the out of bounds situation to the user program.
+ With this program the raw value is also printed out together with the
+ attributes to be able to select good attribute values.
+ Interrupt and trigger modes are not supported so far, but planned for future
+ development.
+
+Requirements
+************
+
+ This sample uses the VEML6046 sensor controlled using the I2C-2 interface of
+ the Olimex-STM32-E407 board on Feather connector pins PF0 and PF1.
+
+References
+**********
+
+ - VEML6046: https://www.vishay.com/docs/80173/veml6046x00.pdf
+ - Application note: https://www.vishay.com/docs/80410/designingveml6046x00.pdf
+
+Building and Running
+********************
+
+ This project outputs sensor data to the console. It requires a VEML6046
+ sensor to be connected to the desired board.
+
+ .. zephyr-app-commands::
+ :zephyr-app: samples/sensor/veml6046/
+ :goals: build flash
+ :board: olimex_stm32_e407
+
+
+Sample Output
+=============
+
+ .. code-block:: console
+
+ Test all attributes for a good guess of attribute usage away of saturation.
+ Red: 68 lx ( 51) green: 68 lx ( 84) blue: 68 lx ( 51) IR: 68 lx ( 27) it: 0 pdd: 0 gain: 0 --
+ Red: 121 lx ( 181) green: 121 lx ( 347) blue: 121 lx ( 240) IR: 121 lx ( 53) it: 0 pdd: 0 gain: 1 --
+ Red: 215 lx ( 106) green: 215 lx ( 226) blue: 215 lx ( 160) IR: 215 lx ( 19) it: 0 pdd: 0 gain: 2 --
+ Red: 201 lx ( 75) green: 201 lx ( 156) blue: 201 lx ( 112) IR: 201 lx ( 14) it: 0 pdd: 0 gain: 3 --
+ [...]
+ Test finished.
diff --git a/samples/sensor/veml6046/boards/olimex_stm32_e407.overlay b/samples/sensor/veml6046/boards/olimex_stm32_e407.overlay
new file mode 100644
index 0000000000000..1960223382389
--- /dev/null
+++ b/samples/sensor/veml6046/boards/olimex_stm32_e407.overlay
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2025 Andreas Klinger
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+&pinctrl {
+ i2c2_sda_pf0: i2c2_sda_pf0 {
+ pinmux = < 0xa04 >;
+ bias-pull-up;
+ drive-open-drain;
+ };
+ i2c2_scl_pf1: i2c2_scl_pf1 {
+ pinmux = < 0xa24 >;
+ bias-pull-up;
+ drive-open-drain;
+ };
+};
+
+&i2c2 {
+ pinctrl-0 = < &i2c2_scl_pf1 &i2c2_sda_pf0 >;
+ pinctrl-names = "default";
+ status = "okay";
+
+ rgbir: rgbir@29 {
+ compatible = "vishay,veml6046";
+ reg = <0x29>;
+ };
+};
diff --git a/samples/sensor/veml6046/prj.conf b/samples/sensor/veml6046/prj.conf
new file mode 100644
index 0000000000000..42fcd3c973bcb
--- /dev/null
+++ b/samples/sensor/veml6046/prj.conf
@@ -0,0 +1 @@
+CONFIG_SENSOR=y
diff --git a/samples/sensor/veml6046/sample.yaml b/samples/sensor/veml6046/sample.yaml
new file mode 100644
index 0000000000000..fe2704301b11c
--- /dev/null
+++ b/samples/sensor/veml6046/sample.yaml
@@ -0,0 +1,10 @@
+sample:
+ name: VEML6046 Sensor Sample
+tests:
+ sample.sensor.veml6046:
+ harness: sensor
+ platform_allow: olimex_stm32_e407
+ integration_platforms:
+ - olimex_stm32_e407
+ tags: sensors
+ filter: dt_compat_enabled("vishay,veml6046")
diff --git a/samples/sensor/veml6046/src/main.c b/samples/sensor/veml6046/src/main.c
new file mode 100644
index 0000000000000..7ac637e37f798
--- /dev/null
+++ b/samples/sensor/veml6046/src/main.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2025 Andreas Klinger
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include
+
+#include
+#include
+
+#include
+#include
+
+#include
+
+static void read_with_attr(const struct device *dev, int it, int pdd, int gain)
+{
+ int ret;
+ struct sensor_value red, green, blue, ir;
+ struct sensor_value red_raw, green_raw, blue_raw, ir_raw;
+ struct sensor_value sen;
+ char result[10];
+
+ sen.val2 = 0;
+
+ sen.val1 = it;
+ ret = sensor_attr_set(dev, SENSOR_CHAN_LIGHT,
+ (enum sensor_attribute)SENSOR_ATTR_VEML6046_IT, &sen);
+ if (ret) {
+ printf("Failed to set it attribute ret: %d\n", ret);
+ }
+ sen.val1 = pdd;
+ ret = sensor_attr_set(dev, SENSOR_CHAN_LIGHT,
+ (enum sensor_attribute)SENSOR_ATTR_VEML6046_PDD, &sen);
+ if (ret) {
+ printf("Failed to set pdd attribute ret: %d\n", ret);
+ }
+ sen.val1 = gain;
+ ret = sensor_attr_set(dev, SENSOR_CHAN_LIGHT,
+ (enum sensor_attribute)SENSOR_ATTR_VEML6046_GAIN, &sen);
+ if (ret) {
+ printf("Failed to set gain attribute ret: %d\n", ret);
+ }
+
+ ret = sensor_sample_fetch(dev);
+ if ((ret < 0) && (ret != -E2BIG)) {
+ printf("sample update error. ret: %d\n", ret);
+ }
+
+ sensor_channel_get(dev, SENSOR_CHAN_RED, &red);
+ sensor_channel_get(dev, (enum sensor_channel)SENSOR_CHAN_VEML6046_RED_RAW_COUNTS,
+ &red_raw);
+
+ sensor_channel_get(dev, SENSOR_CHAN_GREEN, &green);
+ sensor_channel_get(dev, (enum sensor_channel)SENSOR_CHAN_VEML6046_GREEN_RAW_COUNTS,
+ &green_raw);
+
+ sensor_channel_get(dev, SENSOR_CHAN_BLUE, &blue);
+ sensor_channel_get(dev, (enum sensor_channel)SENSOR_CHAN_VEML6046_BLUE_RAW_COUNTS,
+ &blue_raw);
+
+ sensor_channel_get(dev, SENSOR_CHAN_IR, &ir);
+ sensor_channel_get(dev, (enum sensor_channel)SENSOR_CHAN_VEML6046_IR_RAW_COUNTS,
+ &ir_raw);
+
+ if (ret == -E2BIG) {
+ snprintf(result, sizeof(result), "OVERFLOW");
+ } else if (ret) {
+ snprintf(result, sizeof(result), "ERROR");
+ } else {
+ snprintf(result, sizeof(result), "");
+ }
+
+ printf("Red: %6d lx (%6d) green: %6d lx (%6d) "
+ "blue: %6d lx (%6d) IR: %6d lx (%6d) "
+ " it: %d pdd: %d gain: %d -- %s\n",
+ red.val1, red_raw.val1,
+ green.val1, green_raw.val1,
+ blue.val1, blue_raw.val1,
+ ir.val1, ir_raw.val1,
+ it, pdd, gain,
+ result);
+}
+
+static void read_with_all_attr(const struct device *dev)
+{
+ for (int it = VEML60XX_IT_3_125; it <= VEML60XX_IT_400; it++) {
+ for (int pdd = VEML6046_SIZE_2_2; pdd <= VEML6046_SIZE_1_2; pdd++) {
+ for (int gain = VEML60XX_GAIN_1; gain <= VEML60XX_GAIN_0_5; gain++) {
+ read_with_attr(dev, it, pdd, gain);
+ }
+ }
+ }
+}
+
+int main(void)
+{
+ const struct device *const veml = DEVICE_DT_GET(DT_NODELABEL(rgbir));
+
+ if (!device_is_ready(veml)) {
+ printk("sensor: device not ready.\n");
+ return 0;
+ }
+
+ printf("Test all attributes for a good guess of attribute usage away of saturation.\n");
+ read_with_all_attr(veml);
+ printf("Test finished.\n");
+
+ return 0;
+}
diff --git a/tests/drivers/build_all/sensor/i2c.dtsi b/tests/drivers/build_all/sensor/i2c.dtsi
index cf21635773b54..5f6afa816a0e9 100644
--- a/tests/drivers/build_all/sensor/i2c.dtsi
+++ b/tests/drivers/build_all/sensor/i2c.dtsi
@@ -1422,3 +1422,8 @@ test_i2c_als31300: als31300@bd {
compatible = "allegro,als31300";
reg = <0xbd>;
};
+
+test_i2c_veml6046: veml6046@be {
+ compatible = "vishay,veml6046";
+ reg = <0xbe>;
+};