[nrf52] add adc (#9321)

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
tomaszduda23 2025-08-01 03:49:39 +02:00 committed by GitHub
parent 549b0d12b6
commit 7a4738ec4e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 412 additions and 18 deletions

View File

@ -267,6 +267,11 @@ def validate_adc_pin(value):
{CONF_ANALOG: True, CONF_INPUT: True}, internal=True {CONF_ANALOG: True, CONF_INPUT: True}, internal=True
)(value) )(value)
if CORE.is_nrf52:
return pins.gpio_pin_schema(
{CONF_ANALOG: True, CONF_INPUT: True}, internal=True
)(value)
raise NotImplementedError raise NotImplementedError
@ -283,5 +288,6 @@ FILTER_SOURCE_FILES = filter_source_files_from_platform(
PlatformFramework.RTL87XX_ARDUINO, PlatformFramework.RTL87XX_ARDUINO,
PlatformFramework.LN882X_ARDUINO, PlatformFramework.LN882X_ARDUINO,
}, },
"adc_sensor_zephyr.cpp": {PlatformFramework.NRF52_ZEPHYR},
} }
) )

View File

@ -13,6 +13,10 @@
#include "hal/adc_types.h" // This defines ADC_CHANNEL_MAX #include "hal/adc_types.h" // This defines ADC_CHANNEL_MAX
#endif // USE_ESP32 #endif // USE_ESP32
#ifdef USE_ZEPHYR
#include <zephyr/drivers/adc.h>
#endif
namespace esphome { namespace esphome {
namespace adc { namespace adc {
@ -38,15 +42,15 @@ enum class SamplingMode : uint8_t {
const LogString *sampling_mode_to_str(SamplingMode mode); const LogString *sampling_mode_to_str(SamplingMode mode);
class Aggregator { template<typename T> class Aggregator {
public: public:
Aggregator(SamplingMode mode); Aggregator(SamplingMode mode);
void add_sample(uint32_t value); void add_sample(T value);
uint32_t aggregate(); T aggregate();
protected: protected:
uint32_t aggr_{0}; T aggr_{0};
uint32_t samples_{0}; uint8_t samples_{0};
SamplingMode mode_{SamplingMode::AVG}; SamplingMode mode_{SamplingMode::AVG};
}; };
@ -69,6 +73,11 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
/// @return A float representing the setup priority. /// @return A float representing the setup priority.
float get_setup_priority() const override; float get_setup_priority() const override;
#ifdef USE_ZEPHYR
/// Set the ADC channel to be used by the ADC sensor.
/// @param channel Pointer to an adc_dt_spec structure representing the ADC channel.
void set_adc_channel(const adc_dt_spec *channel) { this->channel_ = channel; }
#endif
/// Set the GPIO pin to be used by the ADC sensor. /// Set the GPIO pin to be used by the ADC sensor.
/// @param pin Pointer to an InternalGPIOPin representing the ADC input pin. /// @param pin Pointer to an InternalGPIOPin representing the ADC input pin.
void set_pin(InternalGPIOPin *pin) { this->pin_ = pin; } void set_pin(InternalGPIOPin *pin) { this->pin_ = pin; }
@ -151,6 +160,10 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
#ifdef USE_RP2040 #ifdef USE_RP2040
bool is_temperature_{false}; bool is_temperature_{false};
#endif // USE_RP2040 #endif // USE_RP2040
#ifdef USE_ZEPHYR
const struct adc_dt_spec *channel_ = nullptr;
#endif
}; };
} // namespace adc } // namespace adc

View File

@ -18,15 +18,15 @@ const LogString *sampling_mode_to_str(SamplingMode mode) {
return LOG_STR("unknown"); return LOG_STR("unknown");
} }
Aggregator::Aggregator(SamplingMode mode) { template<typename T> Aggregator<T>::Aggregator(SamplingMode mode) {
this->mode_ = mode; this->mode_ = mode;
// set to max uint if mode is "min" // set to max uint if mode is "min"
if (mode == SamplingMode::MIN) { if (mode == SamplingMode::MIN) {
this->aggr_ = UINT32_MAX; this->aggr_ = std::numeric_limits<T>::max();
} }
} }
void Aggregator::add_sample(uint32_t value) { template<typename T> void Aggregator<T>::add_sample(T value) {
this->samples_ += 1; this->samples_ += 1;
switch (this->mode_) { switch (this->mode_) {
@ -47,7 +47,7 @@ void Aggregator::add_sample(uint32_t value) {
} }
} }
uint32_t Aggregator::aggregate() { template<typename T> T Aggregator<T>::aggregate() {
if (this->mode_ == SamplingMode::AVG) { if (this->mode_ == SamplingMode::AVG) {
if (this->samples_ == 0) { if (this->samples_ == 0) {
return this->aggr_; return this->aggr_;
@ -59,6 +59,12 @@ uint32_t Aggregator::aggregate() {
return this->aggr_; return this->aggr_;
} }
#ifdef USE_ZEPHYR
template class Aggregator<int32_t>;
#else
template class Aggregator<uint32_t>;
#endif
void ADCSensor::update() { void ADCSensor::update() {
float value_v = this->sample(); float value_v = this->sample();
ESP_LOGV(TAG, "'%s': Voltage=%.4fV", this->get_name().c_str(), value_v); ESP_LOGV(TAG, "'%s': Voltage=%.4fV", this->get_name().c_str(), value_v);

View File

@ -152,7 +152,7 @@ float ADCSensor::sample() {
} }
float ADCSensor::sample_fixed_attenuation_() { float ADCSensor::sample_fixed_attenuation_() {
auto aggr = Aggregator(this->sampling_mode_); auto aggr = Aggregator<uint32_t>(this->sampling_mode_);
for (uint8_t sample = 0; sample < this->sample_count_; sample++) { for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
int raw; int raw;

View File

@ -37,7 +37,7 @@ void ADCSensor::dump_config() {
} }
float ADCSensor::sample() { float ADCSensor::sample() {
auto aggr = Aggregator(this->sampling_mode_); auto aggr = Aggregator<uint32_t>(this->sampling_mode_);
for (uint8_t sample = 0; sample < this->sample_count_; sample++) { for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
uint32_t raw = 0; uint32_t raw = 0;

View File

@ -30,7 +30,7 @@ void ADCSensor::dump_config() {
float ADCSensor::sample() { float ADCSensor::sample() {
uint32_t raw = 0; uint32_t raw = 0;
auto aggr = Aggregator(this->sampling_mode_); auto aggr = Aggregator<uint32_t>(this->sampling_mode_);
if (this->output_raw_) { if (this->output_raw_) {
for (uint8_t sample = 0; sample < this->sample_count_; sample++) { for (uint8_t sample = 0; sample < this->sample_count_; sample++) {

View File

@ -41,7 +41,7 @@ void ADCSensor::dump_config() {
float ADCSensor::sample() { float ADCSensor::sample() {
uint32_t raw = 0; uint32_t raw = 0;
auto aggr = Aggregator(this->sampling_mode_); auto aggr = Aggregator<uint32_t>(this->sampling_mode_);
if (this->is_temperature_) { if (this->is_temperature_) {
adc_set_temp_sensor_enabled(true); adc_set_temp_sensor_enabled(true);

View File

@ -0,0 +1,207 @@
#include "adc_sensor.h"
#ifdef USE_ZEPHYR
#include "esphome/core/log.h"
#include "hal/nrf_saadc.h"
namespace esphome {
namespace adc {
static const char *const TAG = "adc.zephyr";
void ADCSensor::setup() {
if (!adc_is_ready_dt(this->channel_)) {
ESP_LOGE(TAG, "ADC controller device %s not ready", this->channel_->dev->name);
return;
}
auto err = adc_channel_setup_dt(this->channel_);
if (err < 0) {
ESP_LOGE(TAG, "Could not setup channel %s (%d)", this->channel_->dev->name, err);
return;
}
}
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
static const LogString *gain_to_str(enum adc_gain gain) {
switch (gain) {
case ADC_GAIN_1_6:
return LOG_STR("1/6");
case ADC_GAIN_1_5:
return LOG_STR("1/5");
case ADC_GAIN_1_4:
return LOG_STR("1/4");
case ADC_GAIN_1_3:
return LOG_STR("1/3");
case ADC_GAIN_2_5:
return LOG_STR("2/5");
case ADC_GAIN_1_2:
return LOG_STR("1/2");
case ADC_GAIN_2_3:
return LOG_STR("2/3");
case ADC_GAIN_4_5:
return LOG_STR("4/5");
case ADC_GAIN_1:
return LOG_STR("1");
case ADC_GAIN_2:
return LOG_STR("2");
case ADC_GAIN_3:
return LOG_STR("3");
case ADC_GAIN_4:
return LOG_STR("4");
case ADC_GAIN_6:
return LOG_STR("6");
case ADC_GAIN_8:
return LOG_STR("8");
case ADC_GAIN_12:
return LOG_STR("12");
case ADC_GAIN_16:
return LOG_STR("16");
case ADC_GAIN_24:
return LOG_STR("24");
case ADC_GAIN_32:
return LOG_STR("32");
case ADC_GAIN_64:
return LOG_STR("64");
case ADC_GAIN_128:
return LOG_STR("128");
}
return LOG_STR("undefined gain");
}
static const LogString *reference_to_str(enum adc_reference reference) {
switch (reference) {
case ADC_REF_VDD_1:
return LOG_STR("VDD");
case ADC_REF_VDD_1_2:
return LOG_STR("VDD/2");
case ADC_REF_VDD_1_3:
return LOG_STR("VDD/3");
case ADC_REF_VDD_1_4:
return LOG_STR("VDD/4");
case ADC_REF_INTERNAL:
return LOG_STR("INTERNAL");
case ADC_REF_EXTERNAL0:
return LOG_STR("External, input 0");
case ADC_REF_EXTERNAL1:
return LOG_STR("External, input 1");
}
return LOG_STR("undefined reference");
}
static const LogString *input_to_str(uint8_t input) {
switch (input) {
case NRF_SAADC_INPUT_AIN0:
return LOG_STR("AIN0");
case NRF_SAADC_INPUT_AIN1:
return LOG_STR("AIN1");
case NRF_SAADC_INPUT_AIN2:
return LOG_STR("AIN2");
case NRF_SAADC_INPUT_AIN3:
return LOG_STR("AIN3");
case NRF_SAADC_INPUT_AIN4:
return LOG_STR("AIN4");
case NRF_SAADC_INPUT_AIN5:
return LOG_STR("AIN5");
case NRF_SAADC_INPUT_AIN6:
return LOG_STR("AIN6");
case NRF_SAADC_INPUT_AIN7:
return LOG_STR("AIN7");
case NRF_SAADC_INPUT_VDD:
return LOG_STR("VDD");
case NRF_SAADC_INPUT_VDDHDIV5:
return LOG_STR("VDDHDIV5");
}
return LOG_STR("undefined input");
}
#endif // ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
void ADCSensor::dump_config() {
LOG_SENSOR("", "ADC Sensor", this);
LOG_PIN(" Pin: ", this->pin_);
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
ESP_LOGV(TAG,
" Name: %s\n"
" Channel: %d\n"
" vref_mv: %d\n"
" Resolution %d\n"
" Oversampling %d",
this->channel_->dev->name, this->channel_->channel_id, this->channel_->vref_mv, this->channel_->resolution,
this->channel_->oversampling);
ESP_LOGV(TAG,
" Gain: %s\n"
" reference: %s\n"
" acquisition_time: %d\n"
" differential %s",
LOG_STR_ARG(gain_to_str(this->channel_->channel_cfg.gain)),
LOG_STR_ARG(reference_to_str(this->channel_->channel_cfg.reference)),
this->channel_->channel_cfg.acquisition_time, YESNO(this->channel_->channel_cfg.differential));
if (this->channel_->channel_cfg.differential) {
ESP_LOGV(TAG,
" Positive: %s\n"
" Negative: %s",
LOG_STR_ARG(input_to_str(this->channel_->channel_cfg.input_positive)),
LOG_STR_ARG(input_to_str(this->channel_->channel_cfg.input_negative)));
} else {
ESP_LOGV(TAG, " Positive: %s", LOG_STR_ARG(input_to_str(this->channel_->channel_cfg.input_positive)));
}
#endif
LOG_UPDATE_INTERVAL(this);
}
float ADCSensor::sample() {
auto aggr = Aggregator<int32_t>(this->sampling_mode_);
int err;
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
int16_t buf = 0;
struct adc_sequence sequence = {
.buffer = &buf,
/* buffer size in bytes, not number of samples */
.buffer_size = sizeof(buf),
};
int32_t val_raw;
err = adc_sequence_init_dt(this->channel_, &sequence);
if (err < 0) {
ESP_LOGE(TAG, "Could sequence init %s (%d)", this->channel_->dev->name, err);
return 0.0;
}
err = adc_read(this->channel_->dev, &sequence);
if (err < 0) {
ESP_LOGE(TAG, "Could not read %s (%d)", this->channel_->dev->name, err);
return 0.0;
}
val_raw = (int32_t) buf;
if (!this->channel_->channel_cfg.differential) {
// https://github.com/adafruit/Adafruit_nRF52_Arduino/blob/0ed4d9ffc674ae407be7cacf5696a02f5e789861/cores/nRF5/wiring_analog_nRF52.c#L222
if (val_raw < 0) {
val_raw = 0;
}
}
aggr.add_sample(val_raw);
}
int32_t val_mv = aggr.aggregate();
if (this->output_raw_) {
return val_mv;
}
err = adc_raw_to_millivolts_dt(this->channel_, &val_mv);
/* conversion to mV may not be supported, skip if not */
if (err < 0) {
ESP_LOGE(TAG, "Value in mV not available %s (%d)", this->channel_->dev->name, err);
return 0.0;
}
return val_mv / 1000.0f;
}
} // namespace adc
} // namespace esphome
#endif

View File

@ -3,6 +3,12 @@ import logging
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import sensor, voltage_sampler from esphome.components import sensor, voltage_sampler
from esphome.components.esp32 import get_esp32_variant from esphome.components.esp32 import get_esp32_variant
from esphome.components.nrf52.const import AIN_TO_GPIO, EXTRA_ADC
from esphome.components.zephyr import (
zephyr_add_overlay,
zephyr_add_prj_conf,
zephyr_add_user,
)
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_ATTENUATION, CONF_ATTENUATION,
@ -11,6 +17,7 @@ from esphome.const import (
CONF_PIN, CONF_PIN,
CONF_RAW, CONF_RAW,
DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLTAGE,
PLATFORM_NRF52,
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
UNIT_VOLT, UNIT_VOLT,
) )
@ -60,6 +67,10 @@ ADCSensor = adc_ns.class_(
"ADCSensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler "ADCSensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler
) )
CONF_NRF_SAADC = "nrf_saadc"
adc_dt_spec = cg.global_ns.class_("adc_dt_spec")
CONFIG_SCHEMA = cv.All( CONFIG_SCHEMA = cv.All(
sensor.sensor_schema( sensor.sensor_schema(
ADCSensor, ADCSensor,
@ -75,6 +86,7 @@ CONFIG_SCHEMA = cv.All(
cv.SplitDefault(CONF_ATTENUATION, esp32="0db"): cv.All( cv.SplitDefault(CONF_ATTENUATION, esp32="0db"): cv.All(
cv.only_on_esp32, _attenuation cv.only_on_esp32, _attenuation
), ),
cv.OnlyWith(CONF_NRF_SAADC, PLATFORM_NRF52): cv.declare_id(adc_dt_spec),
cv.Optional(CONF_SAMPLES, default=1): cv.int_range(min=1, max=255), cv.Optional(CONF_SAMPLES, default=1): cv.int_range(min=1, max=255),
cv.Optional(CONF_SAMPLING_MODE, default="avg"): _sampling_mode, cv.Optional(CONF_SAMPLING_MODE, default="avg"): _sampling_mode,
} }
@ -83,6 +95,8 @@ CONFIG_SCHEMA = cv.All(
validate_config, validate_config,
) )
CONF_ADC_CHANNEL_ID = "adc_channel_id"
async def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
@ -93,7 +107,7 @@ async def to_code(config):
cg.add_define("USE_ADC_SENSOR_VCC") cg.add_define("USE_ADC_SENSOR_VCC")
elif config[CONF_PIN] == "TEMPERATURE": elif config[CONF_PIN] == "TEMPERATURE":
cg.add(var.set_is_temperature()) cg.add(var.set_is_temperature())
else: elif not CORE.is_nrf52 or config[CONF_PIN][CONF_NUMBER] not in EXTRA_ADC:
pin = await cg.gpio_pin_expression(config[CONF_PIN]) pin = await cg.gpio_pin_expression(config[CONF_PIN])
cg.add(var.set_pin(pin)) cg.add(var.set_pin(pin))
@ -122,3 +136,41 @@ async def to_code(config):
): ):
chan = ESP32_VARIANT_ADC2_PIN_TO_CHANNEL[variant][pin_num] chan = ESP32_VARIANT_ADC2_PIN_TO_CHANNEL[variant][pin_num]
cg.add(var.set_channel(adc_unit_t.ADC_UNIT_2, chan)) cg.add(var.set_channel(adc_unit_t.ADC_UNIT_2, chan))
elif CORE.is_nrf52:
CORE.data.setdefault(CONF_ADC_CHANNEL_ID, 0)
channel_id = CORE.data[CONF_ADC_CHANNEL_ID]
CORE.data[CONF_ADC_CHANNEL_ID] = channel_id + 1
zephyr_add_prj_conf("ADC", True)
nrf_saadc = config[CONF_NRF_SAADC]
rhs = cg.RawExpression(
f"ADC_DT_SPEC_GET_BY_IDX(DT_PATH(zephyr_user), {channel_id})"
)
adc = cg.new_Pvariable(nrf_saadc, rhs)
cg.add(var.set_adc_channel(adc))
gain = "ADC_GAIN_1_6"
pin_number = config[CONF_PIN][CONF_NUMBER]
if pin_number == "VDDHDIV5":
gain = "ADC_GAIN_1_2"
if isinstance(pin_number, int):
GPIO_TO_AIN = {v: k for k, v in AIN_TO_GPIO.items()}
pin_number = GPIO_TO_AIN[pin_number]
zephyr_add_user("io-channels", f"<&adc {channel_id}>")
zephyr_add_overlay(
f"""
&adc {{
#address-cells = <1>;
#size-cells = <0>;
channel@{channel_id} {{
reg = <{channel_id}>;
zephyr,gain = "{gain}";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,input-positive = <NRF_SAADC_{pin_number}>;
zephyr,resolution = <14>;
zephyr,oversampling = <8>;
}};
}};
"""
)

View File

@ -2,3 +2,17 @@ BOOTLOADER_ADAFRUIT = "adafruit"
BOOTLOADER_ADAFRUIT_NRF52_SD132 = "adafruit_nrf52_sd132" BOOTLOADER_ADAFRUIT_NRF52_SD132 = "adafruit_nrf52_sd132"
BOOTLOADER_ADAFRUIT_NRF52_SD140_V6 = "adafruit_nrf52_sd140_v6" BOOTLOADER_ADAFRUIT_NRF52_SD140_V6 = "adafruit_nrf52_sd140_v6"
BOOTLOADER_ADAFRUIT_NRF52_SD140_V7 = "adafruit_nrf52_sd140_v7" BOOTLOADER_ADAFRUIT_NRF52_SD140_V7 = "adafruit_nrf52_sd140_v7"
EXTRA_ADC = [
"VDD",
"VDDHDIV5",
]
AIN_TO_GPIO = {
"AIN0": 2,
"AIN1": 3,
"AIN2": 4,
"AIN3": 5,
"AIN4": 28,
"AIN5": 29,
"AIN6": 30,
"AIN7": 31,
}

View File

@ -2,12 +2,23 @@ from esphome import pins
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components.zephyr.const import zephyr_ns from esphome.components.zephyr.const import zephyr_ns
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_INVERTED, CONF_MODE, CONF_NUMBER, PLATFORM_NRF52 from esphome.const import (
CONF_ANALOG,
CONF_ID,
CONF_INVERTED,
CONF_MODE,
CONF_NUMBER,
PLATFORM_NRF52,
)
from .const import AIN_TO_GPIO, EXTRA_ADC
ZephyrGPIOPin = zephyr_ns.class_("ZephyrGPIOPin", cg.InternalGPIOPin) ZephyrGPIOPin = zephyr_ns.class_("ZephyrGPIOPin", cg.InternalGPIOPin)
def _translate_pin(value): def _translate_pin(value):
if value in AIN_TO_GPIO:
return AIN_TO_GPIO[value]
if isinstance(value, dict) or value is None: if isinstance(value, dict) or value is None:
raise cv.Invalid( raise cv.Invalid(
"This variable only supports pin numbers, not full pin schemas " "This variable only supports pin numbers, not full pin schemas "
@ -28,18 +39,33 @@ def _translate_pin(value):
def validate_gpio_pin(value): def validate_gpio_pin(value):
if value in EXTRA_ADC:
return value
value = _translate_pin(value) value = _translate_pin(value)
if value < 0 or value > (32 + 16): if value < 0 or value > (32 + 16):
raise cv.Invalid(f"NRF52: Invalid pin number: {value}") raise cv.Invalid(f"NRF52: Invalid pin number: {value}")
return value return value
def validate_supports(value):
num = value[CONF_NUMBER]
mode = value[CONF_MODE]
is_analog = mode[CONF_ANALOG]
if is_analog:
if num in EXTRA_ADC:
return value
if num not in AIN_TO_GPIO.values():
raise cv.Invalid(f"Cannot use {num} as analog pin")
return value
NRF52_PIN_SCHEMA = cv.All( NRF52_PIN_SCHEMA = cv.All(
pins.gpio_base_schema( pins.gpio_base_schema(
ZephyrGPIOPin, ZephyrGPIOPin,
validate_gpio_pin, validate_gpio_pin,
modes=pins.GPIO_STANDARD_MODES, modes=pins.GPIO_STANDARD_MODES + (CONF_ANALOG,),
), ),
validate_supports,
) )

View File

@ -1,5 +1,5 @@
import os import os
from typing import Final, TypedDict from typing import TypedDict
import esphome.codegen as cg import esphome.codegen as cg
from esphome.const import CONF_BOARD from esphome.const import CONF_BOARD
@ -8,18 +8,19 @@ from esphome.helpers import copy_file_if_changed, write_file_if_changed
from .const import ( from .const import (
BOOTLOADER_MCUBOOT, BOOTLOADER_MCUBOOT,
KEY_BOARD,
KEY_BOOTLOADER, KEY_BOOTLOADER,
KEY_EXTRA_BUILD_FILES, KEY_EXTRA_BUILD_FILES,
KEY_OVERLAY, KEY_OVERLAY,
KEY_PM_STATIC, KEY_PM_STATIC,
KEY_PRJ_CONF, KEY_PRJ_CONF,
KEY_USER,
KEY_ZEPHYR, KEY_ZEPHYR,
zephyr_ns, zephyr_ns,
) )
CODEOWNERS = ["@tomaszduda23"] CODEOWNERS = ["@tomaszduda23"]
AUTO_LOAD = ["preferences"] AUTO_LOAD = ["preferences"]
KEY_BOARD: Final = "board"
PrjConfValueType = bool | str | int PrjConfValueType = bool | str | int
@ -49,6 +50,7 @@ class ZephyrData(TypedDict):
overlay: str overlay: str
extra_build_files: dict[str, str] extra_build_files: dict[str, str]
pm_static: list[Section] pm_static: list[Section]
user: dict[str, list[str]]
def zephyr_set_core_data(config): def zephyr_set_core_data(config):
@ -59,6 +61,7 @@ def zephyr_set_core_data(config):
overlay="", overlay="",
extra_build_files={}, extra_build_files={},
pm_static=[], pm_static=[],
user={},
) )
return config return config
@ -178,7 +181,25 @@ def zephyr_add_pm_static(section: Section):
CORE.data[KEY_ZEPHYR][KEY_PM_STATIC].extend(section) CORE.data[KEY_ZEPHYR][KEY_PM_STATIC].extend(section)
def zephyr_add_user(key, value):
user = zephyr_data()[KEY_USER]
if key not in user:
user[key] = []
user[key] += [value]
def copy_files(): def copy_files():
user = zephyr_data()[KEY_USER]
if user:
zephyr_add_overlay(
f"""
/ {{
zephyr,user {{
{[f"{key} = {', '.join(value)};" for key, value in user.items()][0]}
}};
}};"""
)
want_opts = zephyr_data()[KEY_PRJ_CONF] want_opts = zephyr_data()[KEY_PRJ_CONF]
prj_conf = ( prj_conf = (

View File

@ -10,5 +10,7 @@ KEY_OVERLAY: Final = "overlay"
KEY_PM_STATIC: Final = "pm_static" KEY_PM_STATIC: Final = "pm_static"
KEY_PRJ_CONF: Final = "prj_conf" KEY_PRJ_CONF: Final = "prj_conf"
KEY_ZEPHYR = "zephyr" KEY_ZEPHYR = "zephyr"
KEY_BOARD: Final = "board"
KEY_USER: Final = "user"
zephyr_ns = cg.esphome_ns.namespace("zephyr") zephyr_ns = cg.esphome_ns.namespace("zephyr")

View File

@ -25,6 +25,7 @@ int main() { return 0;}
Path(zephyr_dir / "prj.conf").write_text( Path(zephyr_dir / "prj.conf").write_text(
""" """
CONFIG_NEWLIB_LIBC=y CONFIG_NEWLIB_LIBC=y
CONFIG_ADC=y
""", """,
encoding="utf-8", encoding="utf-8",
) )

View File

@ -0,0 +1,23 @@
sensor:
- platform: adc
pin: VDDHDIV5
name: "VDDH Voltage"
update_interval: 5sec
filters:
- multiply: 5
- platform: adc
pin: VDD
name: "VDD Voltage"
update_interval: 5sec
- platform: adc
pin: AIN0
name: "AIN0 Voltage"
update_interval: 5sec
- platform: adc
pin: P0.03
name: "AIN1 Voltage"
update_interval: 5sec
- platform: adc
name: "AIN2 Voltage"
update_interval: 5sec
pin: 4

View File

@ -0,0 +1,23 @@
sensor:
- platform: adc
pin: VDDHDIV5
name: "VDDH Voltage"
update_interval: 5sec
filters:
- multiply: 5
- platform: adc
pin: VDD
name: "VDD Voltage"
update_interval: 5sec
- platform: adc
pin: AIN0
name: "AIN0 Voltage"
update_interval: 5sec
- platform: adc
pin: P0.03
name: "AIN1 Voltage"
update_interval: 5sec
- platform: adc
name: "AIN2 Voltage"
update_interval: 5sec
pin: 4