Skip to content

Commit

Permalink
drivers: sensor: Aosong AGS10 TVOC sensor
Browse files Browse the repository at this point in the history
Added support for Aosong AGS10 TVOC sensor

Signed-off-by: Balthazar Deliers <bdeliers@bdeliers.com>
  • Loading branch information
BDeliers committed Jan 16, 2024
1 parent a897005 commit 005f2b5
Show file tree
Hide file tree
Showing 8 changed files with 214 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/sensor/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ add_subdirectory_ifdef(CONFIG_ADXL345 adxl345)
add_subdirectory_ifdef(CONFIG_ADXL362 adxl362)
add_subdirectory_ifdef(CONFIG_ADXL367 adxl367)
add_subdirectory_ifdef(CONFIG_ADXL372 adxl372)
add_subdirectory_ifdef(CONFIG_AGS10 ags10)
add_subdirectory_ifdef(CONFIG_AK8975 ak8975)
add_subdirectory_ifdef(CONFIG_AKM09918C akm09918c)
add_subdirectory_ifdef(CONFIG_AMD_SB_TSI amd_sb_tsi)
Expand Down
1 change: 1 addition & 0 deletions drivers/sensor/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ source "drivers/sensor/adxl345/Kconfig"
source "drivers/sensor/adxl362/Kconfig"
source "drivers/sensor/adxl367/Kconfig"
source "drivers/sensor/adxl372/Kconfig"
source "drivers/sensor/ags10/Kconfig"
source "drivers/sensor/ak8975/Kconfig"
source "drivers/sensor/akm09918c/Kconfig"
source "drivers/sensor/amd_sb_tsi/Kconfig"
Expand Down
5 changes: 5 additions & 0 deletions drivers/sensor/ags10/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# SPDX-License-Identifier: Apache-2.0


zephyr_library()
zephyr_library_sources(ags10.c)
12 changes: 12 additions & 0 deletions drivers/sensor/ags10/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright (c) 2023 Balthazar Deliers
# SPDX-License-Identifier: Apache-2.0

# AOSONG AGS10 TVOC sensor driver options.

config AGS10
bool "AOSONG AGS10 TVOC sensor"
default y
depends on DT_HAS_AOSONG_AGS10_ENABLED
select I2C
help
Enable AOSONG AGS10 TVOC sensor driver.
134 changes: 134 additions & 0 deletions drivers/sensor/ags10/ags10.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
* Copyright (c) 2023 Balthazar Deliers
*
* SPDX-License-Identifier: Apache-2.0
*/

#define DT_DRV_COMPAT aosong_ags10

#include <zephyr/drivers/sensor.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/crc.h>

#include "ags10.h"

LOG_MODULE_REGISTER(AGS10, CONFIG_SENSOR_LOG_LEVEL);

#define AGS10_MAX_PAYLOAD_SIZE 5U /* Payload will be max 4 bytes + CRC (datasheet 3.1) */

static int ags10_read(const struct device *dev, uint8_t cmd, uint8_t *data, uint8_t rx_bytes)
{
if (rx_bytes > AGS10_MAX_PAYLOAD_SIZE) {
return -EINVAL;
}

const struct ags10_config *conf = dev->config;

uint8_t recv_buf[AGS10_MAX_PAYLOAD_SIZE] = {0};
int ret = i2c_write_read_dt(&conf->bus, &cmd, sizeof(cmd), &recv_buf, rx_bytes);

if (ret < 0) {
return ret;
}

memcpy(data, recv_buf, rx_bytes);

return 0;
}

static int ags10_sample_fetch(const struct device *dev, enum sensor_channel chan)
{
if (chan != SENSOR_CHAN_VOC && chan != SENSOR_CHAN_ALL) {
return -ENOTSUP;
}

struct ags10_data *data = dev->data;
int ret = -ENOTSUP;
uint8_t recv_buf[5] = {0};

ret = ags10_read(dev, AGS10_CMD_DATA_ACQUISITION, recv_buf, 5);

if (ret == 0) {
/* If CRC is valid and data is valid too */
if (crc8(&recv_buf[0], 4, 0x31, 0xFF, false) == recv_buf[4] &&
((recv_buf[0] & AGS10_MSK_STATUS) == AGS10_REG_STATUS_NRDY_READY)) {
data->status = recv_buf[0] & AGS10_MSK_STATUS;
data->tvoc_ppb = sys_get_be24(&recv_buf[1]);
return 0;
}

LOG_WRN("Bad CRC or data not ready");
ret = -EIO;
}

return ret;
}

