/* * Copyright (c) 2024 Jan Fäh * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT sensirion_sts4x #include #include #include #include #include #include #include #include #include LOG_MODULE_REGISTER(STS4X, CONFIG_SENSOR_LOG_LEVEL); #define STS4X_CMD_RESET 0x94 #define STS4X_RESET_TIME 1 #define STS4X_CRC_POLY 0x31 #define STS4X_CRC_INIT 0xFF #define STS4X_MAX_TEMP 175 #define STS4X_MIN_TEMP -45 struct sts4x_config { struct i2c_dt_spec bus; uint8_t repeatability; }; struct sts4x_data { uint16_t temp_sample; }; static const uint8_t measure_cmds[3] = {0xE0, 0xF6, 0xFD}; static const uint16_t measure_time_us[3] = {1600, 4500, 8300}; static int sts4x_crc_check(uint16_t value, uint8_t sensor_crc) { uint8_t buf[2]; sys_put_be16(value, buf); uint8_t calculated_crc = crc8(buf, 2, STS4X_CRC_POLY, STS4X_CRC_INIT, false); if (calculated_crc == sensor_crc) { return 0; } return -EIO; } static int sts4x_write_command(const struct device *dev, uint8_t cmd) { const struct sts4x_config *cfg = dev->config; uint8_t tx_buf = cmd; return i2c_write_dt(&cfg->bus, &tx_buf, 1); } static int sts4x_read_sample(const struct device *dev, uint16_t *temp_sample) { const struct sts4x_config *cfg = dev->config; uint8_t rx_buf[3]; int ret; ret = i2c_read_dt(&cfg->bus, rx_buf, sizeof(rx_buf)); if (ret < 0) { LOG_ERR("Failed to read data."); return ret; } *temp_sample = sys_get_be16(rx_buf); ret = sts4x_crc_check(*temp_sample, rx_buf[2]); if (ret < 0) { LOG_ERR("Invalid CRC."); return ret; } return 0; } static int sts4x_sample_fetch(const struct device *dev, enum sensor_channel chan) { struct sts4x_data *data = dev->data; const struct sts4x_config *cfg = dev->config; int ret; if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_AMBIENT_TEMP) { ret = sts4x_write_command(dev, measure_cmds[cfg->repeatability]); if (ret < 0) { LOG_ERR("Failed to write measure command."); return ret; } k_usleep(measure_time_us[cfg->repeatability]); ret = sts4x_read_sample(dev, &data->temp_sample); if (ret < 0) { LOG_ERR("Failed to get temperature data."); return ret; } return 0; } else { return -ENOTSUP; } } static int sts4x_channel_get(const struct device *dev, enum sensor_channel chan, struct sensor_value *val) { const struct sts4x_data *data = dev->data; if (chan == SENSOR_CHAN_AMBIENT_TEMP) { int64_t temp; temp = data->temp_sample * STS4X_MAX_TEMP; val->val1 = (int32_t)(temp / 0xFFFF) + STS4X_MIN_TEMP; val->val2 = ((temp % 0xFFFF) * 1000000) / 0xFFFF; } else { return -ENOTSUP; } return 0; } static int sts4x_init(const struct device *dev) { const struct sts4x_config *cfg = dev->config; int ret; if (!i2c_is_ready_dt(&cfg->bus)) { LOG_ERR("Device not ready."); return -ENODEV; } ret = sts4x_write_command(dev, STS4X_CMD_RESET); if (ret < 0) { LOG_ERR("Failed to reset the device."); return ret; } k_msleep(STS4X_RESET_TIME); return 0; } static DEVICE_API(sensor, sts4x_api_funcs) = { .sample_fetch = sts4x_sample_fetch, .channel_get = sts4x_channel_get, }; #define STS4X_INIT(inst) \ static struct sts4x_data sts4x_data_##inst; \ static const struct sts4x_config sts4x_config_##inst = { \ .bus = I2C_DT_SPEC_INST_GET(inst), \ .repeatability = DT_INST_PROP(inst, repeatability), \ }; \ SENSOR_DEVICE_DT_INST_DEFINE(inst, sts4x_init, NULL, &sts4x_data_##inst, \ &sts4x_config_##inst, POST_KERNEL, \ CONFIG_SENSOR_INIT_PRIORITY, &sts4x_api_funcs); DT_INST_FOREACH_STATUS_OKAY(STS4X_INIT)