Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion drivers/sensor/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ add_subdirectory_ifdef(CONFIG_AMG88XX amg88xx)
add_subdirectory_ifdef(CONFIG_AMS_AS5600 ams_as5600)
add_subdirectory_ifdef(CONFIG_AMS_IAQ_CORE ams_iAQcore)
add_subdirectory_ifdef(CONFIG_APDS9960 apds9960)
add_subdirectory_ifdef(CONFIG_BH1750 bh1750)
add_subdirectory_ifdef(CONFIG_BH1750 bh1750)
add_subdirectory_ifdef(CONFIG_BMA280 bma280)
add_subdirectory_ifdef(CONFIG_BMC150_MAGN bmc150_magn)
add_subdirectory_ifdef(CONFIG_BME280 bme280)
Expand Down
3 changes: 2 additions & 1 deletion drivers/sensor/bh1750/Kconfig
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# BH1750 ambient light sensor configuration options

# Copyright (c) 2022 Michal Morsisko
# Copyright (c) 2023 Sngular People SL. Jeronimo Agullo
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You haven't made any changes to this file; don't add your copyright.

#
# SPDX-License-Identifier: Apache-2.0

config BH1750
Expand Down
306 changes: 153 additions & 153 deletions drivers/sensor/bh1750/bh1750.c
Original file line number Diff line number Diff line change
@@ -1,222 +1,222 @@
/* bh1750.c - Driver for BH1750 ambient light sensor */
/*
* Copyright (c) 2022, Michal Morsisko
* Copyright (c) 2023, Sngular People SL. Jeronimo Agullo
*
* SPDX-License-Identifier: Apache-2.0
*/

#define DT_DRV_COMPAT rohm_bh1750

#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/logging/log.h>
#include <zephyr/kernel.h>
#include <zephyr/devicetree.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/util_macro.h>

LOG_MODULE_REGISTER(BH1750, CONFIG_SENSOR_LOG_LEVEL);

#define BH1750_CONTINUOUS_HIGH_RES_MODE 0x10
#define BH1750_CONTINUOUS_HIGH_RES_MODE_2 0x11
#define BH1750_CONTINUOUS_LOW_RES_MODE 0x13
#define BH1750_ONE_TIME_HIGH_RES_MODE 0x20
#define BH1750_ONE_TIME_HIGH_RES_MODE_2 0x21
#define BH1750_ONE_TIME_LOW_RES_MODE 0x23
#define BH1750_MTREG_HIGH_BYTE 0x40
#define BH1750_MTREG_LOW_BYTE 0x60
#define BH1750_MTREG_HIGH_BYTE_MASK 0xE0
#define BH1750_MTREG_LOW_BYTE_MASK 0x1F

#define BH1750_DEFAULT_MTREG 69U
#define BH1750_LOW_RES_MODE_MAX_WAIT 24U
#define BH1750_HIGH_RES_MODE_MAX_WAIT 180U
#define BH1750_LOW_RES_MODE_TYPICAL_WAIT 16U
#define BH1750_HIGH_RES_MODE_TYPICAL_WAIT 120U

#define BH1750_LOW_RES_DTS_ENUM 0U
#define BH1750_HIGH_RES_DTS_ENUM 1U
#define BH1750_HIGH_RES_2_DTS_ENUM 2U

struct bh1750_dev_config {
struct i2c_dt_spec bus;
uint8_t resolution;
uint8_t mtreg;
};
#define BH1750_POWER_DOWN 0x00
#define BH1750_POWER_ON 0x01
#define BH1750_RESET 0x07

struct bh1750_data {
uint16_t sample;
};
#define BH1750_MTREG_HIGH_BYTE 0x40
#define BH1750_MTREG_LOW_BYTE 0x60

