Skip to content

Commit 252bf70

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 252bf70

File tree

3 files changed

+214
-0
lines changed

3 files changed

+214
-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: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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
24+

drivers/sensor/mb7040/mb7040.c

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

0 commit comments

Comments
 (0)