Skip to content

Commit 9e03ed6

Browse files
committed
drivers: sensor: Add support for BH1730 ambient light sensor
This commit adds support for BH1730 ambient light sensor. Signed-off-by: Borislav Kereziev <[email protected]>
1 parent fbdf4b9 commit 9e03ed6

File tree

7 files changed

+368
-0
lines changed

7 files changed

+368
-0
lines changed

drivers/sensor/rohm/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33

44
# zephyr-keep-sorted-start
55
add_subdirectory_ifdef(CONFIG_BD8LB600FS_DIAGNOSTICS bd8lb600fs)
6+
add_subdirectory_ifdef(CONFIG_BH1730 bh1730)
67
add_subdirectory_ifdef(CONFIG_BH1750 bh1750)
78
# zephyr-keep-sorted-stop

drivers/sensor/rohm/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33

44
# zephyr-keep-sorted-start
55
source "drivers/sensor/rohm/bd8lb600fs/Kconfig"
6+
source "drivers/sensor/rohm/bh1730/Kconfig"
67
source "drivers/sensor/rohm/bh1750/Kconfig"
78
# zephyr-keep-sorted-stop
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(bh1730.c)

drivers/sensor/rohm/bh1730/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# BH1730 ambient light sensor configuration options
2+
3+
# Copyright (c) 2025, Borislav Kereziev
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
config BH1730
7+
bool "BH1730 Ambient Light Sensor"
8+
default y
9+
depends on DT_HAS_ROHM_BH1730_ENABLED
10+
select I2C
11+
help
12+
Enable driver for BH1730 ambient light sensor.

