Skip to content

Commit 486a540

Browse files
committed
drivers: sensor: mb7040: add support for MB7040 ultrasonic sensor
This commit adds a new driver for the MaxBotix MB7040 ultrasonic rangefinder. The driver uses I2C communication to read range data from the sensor and exposes it via the Zephyr sensor API. Tested on an esp32-s3 board using I2C bus. Verified readings at multiple distances to confirm accuracy. Signed-off-by : Sabrina Simkhovich <sabrinasimkhovich@gmail.com>
1 parent 1207880 commit 486a540

File tree

7 files changed

+223
-0
lines changed

7 files changed

+223
-0
lines changed

drivers/sensor/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ add_subdirectory_ifdef(CONFIG_VEAA_X_3 veaa_x_3)
7171
add_subdirectory_ifdef(CONFIG_VOLTAGE_DIVIDER voltage_divider)
7272
add_subdirectory_ifdef(CONFIG_XBR818 xbr818)
7373
add_subdirectory_ifdef(CONFIG_TACH_ENE_KB1200 ene_tach_kb1200)
74+
add_subdirectory_ifdef(CONFIG_MB7040 mb7040)
7475

7576
zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/sensor.h)
7677

drivers/sensor/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ source "drivers/sensor/ist8310/Kconfig"
143143
source "drivers/sensor/lm35/Kconfig"
144144
source "drivers/sensor/lm75/Kconfig"
145145
source "drivers/sensor/lm77/Kconfig"
146+
source "drivers/sensor/mb7040/Kconfig"
146147
source "drivers/sensor/mhz19b/Kconfig"
147148
source "drivers/sensor/nct75/Kconfig"
148149
source "drivers/sensor/ntc_thermistor/Kconfig"

drivers/sensor/mb7040/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
zephyr_library()
4+
5+
zephyr_library_sources(mb7040.c)

drivers/sensor/mb7040/Kconfig

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# MB7040 ultrasonic sensor config
2+
3+
# Copyright 2025 Sabrina Simkhovich <sabrinasimkhovich@gmail.com>
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
config MB7040
7+
bool "MB7040 ultrasonic distance sensor"
8+
default y
9+
depends on DT_HAS_MAXBOTIX_MB7040_ENABLED
10+
select I2C
11+
help
12+
Enable driver for MB7040 distance sensor.
13+
14+
config MB7040_DELAY_MS
15+
int "Delay in milliseconds to wait for distance measurment"
16+
default 100
17+
depends on MB7040
18+
help
19+
Default delay if status-gpio is not defined