static int bh1750_opcode_read(const struct device *dev, uint8_t opcode,
uint16_t *val)
{
const struct bh1750_dev_config *cfg = dev->config;
int rc;

rc = i2c_burst_read_dt(&cfg->bus, opcode, (uint8_t *)val, 2);
if (rc < 0) {
return rc;
}
enum bh1750_resolution {
BH1750_H_RESOLUTION_MODE = 0,
BH1750_H_RESOLUTION_MODE2 = 1,
BH1750_L_RESOLUTION_MODE = 3
};

*val = sys_be16_to_cpu(*val);
return 0;
}
enum bh1750_mode {
BH1750_MODE_CONTINUOUSLY = 0,
BH1750_MODE_ONE_TIME = 1
};

static int bh1750_opcode_write(const struct device *dev, uint8_t opcode)
{
const struct bh1750_dev_config *cfg = dev->config;
struct bh1750_data {
float ambient_light;
};

return i2c_write_dt(&cfg->bus, &opcode, 1);
}
struct bh1750_config {
struct i2c_dt_spec i2c;
enum bh1750_resolution resolution;
enum bh1750_mode mode;
uint8_t mtreg;
};

static int bh1750_mtreg_write(const struct device *dev, uint8_t mtreg)
static int bh1750_channel_get(const struct device *dev,
enum sensor_channel chan,
struct sensor_value *val)
{
int rc;
uint8_t high_byte = mtreg & BH1750_MTREG_HIGH_BYTE_MASK;
uint8_t low_byte = mtreg & BH1750_MTREG_LOW_BYTE_MASK;
struct bh1750_data *data = dev->data;

rc = bh1750_opcode_write(dev, BH1750_MTREG_HIGH_BYTE | (high_byte >> 5));
if (rc < 0) {
LOG_ERR("%s, Failed to write high byte of mtreg!",
dev->name);
return rc;
if (chan != SENSOR_CHAN_LIGHT) {
LOG_ERR("incorrect channel, please set SENSOR_CHAN_LIGHT");
return -ENOTSUP;
}

rc = bh1750_opcode_write(dev, BH1750_MTREG_LOW_BYTE | low_byte);
if (rc < 0) {
LOG_ERR("%s, Failed to write low byte of mtreg!",
dev->name);
return rc;
}
val->val1 = (int32_t)(data->ambient_light);
val->val2 = (data->ambient_light - val->val1) * 1000000;

return 0;
}

static uint8_t bh1750_get_mode_from_dts_device(const struct device *dev)
{
const struct bh1750_dev_config *cfg = dev->config;

if (cfg->resolution == BH1750_HIGH_RES_2_DTS_ENUM) {
return BH1750_ONE_TIME_HIGH_RES_MODE_2;
} else if (cfg->resolution == BH1750_HIGH_RES_DTS_ENUM) {
return BH1750_ONE_TIME_HIGH_RES_MODE;
} else {
return BH1750_ONE_TIME_LOW_RES_MODE;
}
}

static int bh1750_get_wait_time_from_dts_device(const struct device *dev)
{
const struct bh1750_dev_config *cfg = dev->config;

if (cfg->resolution == BH1750_HIGH_RES_2_DTS_ENUM) {
return BH1750_HIGH_RES_MODE_MAX_WAIT;
} else if (cfg->resolution == BH1750_HIGH_RES_DTS_ENUM) {
return BH1750_HIGH_RES_MODE_MAX_WAIT;
} else {
return BH1750_LOW_RES_MODE_MAX_WAIT;
}
}

static int bh1750_sample_fetch(const struct device *dev,
enum sensor_channel chan)
enum sensor_channel chan)
{
const struct bh1750_dev_config *cfg = dev->config;
struct bh1750_data *drv_data = dev->data;
const int max_wait = bh1750_get_wait_time_from_dts_device(dev);
const uint8_t mode = bh1750_get_mode_from_dts_device(dev);
int rc;
int wait_time;
const struct bh1750_config *cfg = dev->config;
uint8_t buf[2];
int rc = 0;

if (chan != SENSOR_CHAN_ALL &&
chan != SENSOR_CHAN_LIGHT) {
return -ENOTSUP;
}
/* read raw value data */
rc = i2c_read_dt(&cfg->i2c, buf, 2);

/* Start the measurement */
rc = bh1750_opcode_write(dev, mode);
if (rc < 0) {
LOG_ERR("%s, Failed to start measurement!",
dev->name);
return rc;
LOG_ERR("Failed to read data sample.");
return -EIO;
}

/* Calculate measurement time */
wait_time = (max_wait * (cfg->mtreg * 10000 / BH1750_DEFAULT_MTREG)) / 10000;
/* We need to divide by 1.2 since it is the accuracy according to datasheet */
float data_value = (((uint16_t)buf[0] << 8) | buf[1]) / 1.2;

/* Wait for the measurement to be stored in the sensor memory */
k_msleep(wait_time);

/* Fetch result */
rc = bh1750_opcode_read(dev, mode, &drv_data->sample);
if (rc < 0) {
LOG_ERR("%s: Failed to read measurement result!",
dev->name);
return rc;
/* We need to divide by 2 in case of resolution mode 2 */
if (cfg->resolution == BH1750_H_RESOLUTION_MODE2) {
data_value /= 2;
}

LOG_DBG("Light value: %f", data_value);
drv_data->ambient_light = data_value;

return 0;
}

static int bh1750_channel_get(const struct device *dev,
enum sensor_channel chan,
struct sensor_value *val)
static int bh1750_attr_set(const struct device *dev, enum sensor_channel chan,
enum sensor_attribute attr, const struct sensor_value *val)
{
const struct bh1750_dev_config *cfg = dev->config;
struct bh1750_data *drv_data = dev->data;
uint32_t tmp;
const struct bh1750_config *cfg = dev->config;
uint8_t mode;
uint8_t buf;
int rc = 0;

if (chan != SENSOR_CHAN_ALL &&
chan != SENSOR_CHAN_LIGHT) {
if (chan != SENSOR_CHAN_LIGHT) {
return -ENOTSUP;
}

/* See datasheet (Technical note 11046EDT01), page 11
* https://www.mouser.com/datasheet/2/348/Rohm_11162017_ROHMS34826-1-1279292.pdf
* for more information how to convert raw sample to lx
*/
tmp = (drv_data->sample * 1000 / 12) * (BH1750_DEFAULT_MTREG * 100 / cfg->mtreg);
if (attr == SENSOR_ATTR_CONFIGURATION) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could benefit from a more meaningful name, something like MEASUREMENT_MODE


/* value in val1 sets the measurement mode */
if (val->val1 != 0) {
/* value in val1 sets the sensor mode */
if (val->val1 == BH1750_MODE_CONTINUOUSLY) {
mode = BIT(4);
LOG_DBG("setting continuously mode");
} else if (val->val1 == BH1750_MODE_ONE_TIME) {
mode = BIT(5);
LOG_DBG("setting one time mode");
} else {
return -EINVAL;
}
buf = cfg->resolution | mode;
rc = i2c_write_dt(&cfg->i2c, &buf, 1);
if (rc < 0) {
LOG_ERR("Error writing in i2c bus: %d", rc);
return -ENXIO;
}
LOG_INF("The mode was set correctly: 0x%x", buf);
}

/* value in val2 sets the mtreg time only if it is not zero */
if (val->val2 != 0) {
LOG_INF("mtreg: 0x%x", val->val2);
/* mtreg high bit */
LOG_INF("mask1: 0x%x", GENMASK(7, 5));
buf = BH1750_MTREG_HIGH_BYTE | ((val->val2 & GENMASK(7, 5)) >> 5);
LOG_INF("buf high: 0x%x", buf);
rc = i2c_write_dt(&cfg->i2c, &buf, 1);

/* mtreg low bit */
buf = BH1750_MTREG_LOW_BYTE | (val->val2 & GENMASK(4, 0));
LOG_INF("buf low: 0x%x", buf);
rc = i2c_write_dt(&cfg->i2c, &buf, 1);
LOG_INF("The mode was set correctly: 0x%x", buf);
}

if (cfg->resolution == BH1750_HIGH_RES_2_DTS_ENUM) {
tmp /= 2;
} else {
return -ENOTSUP;
}

val->val1 = tmp / 10000;
val->val2 = (tmp % 10000) * 100;

/* NOTE: in case of BH1750_MODE_ONE_TIME,
* we need to wait for the new measurement to be taken before calling sample fetch
* According to datasheet page 5 section "Measurement mode explanation"
* 120 ms for H_RESOLUTION_MODE and H_RESOLUTION_MODE2
* 16 ms for L_RESOLUTION_MODE
*/
return 0;
}

static const struct sensor_driver_api bh1750_driver_api = {
.sample_fetch = bh1750_sample_fetch,
.channel_get = bh1750_channel_get
.channel_get = bh1750_channel_get,
.attr_set = bh1750_attr_set,
};

static int bh1750_init(const struct device *dev)
{
const struct bh1750_dev_config *cfg = dev->config;
const struct bh1750_config *cfg = dev->config;
uint8_t mode;
int rc = 0;

if (!device_is_ready(cfg->bus.bus)) {
LOG_ERR("I2C dev %s not ready", cfg->bus.bus->name);
if (!device_is_ready(cfg->i2c.bus)) {
LOG_ERR("Bus device is not ready");
return -ENODEV;
}

if (cfg->mtreg != BH1750_DEFAULT_MTREG) {
bh1750_mtreg_write(dev, cfg->mtreg);
LOG_INF("Bus device is ready");

/* Set mode */
if (cfg->mode == BH1750_MODE_CONTINUOUSLY) {
mode = BIT(4);
LOG_DBG("setting continuously mode");
} else if (cfg->mode == BH1750_MODE_ONE_TIME) {
mode = BIT(5);
LOG_DBG("setting one time mode");
} else {
return -EINVAL;
}

uint8_t buf = cfg->resolution | mode;

rc = i2c_write_dt(&cfg->i2c, &buf, 1);
if (rc < 0) {
LOG_ERR("Error writing in i2c bus: %d", rc);
return -ENXIO;
}

LOG_INF("The mode was set correctly: 0x%x", buf);

return 0;
}

#define DEFINE_BH1750(_num) \
static struct bh1750_data bh1750_data_##_num; \
static const struct bh1750_dev_config bh1750_config_##_num = { \
.bus = I2C_DT_SPEC_INST_GET(_num), \
.mtreg = DT_INST_PROP(_num, mtreg), \
.resolution = DT_INST_PROP(_num, resolution) \
}; \
SENSOR_DEVICE_DT_INST_DEFINE(_num, bh1750_init, NULL, \
&bh1750_data_##_num, &bh1750_config_##_num, POST_KERNEL, \
CONFIG_SENSOR_INIT_PRIORITY, &bh1750_driver_api);

DT_INST_FOREACH_STATUS_OKAY(DEFINE_BH1750)
#define BH1750_DEFINE(inst) \
static struct bh1750_data bh1750_data_##inst; \
\
/* pull instance configuration from Devicetree */ \
static const struct bh1750_config bh1750_config_##inst = { \
.i2c = I2C_DT_SPEC_INST_GET(inst), \
.resolution = DT_INST_PROP(inst, resolution), \
.mode = DT_INST_PROP(inst, mode), \
.mtreg = DT_INST_PROP(inst, mtreg), \
}; \
\
/* define a new device inst. with the data and config defined above*/ \
SENSOR_DEVICE_DT_INST_DEFINE(inst, bh1750_init, NULL, \
&bh1750_data_##inst, &bh1750_config_##inst, \
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \
&bh1750_driver_api); \

DT_INST_FOREACH_STATUS_OKAY(BH1750_DEFINE)
Loading