1 /*
2  * Copyright (c) 2024 Adrien Leravat
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #include <zephyr/devicetree.h>
7 #include <zephyr/drivers/gpio.h>
8 #include <zephyr/drivers/gpio/gpio_emul.h>
9 #include <zephyr/drivers/sensor.h>
10 #include <zephyr/ztest.h>
11 
12 #if DT_NODE_HAS_STATUS(DT_INST(0, hc_sr04), okay)
13 #define HC_SR04 DT_NODELABEL(hc_sr04)
14 #define HC_SR04_GPIO_OUT DT_GPIO_CTLR(DT_INST(0, hc_sr04), trigger_gpios)
15 #define HC_SR04_PIN_OUT DT_GPIO_PIN(DT_INST(0, hc_sr04), trigger_gpios)
16 #define HC_SR04_GPIO_IN DT_GPIO_CTLR(DT_INST(0, hc_sr04), echo_gpios)
17 #define HC_SR04_PIN_IN DT_GPIO_PIN(DT_INST(0, hc_sr04), echo_gpios)
18 #else
19 #error "HC-SR04 not enabled"
20 #endif
21 
22 #define TEST_MEASURED_VALUE(fixture, value, duration_us, value1, value2)                   \
23 	fixture->emul.echo_duration_us = duration_us;                                      \
24 	zassert_false(sensor_sample_fetch(fixture->dev), "sensor_sample_fetch failed");    \
25 	zassert_false(sensor_channel_get(fixture->dev, SENSOR_CHAN_DISTANCE, &value),      \
26 			"sensor_channel_get failed");                                      \
27 	zassert_equal(value.val1, value1, "incorrect measurement for value.val1");         \
28 	zassert_within(value.val2, value2, 10000, "incorrect measurement for value.val2"); \
29 
30 struct hcsr04_emul {
31 	bool fail_echo;
32 	uint32_t echo_duration_us;
33 	struct gpio_callback cb;
34 };
35 
36 struct hcsr04_fixture {
37 	const struct device *dev;
38 	struct hcsr04_emul emul;
39 };
40 
41 static void gpio_emul_callback_handler(const struct device *port,
42 					struct gpio_callback *cb,
43 					gpio_port_pins_t pins);
44 
hcsr04_setup(void)45 static void *hcsr04_setup(void)
46 {
47 	static struct hcsr04_fixture fixture = {
48 		.dev = DEVICE_DT_GET(HC_SR04),
49 		.emul = {
50 			.fail_echo = false,
51 			.echo_duration_us = 0
52 		}
53 	};
54 	const struct device *gpio_dev = DEVICE_DT_GET(HC_SR04_GPIO_IN);
55 
56 	zassert_not_null(fixture.dev);
57 	zassert_not_null(gpio_dev);
58 	zassert_true(device_is_ready(fixture.dev));
59 	zassert_equal(DEVICE_DT_GET(HC_SR04_GPIO_IN), DEVICE_DT_GET(HC_SR04_GPIO_OUT),
60 			"Input and output GPIO devices must the same");
61 
62 	zassert_true(device_is_ready(gpio_dev), "GPIO dev is not ready");
63 
64 	gpio_init_callback(&fixture.emul.cb, &gpio_emul_callback_handler, BIT(HC_SR04_PIN_OUT));
65 	zassert_false(gpio_add_callback(gpio_dev, &fixture.emul.cb),
66 			"Failed to add emulation callback");
67 
68 	return &fixture;
69 }
70 
hcsr04_before(void * f)71 static void hcsr04_before(void *f)
72 {
73 	struct hcsr04_fixture *fixture = f;
74 
75 	fixture->emul.fail_echo = false;
76 }
77 
gpio_emul_callback_handler(const struct device * port,struct gpio_callback * cb,gpio_port_pins_t pins)78 static void gpio_emul_callback_handler(const struct device *port,
79 					struct gpio_callback *cb,
80 					gpio_port_pins_t pins)
81 {
82 	const struct hcsr04_emul *emul = CONTAINER_OF(cb, struct hcsr04_emul, cb);
83 
84 	if (emul->fail_echo) {
85 		return;
86 	}
87 	if (gpio_emul_output_get(port, HC_SR04_PIN_OUT) == 1) {
88 		/* Ignore rising edge */
89 		return;
90 	}
91 
92 	/* Output high-level on echo pin */
93 	gpio_emul_input_set(port, HC_SR04_PIN_IN, 1);
94 	k_busy_wait(emul->echo_duration_us);
95 	gpio_emul_input_set(port, HC_SR04_PIN_IN, 0);
96 }
97 
98 ZTEST_SUITE(hcsr04, NULL, hcsr04_setup, hcsr04_before, NULL, NULL);
99 
ZTEST_USER_F(hcsr04,test_sample_fetch_fail_no_echo)100 ZTEST_USER_F(hcsr04, test_sample_fetch_fail_no_echo)
101 {
102 	int ret;
103 
104 	fixture->emul.fail_echo = true;
105 
106 	ret = sensor_sample_fetch(fixture->dev);
107 	zassert_equal(-EIO, ret, "sensor_sample_fetch unexpected return code %d", ret);
108 }
109 
ZTEST_USER_F(hcsr04,test_sample_fetch)110 ZTEST_USER_F(hcsr04, test_sample_fetch)
111 {
112 	int ret;
113 
114 	ret = sensor_sample_fetch(fixture->dev);
115 	zassert_equal(0, ret, "sensor_sample_fetch unexpected return code %d", ret);
116 }
117 
ZTEST_USER_F(hcsr04,test_channel_get_fails_with_wrong_channel)118 ZTEST_USER_F(hcsr04, test_channel_get_fails_with_wrong_channel)
119 {
120 	int ret;
121 	struct sensor_value value;
122 
123 	ret = sensor_channel_get(fixture->dev, SENSOR_CHAN_ACCEL_X, &value);
124 	zassert_equal(-ENOTSUP, ret, "sensor_channel_get returned unexpected code with %d", ret);
125 }
126 
ZTEST_USER_F(hcsr04,test_channel_get_at_10cm)127 ZTEST_USER_F(hcsr04, test_channel_get_at_10cm)
128 {
129 	struct sensor_value value;
130 
131 	TEST_MEASURED_VALUE(fixture, value, 583, 0, 100000);
132 }
133 
ZTEST_USER_F(hcsr04,test_channel_get_at_150cm)134 ZTEST_USER_F(hcsr04, test_channel_get_at_150cm)
135 {
136 	struct sensor_value value;
137 
138 	TEST_MEASURED_VALUE(fixture, value, 8745, 1, 500000);
139 }
140