drivers/sensor/mb7040/mb7040.c

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/*
2+
* Copyright (c) 2025 Sabrina Simkhovich <sabrinasimkhovich@gmail.com>
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT maxbotix_mb7040
8+
9+
#include <zephyr/kernel.h>
10+
#include <zephyr/drivers/sensor.h>
11+
#include <zephyr/drivers/i2c.h>
12+
#include <zephyr/device.h>
13+
#include <zephyr/logging/log.h>
14+
#include <zephyr/drivers/gpio.h>
15+
16+
#define RANGE_CMD 0x51
17+
#define MB7040_HAS_STATUS_GPIO DT_ANY_INST_HAS_PROP_STATUS_OKAY(status_gpios)
18+
19+
LOG_MODULE_REGISTER(mb7040, LOG_LEVEL_DBG);
20+
21+
struct mb7040_data {
22+
uint16_t distance_cm;
23+
struct k_sem read_sem;
24+
#if MB7040_HAS_STATUS_GPIO
25+
struct gpio_callback gpio_cb;
26+
#endif
27+
};
28+
29+
struct mb7040_config {
30+
struct i2c_dt_spec i2c;
31+
#if MB7040_HAS_STATUS_GPIO
32+
struct gpio_dt_spec status_gpio;
33+
#endif
34+
};
35+
36+
#if MB7040_HAS_STATUS_GPIO
37+
static void status_gpio_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
38+
{
39+
struct mb7040_data *data = CONTAINER_OF(cb, struct mb7040_data, gpio_cb);
40+
k_sem_give(&data->read_sem);
41+
}
42+
#endif
43+
44+
static int mb7040_sample_fetch(const struct device *dev, enum sensor_channel chan)
45+
{
46+
const struct mb7040_config *cfg = (struct mb7040_config *)dev->config;
47+
struct mb7040_data *data = (struct mb7040_data *)dev->data;
48+
uint8_t cmd = RANGE_CMD;
49+
int ret;
50+
uint8_t read_data[2];
51+
52+
if (chan != SENSOR_CHAN_DISTANCE && chan != SENSOR_CHAN_ALL) {
53+
LOG_ERR("Sensor only supports distance");
54+
return -EINVAL;
55+
}
56+
57+
k_sem_reset(&data->read_sem);
58+
59+
#if MB7040_HAS_STATUS_GPIO
60+
/* Enable interrupt before writing */
61+
ret = gpio_pin_interrupt_configure_dt(&cfg->status_gpio, GPIO_INT_EDGE_FALLING);
62+
if (ret != 0) {
63+
LOG_ERR("Failed to configure interrupt: %d", ret);
64+
return ret;
65+
}
66+
#endif
67+
68+
/* Write range command to sensor */
69+
ret = i2c_write_dt(&cfg->i2c, &cmd, 1);
70+
if (ret != 0) {
71+
LOG_ERR("I2C write failed with error %d", ret);
72+
return ret;
73+
}
74+
75+
ret = k_sem_take(&data->read_sem, K_MSEC(CONFIG_MB7040_DELAY_MS));
76+
if (ret != 0) {
77+
if (ret == -EAGAIN) {
78+
LOG_ERR("Semaphore take timed out");
79+
return -ETIMEDOUT;
80+
} else {
81+
LOG_ERR("Semaphore take failed with error %d", ret);
82+
return ret;
83+
}
84+
}
85+
86+
#if MB7040_HAS_STATUS_GPIO
87+
gpio_pin_interrupt_configure_dt(&cfg->status_gpio, GPIO_INT_DISABLE);
88+
#endif
89+
90+
/* Small wait due to devie specific i2c timings */
91+
k_msleep(10);
92+
ret = i2c_read_dt(&cfg->i2c, read_data, 2);
93+
if (ret != 0) {
94+
LOG_ERR("I2C read failed with error %d", ret);
95+
return ret;
96+
}
97+
98+
/* Convert MSB/LSB to distance in cm */
99+
data->distance_cm = (read_data[0] << 8) | read_data[1];
100+
return 0;
101+
}
102+
103+
static int mb7040_channel_get(const struct device *dev, enum sensor_channel chan,
104+
struct sensor_value *val)
105+
{
106+
struct mb7040_data *data = (struct mb7040_data *)dev->data;
107+
108+
if (chan != SENSOR_CHAN_DISTANCE && SENSOR_CHAN_ALL != chan) {
109+
LOG_ERR("Sensor only supports distance");
110+
return -EINVAL;
111+
}
112+
113+
val->val1 = data->distance_cm;
114+
val->val2 = 0;
115+
return 0;
116+
}
117+
118+
static DEVICE_API(sensor, mb7040_api) = {
119+
.sample_fetch = mb7040_sample_fetch,
120+
.channel_get = mb7040_channel_get,
121+
};
122+
123+
static int mb7040_init(const struct device *dev)
124+
{
125+
const struct mb7040_config *cfg = (struct mb7040_config *)dev->config;
126+
struct mb7040_data *data = (struct mb7040_data *)dev->data;
127+
128+
k_sem_init(&data->read_sem, 0, 1);
129+
130+
if (!i2c_is_ready_dt(&cfg->i2c)) {
131+
LOG_ERR("I2C not ready!");
132+
return -ENODEV;
133+
}
134+
135+
/* Initialize status GPIO if present */
136+
#if MB7040_HAS_STATUS_GPIO
137+
if (!gpio_is_ready_dt(&cfg->status_gpio)) {
138+
LOG_ERR("Status GPIO not ready");
139+
return -ENODEV;
140+
}
141+
142+
int ret = gpio_pin_configure_dt(&cfg->status_gpio, GPIO_INPUT);
143+
if (ret < 0) {
144+
LOG_ERR("Failed to configure status GPIO: %d", ret);
145+
return ret;
146+
}
147+
148+
gpio_init_callback(&data->gpio_cb, status_gpio_callback, BIT(cfg->status_gpio.pin));
149+
ret = gpio_add_callback(cfg->status_gpio.port, &data->gpio_cb);
150+
if (ret < 0) {
151+
LOG_ERR("Failed to add GPIO callback: %d", ret);
152+
return ret;
153+
}
154+
155+
LOG_INF("MB7040 initialized with status GPIO");
156+
#else
157+
LOG_INF("MB7040 initialized");
158+
#endif
159+
return 0;
160+
}
161+
162+
#define MB7040_DEFINE(inst) \
163+
static struct mb7040_data mb7040_data_##inst = { \
164+
.distance_cm = 0, \
165+
}; \
166+
\
167+
static const struct mb7040_config mb7040_config_##inst = { \
168+
.i2c = I2C_DT_SPEC_INST_GET(inst), \
169+
IF_ENABLED(DT_INST_NODE_HAS_PROP(inst, status_gpios), \
170+
(.status_gpio = GPIO_DT_SPEC_INST_GET(inst, status_gpios),)) }; \
171+
\
172+
SENSOR_DEVICE_DT_INST_DEFINE(inst, mb7040_init, NULL, &mb7040_data_##inst, \
173+
&mb7040_config_##inst, POST_KERNEL, \
174+
CONFIG_SENSOR_INIT_PRIORITY, &mb7040_api);
175+
176+
DT_INST_FOREACH_STATUS_OKAY(MB7040_DEFINE)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
description: MB7040 ultrasonic distance sensor
2+
3+
compatible: "maxbotix,mb7040"
4+
5+
include: [sensor-device.yaml, i2c-device.yaml]
6+
7+
on-bus: i2c
8+
9+
properties:
10+
reg:
11+
description: I2C address of the MB7040 sensor. Zephyr expects 7-bit addressing and handles shifting internally.
12+
required: true
13+
14+
status-gpios:
15+
type: phandle-array
16+
description: GPIO pin connected to Pin 2 (Address Announce/Status) to monitor sensor state. Pin is high during range reading and low when ready for I2C communication.

tests/drivers/build_all/sensor/i2c.dtsi

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1331,3 +1331,8 @@ test_i2c_bh1730: bh1730@b4 {
13311331
compatible = "rohm,bh1730";
13321332
reg = <0xb4>;
13331333
};
1334+
1335+
test_i2c_mb7040: mb7040@b5 {
1336+
compatible = "maxbotix,mb7040";
1337+
reg = <0xb5>;
1338+
};

0 commit comments

Comments
 (0)