Skip to content

Commit 7b53806

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 7b53806

File tree

7 files changed

+356
-0
lines changed

7 files changed

+356
-0
lines changed

drivers/sensor/rohm/CMakeLists.txt

+1
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

+1
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
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

+12
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

+302
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
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+
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+
70+
uint8_t cmd = BH1730_CMD | (reg & BH1730_CMD_ADDR_MASK);
71+
return i2c_write_read_dt(&cfg->i2c, &cmd, sizeof(cmd), val, sizeof(*val));
72+
}
73+
74+
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+
78+
uint8_t cmd = BH1730_CMD | (reg & BH1730_CMD_ADDR_MASK);
79+
uint8_t buf[2] = {cmd, val};
80+
81+
return i2c_write_dt(&cfg->i2c, buf, sizeof(buf));
82+
}
83+
84+
static int bh1730_data_read(const struct device *dev, uint16_t *data0, uint16_t *data1)
85+
{
86+
const struct bh1730_config *cfg = dev->config;
87+
88+
/* Ensure data has been updated */
89+
uint8_t control_reg = 0x0;
90+
int err = bh1730_reg_read_8(dev, 0x0, &control_reg);
91+
92+
if (err) {
93+
LOG_ERR("Failed reading CONTROL register");
94+
return -EIO;
95+
}
96+
if ((control_reg & BH1730_CONTROL_ADC_DATA_UPDATED) == 0) {
97+
LOG_ERR("Data not updated");
98+
return -ENODATA;
99+
}
100+
101+
/* Read data0 and data1 bytes */
102+
uint8_t buffer[4] = {0};
103+
uint8_t cmd = BH1730_CMD | BH1730_REG_DATA0LOW;
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+
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+
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+
uint32_t bh1730_calculate_lux(uint16_t data0, uint16_t data1, uint8_t itime_reg, 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+
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_CONTORL_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+
k_msleep(sleep_period_ms);
214+
215+
/* Read conversion result from device */
216+
ret = bh1730_data_read(dev, &data->data0, &data->data1);
217+
if (ret) {
218+
LOG_ERR("Failed reading data from sensor");
219+
return -EIO;
220+
}
221+
222+
return 0;
223+
}
224+
225+
int bh1730_sample_get(const struct device *dev, enum sensor_channel chan, struct sensor_value *val)
226+
{
227+
struct bh1730_data *data = dev->data;
228+
const struct bh1730_config *cfg = dev->config;
229+
230+
if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_LIGHT) {
231+
return -ENOTSUP;
232+
}
233+
234+
uint32_t lux = bh1730_calculate_lux(data->data0, data->data1, cfg->itime, cfg->gain);
235+
val->val1 = lux;
236+
val->val2 = 0;
237+
238+
return 0;
239+
}
240+
241+
int bh1730_init(const struct device *dev)
242+
{
243+
const struct bh1730_config *cfg = dev->config;
244+
int ret = 0;
245+
246+
LOG_DBG("Initializing");
247+
248+
if (!device_is_ready(cfg->i2c.bus)) {
249+
LOG_ERR("I2C device not ready");
250+
return -ENODEV;
251+
}
252+
253+
/* Ensure BH1730 has valid ID */
254+
uint8_t val = 0;
255+
ret = bh1730_reg_read_8(dev, BH1730_REG_ID, &val);
256+
if (ret != 0) {
257+
LOG_ERR("Failed reading ID reg");
258+
return -ENODEV;
259+
}
260+
261+
/* Part number in bits 7:4 */
262+
if ((val >> 4) != (BH1730_PART_ID >> 4)) {
263+
LOG_ERR("Part number do not match, received 0x%1X", val >> 4);
264+
return -ENODEV;
265+
}
266+
267+
/* Configure part with gain */
268+
if (cfg->gain != BH1730_GAIN_DEFAULT) {
269+
ret = bh1730_reg_write_8(dev, BH1730_REG_GAIN, cfg->gain);
270+
if (ret) {
271+
LOG_ERR("Failed writing to gain register");
272+
return -EIO;
273+
}
274+
}
275+
276+
/* Configure part with itime */
277+
if (cfg->itime != BH1730_ITIME_DEFAULT) {
278+
ret = bh1730_reg_write_8(dev, BH1730_REG_TIMING, cfg->itime);
279+
if (ret) {
280+
LOG_ERR("Failed writing to ITIME register");
281+
return -EIO;
282+
}
283+
}
284+
285+
return ret;
286+
}
287+
288+
static const struct sensor_driver_api bh1730_api = {.sample_fetch = bh1730_sample_fetch,
289+
.channel_get = bh1730_sample_get};
290+
291+
#define BH1730_DEFINE(inst) \
292+
static struct bh1730_data bh1730_data_##inst; \
293+
static const struct bh1730_config bh1730_config_##inst = { \
294+
.i2c = I2C_DT_SPEC_INST_GET(inst), \
295+
.gain = DT_INST_PROP(inst, gain), \
296+
.itime = DT_INST_PROP(inst, itime), \
297+
}; \
298+
SENSOR_DEVICE_DT_INST_DEFINE(inst, bh1730_init, NULL, &bh1730_data_##inst, \
299+
&bh1730_config_##inst, POST_KERNEL, \
300+
CONFIG_SENSOR_INIT_PRIORITY, &bh1730_api);
301+
302+
DT_INST_FOREACH_STATUS_OKAY(BH1730_DEFINE)

dts/bindings/sensor/rohm,bh1730.yaml

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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 gain setting
16+
0 = x1 gain mode
17+
1 = x2 gain mode
18+
2 = x64 gain mode
19+
3 = x64 gain mode
20+
enum:
21+
- 0
22+
- 1
23+
- 2
24+
- 3
25+
26+
itime:
27+
type: int
28+
default: 0xDA
29+
description: |
30+
Integration time of measurement.

tests/drivers/build_all/sensor/i2c.dtsi

+5
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)