static int ags10_channel_get(const struct device *dev, enum sensor_channel chan,
struct sensor_value *val)
{
struct ags10_data *data = dev->data;

if (chan == SENSOR_CHAN_VOC) {
val->val1 = data->tvoc_ppb;
} else {
return -ENOTSUP;
}

val->val2 = 0;

return 0;
}

static int ags10_init(const struct device *dev)
{
const struct ags10_config *conf = dev->config;
struct ags10_data *data = dev->data;
int ret;

if (!i2c_is_ready_dt(&conf->bus)) {
LOG_ERR("Device not ready");
return -ENODEV;
}

/* Set initial data values */
data->tvoc_ppb = 0;
data->status = 0xFF;
data->version = 0;

/* Read firmware version and check CRC */
uint8_t recv_buf[5] = {0};

ret = ags10_read(dev, AGS10_CMD_READ_VERSION, recv_buf, 5);

/* Bytes 0 to 2 are reserved, byte 3 is version, byte 4 is CRC */
if (ret == 0 && crc8(&recv_buf[0], 4, 0x31, 0xFF, false) == recv_buf[4]) {
data->version = recv_buf[3];
LOG_DBG("Sensor detected");
} else if (ret != 0) {
LOG_ERR("No reply from sensor");
ret = -ENODEV;
} else {
LOG_WRN("Bad CRC");
ret = -EIO;
}

return ret;
}

static const struct sensor_driver_api ags10_api = {.sample_fetch = ags10_sample_fetch,
.channel_get = ags10_channel_get};

#define AGS10_INIT(n) \
static struct ags10_data ags10_data_##n; \
\
static const struct ags10_config ags10_config_##n = { \
.bus = I2C_DT_SPEC_INST_GET(n), \
}; \
\
SENSOR_DEVICE_DT_INST_DEFINE(n, ags10_init, NULL, &ags10_data_##n, &ags10_config_##n, \
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &ags10_api);

DT_INST_FOREACH_STATUS_OKAY(AGS10_INIT)
46 changes: 46 additions & 0 deletions drivers/sensor/ags10/ags10.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2023 Balthazar Deliers
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef ZEPHYR_INCLUDE_DRIVERS_SENSOR_AGS10_H_
#define ZEPHYR_INCLUDE_DRIVERS_SENSOR_AGS10_H_

#ifdef __cplusplus
extern "C" {
#endif

#include <zephyr/device.h>
#include <zephyr/drivers/i2c.h>

#define AGS10_CMD_DATA_ACQUISITION 0x00
#define AGS10_CMD_ZERO_POINT_CALIBRATION 0x01
#define AGS10_CMD_READ_VERSION 0x11
#define AGS10_CMD_READ_RESISTANCE 0x20
#define AGS10_CMD_MODIFY_SLAVE_ADDRESS 0x21

#define AGS10_REG_ZERO_POINT_CALIBRATION_RESET 0xFFFF /* Reset to the factory value */
#define AGS10_REG_ZERO_POINT_CALIBRATION_SET 0x0000 /* Set sensor resistance to zero-point */
#define AGS10_REG_STATUS_NRDY_READY 0x00 /* Device is ready */
#define AGS10_REG_STATUS_CH_PPB 0x00 /* Unit is PPB */

#define AGS10_MSK_STATUS 0x0F
#define AGS10_MSK_STATUS_NRDY 0x01
#define AGS10_MSK_STATUS_CH 0x0E

struct ags10_config {
struct i2c_dt_spec bus;
};

struct ags10_data {
uint32_t tvoc_ppb;
uint8_t status;
uint32_t version;
};

#ifdef __cplusplus
}
#endif

#endif /* ZEPHYR_INCLUDE_DRIVERS_SENSOR_AGS10_H_ */
10 changes: 10 additions & 0 deletions dts/bindings/sensor/aosong,ags10.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright (c) 2023 Balthazar Deliers
# SPDX-License-Identifier: Apache-2.0

description: |
AOSONG AGS10 a high-performance TVOC Sensor With I2C Interface.
See: http://www.aosong.com/en/products-86.html
compatible: "aosong,ags10"

include: [sensor-device.yaml, i2c-device.yaml]
5 changes: 5 additions & 0 deletions tests/drivers/build_all/sensor/i2c.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -884,3 +884,8 @@ test_i2c_bma4xx: bma4xx@7d {
compatible = "bosch,bma4xx";
reg = <0x7d>;
};

test_i2c_ags10: ags10@7e {
compatible = "aosong,ags10";
reg = <0x7e>;
};

0 comments on commit 005f2b5

Please sign in to comment.