Skip to content

Commit 36aa2d2

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 36aa2d2

File tree

3 files changed

+199
-0
lines changed

3 files changed

+199
-0
lines changed

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: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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
20+
21+
config MB7040_STATUS_GPIO
22+
bool "Enable status GPIO support"
23+
default y

drivers/sensor/mb7040/mb7040.c

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
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/devicetree.h>
14+
#include <zephyr/logging/log.h>
15+
#include <zephyr/drivers/gpio.h>
16+
17+
#define RANGE_CMD 0x51
18+
#define MB7040_HAS_STATUS_GPIO DT_ANY_INST_HAS_PROP_STATUS_OKAY(status_gpios)
19+
20+
LOG_MODULE_REGISTER(mb7040, LOG_LEVEL_DBG);
21+
22+
struct mb7040_data {
23+
uint16_t distance_cm;
24+
struct k_sem read_sem;
25+
#if MB7040_HAS_STATUS_GPIO
26+
struct gpio_callback gpio_cb;
27+
#endif
28+
};
29+
30+
struct mb7040_config {
31+
struct i2c_dt_spec i2c;
32+
#if MB7040_HAS_STATUS_GPIO
33+
struct gpio_dt_spec status_gpio;
34+
#endif
35+
};
36+
37+
#if MB7040_HAS_STATUS_GPIO
38+
static void status_gpio_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
39+
{
40+
struct mb7040_data *data = CONTAINER_OF(cb, struct mb7040_data, gpio_cb);
41+
k_sem_give(&data->read_sem);
42+
}
43+
#endif
44+
45+
static int mb7040_sample_fetch(const struct device *dev, enum sensor_channel chan)
46+
{
47+
const struct mb7040_config *cfg = (struct mb7040_config *)dev->config;
48+
struct mb7040_data *data = (struct mb7040_data *)dev->data;
49+
uint8_t cmd = RANGE_CMD;
50+
int ret;
51+
uint8_t read_data[2];
52+
53+
if (chan != SENSOR_CHAN_DISTANCE && chan != SENSOR_CHAN_ALL) {
54+
LOG_ERR("Sensor only supports distance");
55+
return -EINVAL;
56+
}
57+
58+
// reset sem before using it again
59+
k_sem_reset(&data->read_sem);
60+
61+
#if MB7040_HAS_STATUS_GPIO
62+
// Enable interrupt before writing
63+
ret = gpio_pin_interrupt_configure_dt(&cfg->status_gpio, GPIO_INT_EDGE_FALLING);
64+
if (ret != 0) {
65+
LOG_ERR("Failed to configure interrupt: %d", ret);
66+
return ret;
67+
}
68+
#endif
69+
70+
// Write range command to sensor
71+
ret = i2c_write_dt(&cfg->i2c, &cmd, 1);
72+
if (ret != 0) {
73+
LOG_ERR("I2C write failed with error %d", ret);
74+
return ret;
75+
}
76+
77+
k_sem_take(&data->read_sem, K_MSEC(CONFIG_MB7040_DELAY_MS));
78+
79+
#if MB7040_HAS_STATUS_GPIO
80+
gpio_pin_interrupt_configure_dt(&cfg->status_gpio, GPIO_INT_DISABLE);
81+
#endif
82+
83+
k_msleep(10);
84+
ret = i2c_read_dt(&cfg->i2c, read_data, 2);
85+
if (ret != 0) {
86+
LOG_ERR("I2C read failed with error %d", ret);
87+
return ret;
88+
}
89+
90+
// Convert MSB/LSB to distance in cm
91+
data->distance_cm = (read_data[0] << 8) | read_data[1];
92+
return 0;
93+
}
94+
95+
static int mb7040_channel_get(const struct device *dev, enum sensor_channel chan,
96+
struct sensor_value *val)
97+
{
98+
struct mb7040_data *data = (struct mb7040_data *)dev->data;
99+
100+
if (chan != SENSOR_CHAN_DISTANCE && SENSOR_CHAN_ALL != chan) {
101+
LOG_ERR("Sensor only supports distance");
102+
return -EINVAL;
103+
}
104+
105+
// check if measurment is complete
106+
val->val1 = data->distance_cm;
107+
val->val2 = 0;
108+
return 0;
109+
}
110+
111+
static const struct sensor_driver_api mb7040_api = {
112+
.sample_fetch = mb7040_sample_fetch,
113+
.channel_get = mb7040_channel_get,
114+
};
115+
116+
static int mb7040_init(const struct device *dev)
117+
{
118+
const struct mb7040_config *cfg = (struct mb7040_config *)dev->config;
119+
struct mb7040_data *data = (struct mb7040_data *)dev->data;
120+
121+
k_sem_init(&data->read_sem, 0, 1);
122+
123+
if (!i2c_is_ready_dt(&cfg->i2c)) {
124+
LOG_ERR("I2C not ready!\n");
125+
return -ENODEV;
126+
}
127+
128+
// Initialize status GPIO if present
129+
#if MB7040_HAS_STATUS_GPIO
130+
if (!gpio_is_ready_dt(&cfg->status_gpio)) {
131+
LOG_ERR("Status GPIO not ready");
132+
return -ENODEV;
133+
}
134+
135+
// configure gpio pin
136+
int ret = gpio_pin_configure_dt(&cfg->status_gpio, GPIO_INPUT);
137+
if (ret < 0) {
138+
LOG_ERR("Failed to configure status GPIO: %d", ret);
139+
return ret;
140+
}
141+
142+
// Initialize and add callback
143+
gpio_init_callback(&data->gpio_cb, status_gpio_callback, BIT(cfg->status_gpio.pin));
144+
ret = gpio_add_callback(cfg->status_gpio.port, &data->gpio_cb);
145+
if (ret < 0) {
146+
LOG_ERR("Failed to add GPIO callback: %d", ret);
147+
return ret;
148+
}
149+
150+
LOG_INF("MB7040 initialized with status GPIO");
151+
#else
152+
LOG_INF("MB7040 initialized");
153+
#endif
154+
return 0;
155+
}
156+
157+
#define MB7040_DEFINE(inst) \
158+
static struct mb7040_data mb7040_data_##inst = { \
159+
.distance_cm = 0, \
160+
}; \
161+
\
162+
static const struct mb7040_config mb7040_config_##inst = { \
163+
.i2c = I2C_DT_SPEC_INST_GET(inst), \
164+
IF_ENABLED(DT_INST_NODE_HAS_PROP(inst, status_gpios), \
165+
(.status_gpio = GPIO_DT_SPEC_INST_GET(inst, status_gpios),)) }; \
166+
\
167+
SENSOR_DEVICE_DT_INST_DEFINE(inst, mb7040_init, NULL, &mb7040_data_##inst, \
168+
&mb7040_config_##inst, POST_KERNEL, \
169+
CONFIG_SENSOR_INIT_PRIORITY, &mb7040_api);
170+
171+
DT_INST_FOREACH_STATUS_OKAY(MB7040_DEFINE)

0 commit comments

Comments
 (0)