Compare commits
2 commits
6ea2ead13e
...
980ea12c05
Author | SHA1 | Date | |
---|---|---|---|
Artemis Tosini | 980ea12c05 | ||
Artemis Tosini | 4ef8101a7d |
92
.clang-format
Normal file
92
.clang-format
Normal file
|
@ -0,0 +1,92 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Taken verbatim from Zephyr source code in :/.clang-format
|
||||
# Note: The list of ForEachMacros can be obtained using:
|
||||
#
|
||||
# git grep -h '^#define [^[:space:]]*FOR_EACH[^[:space:]]*(' include/ \
|
||||
# | sed "s,^#define \([^[:space:]]*FOR_EACH[^[:space:]]*\)(.*$, - '\1'," \
|
||||
# | sort | uniq
|
||||
#
|
||||
# References:
|
||||
# - https://clang.llvm.org/docs/ClangFormatStyleOptions.html
|
||||
|
||||
---
|
||||
BasedOnStyle: LLVM
|
||||
AlignConsecutiveMacros: AcrossComments
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortEnumsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AttributeMacros:
|
||||
- __aligned
|
||||
- __deprecated
|
||||
- __packed
|
||||
- __printf_like
|
||||
- __syscall
|
||||
- __syscall_always_inline
|
||||
- __subsystem
|
||||
BitFieldColonSpacing: After
|
||||
BreakBeforeBraces: Linux
|
||||
ColumnLimit: 100
|
||||
ConstructorInitializerIndentWidth: 8
|
||||
ContinuationIndentWidth: 8
|
||||
ForEachMacros:
|
||||
- 'FOR_EACH'
|
||||
- 'FOR_EACH_FIXED_ARG'
|
||||
- 'FOR_EACH_IDX'
|
||||
- 'FOR_EACH_IDX_FIXED_ARG'
|
||||
- 'FOR_EACH_NONEMPTY_TERM'
|
||||
- 'RB_FOR_EACH'
|
||||
- 'RB_FOR_EACH_CONTAINER'
|
||||
- 'SYS_DLIST_FOR_EACH_CONTAINER'
|
||||
- 'SYS_DLIST_FOR_EACH_CONTAINER_SAFE'
|
||||
- 'SYS_DLIST_FOR_EACH_NODE'
|
||||
- 'SYS_DLIST_FOR_EACH_NODE_SAFE'
|
||||
- 'SYS_SFLIST_FOR_EACH_CONTAINER'
|
||||
- 'SYS_SFLIST_FOR_EACH_CONTAINER_SAFE'
|
||||
- 'SYS_SFLIST_FOR_EACH_NODE'
|
||||
- 'SYS_SFLIST_FOR_EACH_NODE_SAFE'
|
||||
- 'SYS_SLIST_FOR_EACH_CONTAINER'
|
||||
- 'SYS_SLIST_FOR_EACH_CONTAINER_SAFE'
|
||||
- 'SYS_SLIST_FOR_EACH_NODE'
|
||||
- 'SYS_SLIST_FOR_EACH_NODE_SAFE'
|
||||
- '_WAIT_Q_FOR_EACH'
|
||||
- 'Z_FOR_EACH'
|
||||
- 'Z_FOR_EACH_ENGINE'
|
||||
- 'Z_FOR_EACH_EXEC'
|
||||
- 'Z_FOR_EACH_FIXED_ARG'
|
||||
- 'Z_FOR_EACH_FIXED_ARG_EXEC'
|
||||
- 'Z_FOR_EACH_IDX'
|
||||
- 'Z_FOR_EACH_IDX_EXEC'
|
||||
- 'Z_FOR_EACH_IDX_FIXED_ARG'
|
||||
- 'Z_FOR_EACH_IDX_FIXED_ARG_EXEC'
|
||||
- 'Z_GENLIST_FOR_EACH_CONTAINER'
|
||||
- 'Z_GENLIST_FOR_EACH_CONTAINER_SAFE'
|
||||
- 'Z_GENLIST_FOR_EACH_NODE'
|
||||
- 'Z_GENLIST_FOR_EACH_NODE_SAFE'
|
||||
- 'STRUCT_SECTION_FOREACH'
|
||||
- 'TYPE_SECTION_FOREACH'
|
||||
IfMacros:
|
||||
- 'CHECKIF'
|
||||
# Disabled for now, see bug https://github.com/zephyrproject-rtos/zephyr/issues/48520
|
||||
#IncludeBlocks: Regroup
|
||||
IncludeCategories:
|
||||
- Regex: '^".*\.h"$'
|
||||
Priority: 0
|
||||
- Regex: '^<(assert|complex|ctype|errno|fenv|float|inttypes|limits|locale|math|setjmp|signal|stdarg|stdbool|stddef|stdint|stdio|stdlib|string|tgmath|time|wchar|wctype)\.h>$'
|
||||
Priority: 1
|
||||
- Regex: '^\<zephyr/.*\.h\>$'
|
||||
Priority: 2
|
||||
- Regex: '.*'
|
||||
Priority: 3
|
||||
IndentCaseLabels: false
|
||||
IndentWidth: 8
|
||||
InsertBraces: true
|
||||
SpaceBeforeParens: ControlStatementsExceptControlMacros
|
||||
SortIncludes: Never
|
||||
UseTab: ForContinuationAndIndentation
|
||||
WhitespaceSensitiveMacros:
|
||||
- STRINGIFY
|
||||
- Z_STRINGIFY
|
27
.editorconfig
Normal file
27
.editorconfig
Normal file
|
@ -0,0 +1,27 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.{c,h}]
|
||||
indent_style = tab
|
||||
indent_size = 8
|
||||
|
||||
[{CMakeLists.txt,*.cmake}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.{dts,dtsi}]
|
||||
indent_style = tab
|
||||
indent_size = 8
|
||||
|
||||
[*.{yml,yaml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[Kconfig*]
|
||||
indent_style = tab
|
||||
indent_size = 8
|
|
@ -17,7 +17,7 @@
|
|||
i2c0_default: i2c0_default {
|
||||
group1 {
|
||||
pinmux = <I2C0_SDA_GPIO32>,
|
||||
<I2C0_SDA_GPIO33>;
|
||||
<I2C0_SCL_GPIO33>;
|
||||
bias-pull-up;
|
||||
drive-open-drain;
|
||||
output-high;
|
||||
|
|
|
@ -0,0 +1,176 @@
|
|||
#define DT_DRV_COMPAT sensirion_scd30
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#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 "scd30.h"
|
||||
|
||||
LOG_MODULE_REGISTER(SCD30, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
/*
|
||||
* Checksum used when recieving data. Used on several
|
||||
* Sensirion devices, including SCD30
|
||||
*/
|
||||
static uint8_t scd30_compute_checksum(uint16_t value)
|
||||
{
|
||||
uint8_t buf[2];
|
||||
sys_put_be16(value, buf);
|
||||
return crc8(buf, 2, 0x31, 0xff, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the Sensirion checksum recieved is valid
|
||||
* Takes a pointer to an array and uses 3 bytes
|
||||
*/
|
||||
static bool scd30_verify_checksum(uint8_t *buf)
|
||||
{
|
||||
return crc8(buf, 2, 0x31, 0xff, false) == buf[2];
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract a float from a measurement response buffer
|
||||
* Takes a pointer to an array and uses 6 bytes
|
||||
*/
|
||||
static float scd30_extract_float(uint8_t *buf)
|
||||
{
|
||||
uint32_t upper = sys_get_be16(buf);
|
||||
uint32_t lower = sys_get_be16(&buf[3]);
|
||||
uint32_t full = upper << 16 | lower;
|
||||
return *((float *)&full);
|
||||
}
|
||||
|
||||
static int scd30_write(const struct device *dev, uint16_t cmd, uint16_t data, bool has_data)
|
||||
{
|
||||
const struct scd30_config *config = dev->config;
|
||||
uint8_t tx_buf[5];
|
||||
|
||||
sys_put_be16(cmd, tx_buf);
|
||||
if (has_data) {
|
||||
sys_put_be16(data, &tx_buf[2]);
|
||||
tx_buf[4] = scd30_compute_checksum(data);
|
||||
}
|
||||
|
||||
return i2c_write_dt(&config->bus, tx_buf, has_data ? 5 : 2);
|
||||
}
|
||||
|
||||
static int scd30_read(const struct device *dev, uint16_t cmd, uint8_t *buf, size_t buf_size)
|
||||
{
|
||||
const struct scd30_config *config = dev->config;
|
||||
|
||||
if (scd30_write(dev, cmd, 0, false) < 0) {
|
||||
LOG_ERR("Unable to write command while reading!");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
k_sleep(SCD30_READ_DELAY);
|
||||
|
||||
if (i2c_read_dt(&config->bus, buf, buf_size) < 0) {
|
||||
LOG_ERR("Unable to read data!");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i * 3 < buf_size - 2; i++) {
|
||||
if (!scd30_verify_checksum(&buf[i * 3])) {
|
||||
LOG_ERR("Recieved invalid CRC!");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scd30_sample_fetch(const struct device *dev, enum sensor_channel chan)
|
||||
{
|
||||
struct scd30_data *data = dev->data;
|
||||
|
||||
__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL);
|
||||
uint8_t rx_buf[18];
|
||||
|
||||
// Make sure there's a sample ready for us and clear the ready flag
|
||||
if (scd30_read(dev, SCD30_CMD_CHECK_DATA_READY, rx_buf, 3) < 0) {
|
||||
LOG_ERR("Unable to check if measurement is ready!");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
// Now actually read
|
||||
if (scd30_read(dev, SCD30_CMD_READ_MEASUREMENT, rx_buf, 18) < 0) {
|
||||
LOG_ERR("Unable to read measurement!");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
data->co2_sample = scd30_extract_float(rx_buf);
|
||||
data->t_sample = scd30_extract_float(&rx_buf[6]);
|
||||
data->rh_sample = scd30_extract_float(&rx_buf[12]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scd30_channel_get(const struct device *dev, enum sensor_channel chan,
|
||||
struct sensor_value *val)
|
||||
{
|
||||
struct scd30_data *data = dev->data;
|
||||
switch (chan) {
|
||||
case SENSOR_CHAN_CO2:
|
||||
return sensor_value_from_float(val, data->co2_sample);
|
||||
case SENSOR_CHAN_AMBIENT_TEMP:
|
||||
return sensor_value_from_float(val, data->t_sample);
|
||||
case SENSOR_CHAN_HUMIDITY:
|
||||
return sensor_value_from_float(val, data->rh_sample);
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct sensor_driver_api scd30_driver_api = {
|
||||
.sample_fetch = scd30_sample_fetch,
|
||||
.channel_get = scd30_channel_get,
|
||||
};
|
||||
|
||||
static int scd30_init(const struct device *dev)
|
||||
{
|
||||
const struct scd30_config *cfg = dev->config;
|
||||
|
||||
if (!device_is_ready(cfg->bus.bus)) {
|
||||
LOG_ERR("I2C bus %s is not ready!", cfg->bus.bus->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (cfg->sampling_interval != 0) {
|
||||
if (scd30_write(dev, SCD30_CMD_INTERVAL, cfg->sampling_interval, true) < 0) {
|
||||
LOG_ERR("Failed to set SCD30 sampling interval!");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
if (cfg->asc_enabled < 2) {
|
||||
if (scd30_write(dev, SCD30_CMD_ASC, cfg->asc_enabled, true) < 0) {
|
||||
LOG_ERR("Failed to set SCD30 ASC!");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
if (scd30_write(dev, SCD30_CMD_START, 0, 1) < 0) {
|
||||
LOG_ERR("Could not start SCD30!");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
#define SCD30_DEFINE(inst) \
|
||||
struct scd30_data scd30_data_##inst; \
|
||||
static const struct scd30_config scd30_cfg_##inst = { \
|
||||
.bus = I2C_DT_SPEC_INST_GET(inst), \
|
||||
.asc_enabled = DT_INST_PROP_OR(inst, asc_enabled, 2), \
|
||||
.sampling_interval = DT_INST_PROP_OR(inst, sampling_interval, 0), \
|
||||
.rdy_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, {0})}; \
|
||||
SENSOR_DEVICE_DT_INST_DEFINE(inst, scd30_init, NULL, &scd30_data_##inst, \
|
||||
&scd30_cfg_##inst, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \
|
||||
&scd30_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(SCD30_DEFINE)
|
30
drivers/sensor/scd30/scd30.h
Normal file
30
drivers/sensor/scd30/scd30.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
|
||||
#define SCD30_READ_DELAY K_MSEC(3)
|
||||
|
||||
#define SCD30_CMD_START 0x0010
|
||||
#define SCD30_CMD_STOP 0x0104
|
||||
#define SCD30_CMD_INTERVAL 0x4600
|
||||
#define SCD30_CMD_CHECK_DATA_READY 0x0202
|
||||
#define SCD30_CMD_READ_MEASUREMENT 0x0300
|
||||
#define SCD30_CMD_ASC 0x5306
|
||||
#define SCD30_CMD_SET_TEMP_OFFSET 0x5403
|
||||
#define SCD30_CMD_SET_ALTITUDE 0x5102
|
||||
#define SCD30_CMD_READ_VERSION 0xd100
|
||||
#define SCD30_CMD_SOFT_RESET 0xd304
|
||||
|
||||
struct scd30_config {
|
||||
struct i2c_dt_spec bus;
|
||||
struct gpio_dt_spec rdy_gpio;
|
||||
uint8_t asc_enabled;
|
||||
uint16_t sampling_interval;
|
||||
};
|
||||
|
||||
struct scd30_data {
|
||||
float t_sample;
|
||||
float rh_sample;
|
||||
float co2_sample;
|
||||
};
|
|
@ -1,3 +1,18 @@
|
|||
description: Sensirion SCD30 CO2, humidity, and temperature sensor
|
||||
compatible: "sensirion,scd30"
|
||||
include: [sensor-device.yaml, i2c-device.yaml]
|
||||
properties:
|
||||
int-gpios:
|
||||
type: phandle-array
|
||||
description: |
|
||||
GPIO pin connected to the SCD30 RDY pin. Without this async reads are unavailable
|
||||
asc-enabled:
|
||||
type: boolean
|
||||
description: |
|
||||
Enable automatic self calibration. If this property is not set then the value in the SCD30's
|
||||
nonvolatile storage will be used.
|
||||
sampling-interval:
|
||||
type: int
|
||||
description: |
|
||||
How often to sample at bootup. If this property is not set then the value in the SCD30's
|
||||
nonvolatile storage will be used.
|
||||
|
|
|
@ -22,7 +22,10 @@
|
|||
mbedtls
|
||||
hal_espressif
|
||||
];
|
||||
extraPackages = with pkgs; [ esptool ];
|
||||
extraPackages = with pkgs; [
|
||||
esptool
|
||||
(clang-tools.override { llvmPackages = llvmPackages_latest; })
|
||||
];
|
||||
};
|
||||
devShells.default = devShells.zephyr;
|
||||
formatter = pkgs.nixfmt;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
int main(void)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue