| /* |
| * Copyright (c) 2023 Ian Morris |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT renesas_hs300x |
| |
| #include <zephyr/device.h> |
| #include <zephyr/drivers/i2c.h> |
| #include <zephyr/kernel.h> |
| #include <zephyr/drivers/sensor.h> |
| #include <zephyr/sys/__assert.h> |
| #include <zephyr/logging/log.h> |
| #include <zephyr/sys/byteorder.h> |
| |
| #define HS300X_STATUS_MASK (BIT(0) | BIT(1)) |
| |
| LOG_MODULE_REGISTER(HS300X, CONFIG_SENSOR_LOG_LEVEL); |
| |
| struct hs300x_config { |
| struct i2c_dt_spec bus; |
| }; |
| |
| struct hs300x_data { |
| int16_t t_sample; |
| uint16_t rh_sample; |
| }; |
| |
| static int hs300x_read_sample(const struct device *dev, uint16_t *t_sample, uint16_t *rh_sample) |
| { |
| const struct hs300x_config *cfg = dev->config; |
| uint8_t rx_buf[4]; |
| int rc; |
| |
| rc = i2c_read_dt(&cfg->bus, rx_buf, sizeof(rx_buf)); |
| if (rc < 0) { |
| LOG_ERR("Failed to read data from device."); |
| return rc; |
| } |
| |
| if ((rx_buf[3] & HS300X_STATUS_MASK) != 0) { |
| LOG_ERR("Stale data"); |
| return -EIO; |
| } |
| |
| *rh_sample = sys_get_be16(rx_buf); |
| *t_sample = sys_get_be16(&rx_buf[2]); |
| |
| /* Remove status bits (only present in temperature value)*/ |
| *t_sample >>= 2; |
| |
| return 0; |
| } |
| |
| static int hs300x_sample_fetch(const struct device *dev, enum sensor_channel chan) |
| { |
| struct hs300x_data *data = dev->data; |
| const struct hs300x_config *cfg = dev->config; |
| int rc; |
| |
| if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_AMBIENT_TEMP && |
| chan != SENSOR_CHAN_HUMIDITY) { |
| return -ENOTSUP; |
| } |
| |
| /* |
| * Initiate a measurement simply by sending 7-bit address followed |
| * by an eighth bit set to 0 (write) and NO data. |
| */ |
| rc = i2c_write_dt(&cfg->bus, NULL, 0); |
| if (rc < 0) { |
| LOG_ERR("Failed to start measurement."); |
| return rc; |
| } |
| |
| /* |
| * According to datasheet maximum time to make temperature and humidity |
| * measurements is 33ms, add a little safety margin... |
| */ |
| k_msleep(50); |
| |
| rc = hs300x_read_sample(dev, &data->t_sample, &data->rh_sample); |
| if (rc < 0) { |
| LOG_ERR("Failed to fetch data."); |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| static void hs300x_temp_convert(struct sensor_value *val, int16_t raw) |
| { |
| int32_t micro_c; |
| |
| /* |
| * Convert to micro Celsius. See datasheet "Calculating Humidity and |
| * Temperature Output" section for more details on processing sample data. |
| */ |
| micro_c = (((int64_t)raw * 165000000) / 16383) - 40000000; |
| |
| val->val1 = micro_c / 1000000; |
| val->val2 = micro_c % 1000000; |
| } |
| |
| static void hs300x_rh_convert(struct sensor_value *val, uint16_t raw) |
| { |
| int32_t micro_rh; |
| |
| /* |
| * Convert to micro %RH. See datasheet "Calculating Humidity and |
| * Temperature Output" section for more details on processing sample data. |
| */ |
| micro_rh = ((uint64_t)raw * 100000000) / 16383; |
| |
| val->val1 = micro_rh / 1000000; |
| val->val2 = micro_rh % 1000000; |
| } |
| |
| static int hs300x_channel_get(const struct device *dev, enum sensor_channel chan, |
| struct sensor_value *val) |
| { |
| const struct hs300x_data *data = dev->data; |
| |
| if (chan == SENSOR_CHAN_AMBIENT_TEMP) { |
| hs300x_temp_convert(val, data->t_sample); |
| } else if (chan == SENSOR_CHAN_HUMIDITY) { |
| hs300x_rh_convert(val, data->rh_sample); |
| } else { |
| return -ENOTSUP; |
| } |
| |
| return 0; |
| } |
| |
| static int hs300x_init(const struct device *dev) |
| { |
| const struct hs300x_config *cfg = dev->config; |
| |
| if (!i2c_is_ready_dt(&cfg->bus)) { |
| LOG_ERR("I2C dev %s not ready", cfg->bus.bus->name); |
| return -ENODEV; |
| } |
| |
| return 0; |
| } |
| |
| static const struct sensor_driver_api hs300x_driver_api = {.sample_fetch = hs300x_sample_fetch, |
| .channel_get = hs300x_channel_get}; |
| |
| #define DEFINE_HS300X(n) \ |
| static struct hs300x_data hs300x_data_##n; \ |
| \ |
| static const struct hs300x_config hs300x_config_##n = {.bus = I2C_DT_SPEC_INST_GET(n)}; \ |
| \ |
| SENSOR_DEVICE_DT_INST_DEFINE(n, hs300x_init, NULL, &hs300x_data_##n, &hs300x_config_##n, \ |
| POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \ |
| &hs300x_driver_api); |
| |
| DT_INST_FOREACH_STATUS_OKAY(DEFINE_HS300X) |