Skip to content

Commit bcab78e

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 <b.kereziev@gmail.com>
1 parent a1e66b4 commit bcab78e

File tree

7 files changed

+367
-0
lines changed

7 files changed

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

0 commit comments

Comments
 (0)