samples: sensor: veml6046: add attribution test application

- Test all attribute combinations of Vishay RGBIR color sensor VEML6046.
- Print OVERFLOW in case of saturation of sensor.
- This small program is intended to be helping when finding appropriate
  attributes for an application of the sensor.

Signed-off-by: Andreas Klinger <ak@it-klinger.de>
diff --git a/samples/sensor/veml6046/CMakeLists.txt b/samples/sensor/veml6046/CMakeLists.txt
new file mode 100644
index 0000000..4aa3e0a
--- /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 0000000..12f34ed
--- /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 0000000..1960223
--- /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 0000000..42fcd3c
--- /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 0000000..fe27043
--- /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 0000000..aec830e
--- /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 <zephyr/kernel.h>
+
+#include <zephyr/sys/printk.h>
+#include <stdio.h>
+
+#include <zephyr/device.h>
+#include <zephyr/drivers/sensor.h>
+
+#include <zephyr/drivers/sensor/veml6046.h>
+
+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 = VEML6046_IT_3_125; it <= VEML6046_IT_400; it++) {
+		for (int pdd = VEML6046_SIZE_2_2; pdd <= VEML6046_SIZE_1_2; pdd++) {
+			for (int gain = VEML6046_GAIN_1; gain <= VEML6046_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;
+}