diff --git a/drivers/sensor/CMakeLists.txt b/drivers/sensor/CMakeLists.txt index 0d3cec2a13d60..45d74897cfd1e 100644 --- a/drivers/sensor/CMakeLists.txt +++ b/drivers/sensor/CMakeLists.txt @@ -22,6 +22,7 @@ add_subdirectory(microchip) add_subdirectory(nordic) add_subdirectory(nuvoton) add_subdirectory(nxp) +add_subdirectory(omron) add_subdirectory(pixart) add_subdirectory(pni) add_subdirectory(realtek) diff --git a/drivers/sensor/Kconfig b/drivers/sensor/Kconfig index 338329cc91d61..c3e56e29f981a 100644 --- a/drivers/sensor/Kconfig +++ b/drivers/sensor/Kconfig @@ -108,6 +108,7 @@ source "drivers/sensor/microchip/Kconfig" source "drivers/sensor/nordic/Kconfig" source "drivers/sensor/nuvoton/Kconfig" source "drivers/sensor/nxp/Kconfig" +source "drivers/sensor/omron/Kconfig" source "drivers/sensor/pixart/Kconfig" source "drivers/sensor/pni/Kconfig" source "drivers/sensor/realtek/Kconfig" diff --git a/drivers/sensor/omron/CMakeLists.txt b/drivers/sensor/omron/CMakeLists.txt new file mode 100644 index 0000000000000..0237de521fa7f --- /dev/null +++ b/drivers/sensor/omron/CMakeLists.txt @@ -0,0 +1,6 @@ +# Copyright (c) 2025 Prevas A/S +# SPDX-License-Identifier: Apache-2.0 + +# zephyr-keep-sorted-start +add_subdirectory_ifdef(CONFIG_D6F d6f) +# zephyr-keep-sorted-stop diff --git a/drivers/sensor/omron/Kconfig b/drivers/sensor/omron/Kconfig new file mode 100644 index 0000000000000..fce9097a74efe --- /dev/null +++ b/drivers/sensor/omron/Kconfig @@ -0,0 +1,6 @@ +# Copyright (c) 2025 Prevas A/S +# SPDX-License-Identifier: Apache-2.0 + +# zephyr-keep-sorted-start +source "drivers/sensor/omron/d6f/Kconfig" +# zephyr-keep-sorted-stop diff --git a/drivers/sensor/omron/d6f/CMakeLists.txt b/drivers/sensor/omron/d6f/CMakeLists.txt new file mode 100644 index 0000000000000..42297ff12b2ac --- /dev/null +++ b/drivers/sensor/omron/d6f/CMakeLists.txt @@ -0,0 +1,5 @@ +# Copyright (c) 2025 Prevas A/S +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() +zephyr_library_sources(d6f.c) diff --git a/drivers/sensor/omron/d6f/Kconfig b/drivers/sensor/omron/d6f/Kconfig new file mode 100644 index 0000000000000..a89a0c55f1488 --- /dev/null +++ b/drivers/sensor/omron/d6f/Kconfig @@ -0,0 +1,12 @@ +# Copyright (c) 2025 Prevas A/S +# SPDX-License-Identifier: Apache-2.0 + +config D6F + bool "D6F mass flow rate sensor" + default y + depends on DT_HAS_OMRON_D6F_P0001_ENABLED || DT_HAS_OMRON_D6F_P0010_ENABLED + select ADC + select FPU if CPU_HAS_FPU + help + Enable Omron D6F mass flow rate sensor driver. The sensor series + outputs an analogue voltage which is read using an ADC. diff --git a/drivers/sensor/omron/d6f/d6f.c b/drivers/sensor/omron/d6f/d6f.c new file mode 100644 index 0000000000000..f89ca6c4d5dd8 --- /dev/null +++ b/drivers/sensor/omron/d6f/d6f.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2025 Prevas A/S + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(d6f, CONFIG_SENSOR_LOG_LEVEL); + +struct d6f_config { + const struct adc_dt_spec *adc; + struct adc_sequence sequence; + const float *polynomial_coefficients; + uint8_t polynomial_degree; +}; + +struct d6f_data { + uint32_t adc_sample; +}; + +static int d6f_sample_fetch(const struct device *dev, enum sensor_channel chan) +{ + const struct d6f_config *config = dev->config; + + switch (chan) { + case SENSOR_CHAN_ALL: + case SENSOR_CHAN_FLOW_RATE: + return adc_read_dt(config->adc, &config->sequence); + default: + return -ENOTSUP; + } +} + +static int d6f_flow_rate(const struct d6f_config *config, struct d6f_data *data, + struct sensor_value *val) +{ + float flow_rate = config->polynomial_coefficients[0]; + int32_t uv = data->adc_sample; + float v; + int rc; + + rc = adc_raw_to_microvolts_dt(config->adc, &uv); + if (rc != 0) { + return rc; + } + v = uv / 1000000.f; + + for (uint8_t i = 1; i <= config->polynomial_degree; ++i) { + flow_rate += config->polynomial_coefficients[i] * powf(v, i); + } + + rc = sensor_value_from_float(val, flow_rate); + if (rc != 0) { + return rc; + } + + return 0; +} + +static int d6f_channel_get(const struct device *dev, enum sensor_channel chan, + struct sensor_value *val) +{ + const struct d6f_config *config = dev->config; + struct d6f_data *data = dev->data; + + switch (chan) { + case SENSOR_CHAN_FLOW_RATE: + return d6f_flow_rate(config, data, val); + default: + return -ENOTSUP; + } + + return 0; +} + +static int d6f_init(const struct device *dev) +{ + const struct d6f_config *config = dev->config; + int rc; + + LOG_DBG("Initializing %s", dev->name); + + if (!adc_is_ready_dt(config->adc)) { + LOG_ERR("%s not ready", dev->name); + return -ENODEV; + } + + rc = adc_channel_setup_dt(config->adc); + if (rc != 0) { + LOG_ERR("%s setup failed: %d", config->adc->dev->name, rc); + return -ENODEV; + } + + return 0; +} + +static DEVICE_API(sensor, d6f_driver_api) = { + .sample_fetch = d6f_sample_fetch, + .channel_get = d6f_channel_get, +}; + +#define D6F_INIT(n, c, p) \ + static struct d6f_data d6f_data_##c##_##n; \ + static const struct adc_dt_spec d6f_adc_##c##_##n = ADC_DT_SPEC_INST_GET(n); \ + static const struct d6f_config d6f_config_##c##_##n = { \ + .adc = &d6f_adc_##c##_##n, \ + .sequence = \ + { \ + .options = NULL, \ + .channels = BIT(d6f_adc_##c##_##n.channel_id), \ + .buffer = &d6f_data_##c##_##n.adc_sample, \ + .buffer_size = sizeof(d6f_data_##c##_##n.adc_sample), \ + .resolution = d6f_adc_##c##_##n.resolution, \ + .oversampling = d6f_adc_##c##_##n.oversampling, \ + .calibrate = false, \ + }, \ + .polynomial_coefficients = (p), \ + .polynomial_degree = (ARRAY_SIZE(p) - 1), \ + }; \ + \ + SENSOR_DEVICE_DT_INST_DEFINE(n, d6f_init, NULL, &d6f_data_##c##_##n, \ + &d6f_config_##c##_##n, POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, &d6f_driver_api); + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT omron_d6f_p0001 +static __maybe_unused const float d6f_p0001_polynomial_coefficients[] = {-0.024864, 0.049944}; +DT_INST_FOREACH_STATUS_OKAY_VARGS(D6F_INIT, DT_DRV_COMPAT, d6f_p0001_polynomial_coefficients) + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT omron_d6f_p0010 +static __maybe_unused const float d6f_p0010_polynomial_coefficients[] = { + -0.269996, 1.060657, -1.601495, 1.374705, 1.374705, -0.564312, 0.094003}; +DT_INST_FOREACH_STATUS_OKAY_VARGS(D6F_INIT, DT_DRV_COMPAT, d6f_p0010_polynomial_coefficients) diff --git a/drivers/sensor/sensor_shell.c b/drivers/sensor/sensor_shell.c index 9d2657130c054..767b0da66b245 100644 --- a/drivers/sensor/sensor_shell.c +++ b/drivers/sensor/sensor_shell.c @@ -81,6 +81,7 @@ static const char *const sensor_channel_name[SENSOR_CHAN_COMMON_COUNT] = { [SENSOR_CHAN_O2] = "o2", [SENSOR_CHAN_VOC] = "voc", [SENSOR_CHAN_GAS_RES] = "gas_resistance", + [SENSOR_CHAN_FLOW_RATE] = "flow_rate", [SENSOR_CHAN_VOLTAGE] = "voltage", [SENSOR_CHAN_VSHUNT] = "vshunt", [SENSOR_CHAN_CURRENT] = "current", diff --git a/dts/bindings/sensor/omron,d6f-analog.yaml b/dts/bindings/sensor/omron,d6f-analog.yaml new file mode 100644 index 0000000000000..6122574234260 --- /dev/null +++ b/dts/bindings/sensor/omron,d6f-analog.yaml @@ -0,0 +1,14 @@ +# Copyright (c) 2025, Prevas A/S +# SPDX-License-Identifier: Apache-2.0 + +title: Omron D6F mass flow rate sensor. + +include: sensor-device.yaml + +properties: + io-channels: + type: phandle-array + required: true + description: | + ADC used to measure the mass flow rate: + <&adc_node channel> diff --git a/dts/bindings/sensor/omron,d6f-p0001.yaml b/dts/bindings/sensor/omron,d6f-p0001.yaml new file mode 100644 index 0000000000000..26f4e06a636f9 --- /dev/null +++ b/dts/bindings/sensor/omron,d6f-p0001.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2025, Prevas A/S +# SPDX-License-Identifier: Apache-2.0 + +title: Omron D6F-P0001A1 0.1 L/min flow rate sensor. + +compatible: "omron,d6f-p0001" + +include: omron,d6f-analog.yaml diff --git a/dts/bindings/sensor/omron,d6f-p0010.yaml b/dts/bindings/sensor/omron,d6f-p0010.yaml new file mode 100644 index 0000000000000..79f86b0f4809b --- /dev/null +++ b/dts/bindings/sensor/omron,d6f-p0010.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2025, Prevas A/S +# SPDX-License-Identifier: Apache-2.0 + +title: Omron D6F-P0010 1 L/min flow rate sensor. + +compatible: "omron,d6f-p0010" + +include: omron,d6f-analog.yaml diff --git a/dts/bindings/vendor-prefixes.txt b/dts/bindings/vendor-prefixes.txt index 21fe4c83139d9..1c8d0ccf47e78 100644 --- a/dts/bindings/vendor-prefixes.txt +++ b/dts/bindings/vendor-prefixes.txt @@ -492,6 +492,7 @@ okaya Okaya Electric America, Inc. oki Oki Electric Industry Co., Ltd. olimex OLIMEX Ltd. olpc One Laptop Per Child +omron OMRON Corporation onion Onion Corporation onnn ON Semiconductor Corp. ontat On Tat Industrial Company diff --git a/include/zephyr/drivers/sensor.h b/include/zephyr/drivers/sensor.h index adb0dc9791651..5e0a7e7a33926 100644 --- a/include/zephyr/drivers/sensor.h +++ b/include/zephyr/drivers/sensor.h @@ -130,6 +130,8 @@ enum sensor_channel { SENSOR_CHAN_VOC, /** Gas sensor resistance in ohms. */ SENSOR_CHAN_GAS_RES, + /** Flow rate in litres per minute */ + SENSOR_CHAN_FLOW_RATE, /** Voltage, in volts **/ SENSOR_CHAN_VOLTAGE, diff --git a/samples/sensor/sensor_shell/pytest/test_sensor_shell.py b/samples/sensor/sensor_shell/pytest/test_sensor_shell.py index 276d038ce0876..366c3a9876236 100644 --- a/samples/sensor/sensor_shell/pytest/test_sensor_shell.py +++ b/samples/sensor/sensor_shell/pytest/test_sensor_shell.py @@ -41,7 +41,7 @@ def test_sensor_shell_attr_get(shell: Shell): assert any(['sensor@0(channel=co2, attr=sampling_frequency)' in line for line in lines]), 'expected response not found' shell.wait_for_prompt() - lines = shell.exec_command('sensor attr_get sensor@1 54 3') + lines = shell.exec_command('sensor attr_get sensor@1 55 3') assert any(['sensor@1(channel=gauge_state_of_health, attr=slope_th)' in line for line in lines]), 'expected response not found' logger.info('response is valid') @@ -56,7 +56,7 @@ def test_sensor_shell_attr_set(shell: Shell): assert any([expected_line in line for line in lines]), 'expected response not found' shell.wait_for_prompt() - lines = shell.exec_command('sensor attr_set sensor@1 54 3 1') + lines = shell.exec_command('sensor attr_set sensor@1 55 3 1') expected_line = 'sensor@1 channel=gauge_state_of_health, attr=slope_th set to value=1' assert any([expected_line in line for line in lines]), 'expected response not found' diff --git a/tests/drivers/build_all/sensor/adc.dtsi b/tests/drivers/build_all/sensor/adc.dtsi index b2a6c59ed7440..b43f2a0c5548a 100644 --- a/tests/drivers/build_all/sensor/adc.dtsi +++ b/tests/drivers/build_all/sensor/adc.dtsi @@ -92,6 +92,18 @@ test_murata_ncp15wb473: murata-ncp15wb473 { connected-positive; }; +test_omron_d6f_p0001: test-omron-d6f-p0001 { + status = "okay"; + compatible = "omron,d6f-p0001"; + io-channels = <&test_adc 0>; +}; + +test_omron_d6f_p0010: test-omron-d6f-p0010 { + status = "okay"; + compatible = "omron,d6f-p0010"; + io-channels = <&test_adc 0>; +}; + test_tdk_ntcg163jf103ft1: tdk-ntcg163jf103ft1 { compatible = "tdk,ntcg163jf103ft1"; io-channels = <&test_adc 0>;