drivers/sensor/rohm/bh1730/bh1730.c

Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
1+
/*
2+
* Copyright (c) 2025, Borislav Kereziev
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT rohm_bh1730
8+
9+
#include <errno.h>
10+
#include <zephyr/drivers/i2c.h>
11+
#include <zephyr/drivers/sensor.h>
12+
#include <zephyr/logging/log.h>
13+
#include <zephyr/sys/byteorder.h>
14+
15+
LOG_MODULE_REGISTER(BH1730, CONFIG_SENSOR_LOG_LEVEL);
16+
17+
#define BH1730_REG_CONTROL 0x00
18+
#define BH1730_REG_TIMING 0x01
19+
#define BH1730_REG_GAIN 0x07
20+
#define BH1730_REG_ID 0x12
21+
#define BH1730_REG_DATA0LOW 0x14
22+
#define BH1730_REG_DATA0HIGH 0x15
23+
#define BH1730_REG_DATA1LOW 0x16
24+
#define BH1730_REG_DATA1HIGH 0x17
25+
26+
#define BH1730_PART_ID 0x71
27+
#define BH1730_GAIN_DEFAULT 0x0
28+
#define BH1730_ITIME_DEFAULT 0xDA
29+
#define BH1730_CONTORL_ADC_EN_POWER_ON_SINGLE_READING 0x0B
30+
#define BH1730_CONTROL_ADC_EN_POWER_ON 0x03
31+
#define BH1730_CONTROL_ADC_DATA_UPDATED (1 << 4)
32+
#define BH1730_TINT 2.8E-9 /* Internal clock period Tint */
33+
34+
#define BH1730_CMD 0x80 /* Command register CMD bit */
35+
#define BH1730_CMD_ADDR_MASK 0x1F
36+
37+
/* Data definitions */
38+
39+
struct bh1730_data {
40+
uint16_t data0; /* visible light */
41+
uint16_t data1; /* infrared light */
42+
};
43+
44+
struct bh1730_config {
45+
struct i2c_dt_spec i2c;
46+
uint8_t gain;
47+
uint8_t itime;
48+
};
49+
50+
/* Prototypes */
51+
52+
static int bh1730_reg_read_8(const struct device *dev, uint8_t reg, uint8_t *val);
53+
static int bh1730_reg_write_8(const struct device *dev, uint8_t reg, uint8_t val);
54+
static int bh1730_data_read(const struct device *dev, uint16_t *data0, uint16_t *data1);
55+
static uint32_t bh1730_get_integration_time_ms(uint8_t itime_reg);
56+
static uint8_t bh1730_get_gain(uint8_t gain_reg_val);
57+
static uint32_t bh1730_calculate_lux(uint16_t data0, uint16_t data1, uint8_t itime_reg,
58+
uint8_t gain_reg);
59+
static int bh1730_sample_fetch(const struct device *dev, enum sensor_channel chan);
60+
static int bh1730_sample_get(const struct device *dev, enum sensor_channel chan,
61+
struct sensor_value *val);
62+
static int bh1730_init(const struct device *dev);
63+
64+
/* Functions */
65+
66+
static int bh1730_reg_read_8(const struct device *dev, uint8_t reg, uint8_t *val)
67+
{
68+
const struct bh1730_config *cfg = dev->config;
69+
uint8_t cmd = BH1730_CMD | (reg & BH1730_CMD_ADDR_MASK);
70+
71+
return i2c_write_read_dt(&cfg->i2c, &cmd, sizeof(cmd), val, sizeof(*val));
72+
}
73+
74+
static int bh1730_reg_write_8(const struct device *dev, uint8_t reg, uint8_t val)
75+
{
76+
const struct bh1730_config *cfg = dev->config;
77+
uint8_t cmd = BH1730_CMD | (reg & BH1730_CMD_ADDR_MASK);
78+
uint8_t buf[2] = {cmd, val};
79+
80+
return i2c_write_dt(&cfg->i2c, buf, sizeof(buf));
81+
}
82+
83+
static int bh1730_data_read(const struct device *dev, uint16_t *data0, uint16_t *data1)
84+
{
85+
const struct bh1730_config *cfg = dev->config;
86+
87+
/* Ensure data has been updated */
88+
uint8_t control_reg = 0x0;
89+
int err = bh1730_reg_read_8(dev, 0x0, &control_reg);
90+
91+
if (err) {
92+
LOG_ERR("Failed reading CONTROL register");
93+
return -EIO;
94+
}
95+
if ((control_reg & BH1730_CONTROL_ADC_DATA_UPDATED) == 0) {
96+
LOG_ERR("Data not updated");
97+
return -ENODATA;
98+
}
99+
100+
/* Read data0 and data1 bytes */
101+
uint8_t buffer[4] = {0};
102+
uint8_t cmd = BH1730_CMD | BH1730_REG_DATA0LOW;
103+
104+
err = i2c_write_read_dt(&cfg->i2c, &cmd, sizeof(cmd), &buffer, sizeof(buffer));
105+
if (err) {
106+
return -EIO;
107+
}
108+
109+
/* Data is shifted LSB first from sensor - swap */
110+
*data0 = (buffer[1] << 8 | buffer[0]);
111+
*data1 = (buffer[3] << 8 | buffer[2]);
112+
113+
return 0;
114+
}
115+
116+
static inline uint32_t bh1730_get_integration_time_ms(uint8_t itime_reg)
117+
{
118+
/* Convert register to integration time in ms */
119+
const uint32_t K_scaled = 2699200; /* 2.8e-6 * 964 * 1000 * 1e6 */
120+
uint32_t integration_time_ms;
121+
122+
integration_time_ms = (K_scaled * (256 - itime_reg)) / 1000000;
123+
124+
return integration_time_ms;
125+
}
126+
127+
static inline uint8_t bh1730_get_gain(uint8_t gain_reg_val)
128+
{
129+
/* Convert register value to gain */
130+
if (gain_reg_val == 0x0) {
131+
return 1;
132+
}
133+
if (gain_reg_val == 0x01) {
134+
return 2;
135+
}
136+
if (gain_reg_val == 0x02) {
137+
return 64;
138+
}
139+
if (gain_reg_val == 0x03) {
140+
return 128;
141+
}
142+
143+
return 1;
144+
}
145+
146+
static uint32_t bh1730_calculate_lux(uint16_t data0, uint16_t data1, uint8_t itime_reg,
147+
uint8_t gain_reg)
148+
{
149+
/* Check page 13 of datasheet for lux calculation formula */
150+
const uint32_t SCALE = 1000;
151+
const uint32_t scale1026 = 102600; /* 102.6 * 1000 */
152+
153+
/* prevent division by zero */
154+
if (data0 == 0) {
155+
return 0;
156+
}
157+
158+
uint32_t itime_ms = bh1730_get_integration_time_ms(itime_reg);
159+
uint8_t gain = bh1730_get_gain(gain_reg);
160+
161+
/* Compute ratio = (DATA1 / DATA0) scaled by 1000 */
162+
uint32_t ratio = ((uint32_t)data1 * SCALE) / data0;
163+
uint32_t k0_scaled, k1_scaled;
164+
165+
if (ratio < 260) {
166+
k0_scaled = 1290;
167+
k1_scaled = 2733;
168+
} else if (ratio < 550) {
169+
k0_scaled = 795;
170+
k1_scaled = 859;
171+
} else if (ratio < 1090) {
172+
k0_scaled = 510;
173+
k1_scaled = 345;
174+
} else if (ratio < 2130) {
175+
k0_scaled = 276;
176+
k1_scaled = 130;
177+
} else {
178+
return 0;
179+
}
180+
181+
/* Compute numerator (scaled values, may be negative → use int64_t) */
182+
int64_t numerator = (int64_t)data0 * k0_scaled - (int64_t)data1 * k1_scaled;
183+
uint64_t denominator = (uint64_t)gain * scale1026;
184+
185+
if (numerator <= 0 || denominator == 0) {
186+
return 0;
187+
}
188+
189+
uint32_t lux = (uint32_t)((numerator * itime_ms) / denominator);
190+
191+
return lux;
192+
}
193+
194+
static int bh1730_sample_fetch(const struct device *dev, enum sensor_channel chan)
195+
{
196+
struct bh1730_data *data = dev->data;
197+
const struct bh1730_config *cfg = dev->config;
198+
199+
if ((chan != SENSOR_CHAN_ALL) && (chan != SENSOR_CHAN_LIGHT)) {
200+
LOG_ERR("Unsupported channel %d", chan);
201+
return -ENOTSUP;
202+
}
203+
204+
/* Trigger measurement */
205+
int ret = bh1730_reg_write_8(dev, BH1730_REG_CONTROL,
206+
BH1730_CONTORL_ADC_EN_POWER_ON_SINGLE_READING);
207+
if (ret) {
208+
LOG_ERR("Failed writing to CONTROL register");
209+
return -EIO;
210+
}
211+
212+
/* Wait for conversion */
213+
int32_t sleep_period_ms = bh1730_get_integration_time_ms(cfg->itime);
214+
215+
k_msleep(sleep_period_ms);
216+
217+
/* Read conversion result from device */
218+
ret = bh1730_data_read(dev, &data->data0, &data->data1);
219+
if (ret) {
220+
LOG_ERR("Failed reading data from sensor");
221+
return -EIO;
222+
}
223+
224+
return 0;
225+
}
226+
227+
static int bh1730_sample_get(const struct device *dev, enum sensor_channel chan,
228+
struct sensor_value *val)
229+
{
230+
struct bh1730_data *data = dev->data;
231+
const struct bh1730_config *cfg = dev->config;
232+
233+
if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_LIGHT) {
234+
return -ENOTSUP;
235+
}
236+
237+
uint32_t lux = bh1730_calculate_lux(data->data0, data->data1, cfg->itime, cfg->gain);
238+
239+
val->val1 = lux;
240+
val->val2 = 0;
241+
242+
return 0;
243+
}
244+
245+
static int bh1730_init(const struct device *dev)
246+
{
247+
const struct bh1730_config *cfg = dev->config;
248+
int ret = 0;
249+
250+
LOG_DBG("Initializing");
251+
252+
if (!device_is_ready(cfg->i2c.bus)) {
253+
LOG_ERR("I2C device not ready");
254+
return -ENODEV;
255+
}
256+
257+
/* Ensure BH1730 has valid ID */
258+
uint8_t val = 0;
259+
260+
ret = bh1730_reg_read_8(dev, BH1730_REG_ID, &val);
261+
if (ret != 0) {
262+
LOG_ERR("Failed reading ID reg");
263+
return -ENODEV;
264+
}
265+
266+
/* Part number in bits 7:4 */
267+
if ((val >> 4) != (BH1730_PART_ID >> 4)) {
268+
LOG_ERR("Part number does not match, received 0x%1X", val >> 4);
269+
return -ENODEV;
270+
}
271+
272+
/* Configure part with gain */
273+
if (cfg->gain != BH1730_GAIN_DEFAULT) {
274+
ret = bh1730_reg_write_8(dev, BH1730_REG_GAIN, cfg->gain);
275+
if (ret) {
276+
LOG_ERR("Failed writing to gain register");
277+
return -EIO;
278+
}
279+
}
280+
281+
/* Configure part with itime */
282+
if (cfg->itime != BH1730_ITIME_DEFAULT) {
283+
ret = bh1730_reg_write_8(dev, BH1730_REG_TIMING, cfg->itime);
284+
if (ret) {
285+
LOG_ERR("Failed writing to ITIME register");
286+
return -EIO;
287+
}
288+
}
289+
290+
return ret;
291+
}
292+
293+
static const struct sensor_driver_api bh1730_api = {.sample_fetch = bh1730_sample_fetch,
294+
.channel_get = bh1730_sample_get};
295+
296+
#define BH1730_DEFINE(inst) \
297+
static struct bh1730_data bh1730_data_##inst; \
298+
static const struct bh1730_config bh1730_config_##inst = { \
299+
.i2c = I2C_DT_SPEC_INST_GET(inst), \
300+
.gain = DT_INST_PROP(inst, gain), \
301+
.itime = DT_INST_PROP(inst, itime), \
302+
}; \
303+
SENSOR_DEVICE_DT_INST_DEFINE(inst, bh1730_init, NULL, &bh1730_data_##inst, \
304+
&bh1730_config_##inst, POST_KERNEL, \
305+
CONFIG_SENSOR_INIT_PRIORITY, &bh1730_api);
306+
307+
DT_INST_FOREACH_STATUS_OKAY(BH1730_DEFINE)

dts/bindings/sensor/rohm,bh1730.yaml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Copyright (c) 2025, Borislav Kereziev
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: Rohm BH1730 ambient light sensor.
5+
6+
compatible: "rohm,bh1730"
7+
8+
include: [sensor-device.yaml, i2c-device.yaml]
9+
10+
properties:
11+
gain:
12+
type: int
13+
default: 0
14+
description: |
15+
ADC resolution setting
16+
0 = x1 gain mode
17+
1 = x2 gain mode
18+
2 = x64 gain mode
19+
3 = x128 gain mode
20+
Lower gain values (x1 and x2) reduce sensitivity and prevent
21+
saturation in brighter environments.
22+
Higher gain values (x64 and x128) result in higher sensitivity,
23+
which are useful in low-light environments.
24+
The default corresponds to the reset value of the GAIN register.
25+
enum:
26+
- 0
27+
- 1
28+
- 2
29+
- 3
30+
itime:
31+
type: int
32+
default: 0xDA
33+
description: |
34+
ITIME value in TIMING register determines integration time.
35+
Higher values result in longer integration time and
36+
increased accuracy.
37+
The default corresponds to the reset value of the TIMING register.

tests/drivers/build_all/sensor/i2c.dtsi

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1296,3 +1296,8 @@ test_i2c_icm45686: icm45686@b1 {
12961296
reg = <0xb1>;
12971297
int-gpios = <&test_gpio 0 0>;
12981298
};
1299+
1300+
test_i2c_bh1730: bh1730@b2 {
1301+
compatible = "rohm,bh1730";
1302+
reg = <0xb2>;
1303+
};

0 commit comments

Comments
 (0)