mirror of
https://github.com/esphome/esphome.git
synced 2025-08-02 16:37:46 +00:00
[nrf52] add adc (#9321)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
parent
549b0d12b6
commit
7a4738ec4e
@ -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},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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++) {
|
||||||
|
@ -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);
|
||||||
|
207
esphome/components/adc/adc_sensor_zephyr.cpp
Normal file
207
esphome/components/adc/adc_sensor_zephyr.cpp
Normal 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
|
@ -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>;
|
||||||
|
}};
|
||||||
|
}};
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
@ -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,
|
||||||
|
}
|
||||||
|
@ -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,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -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 = (
|
||||||
|
@ -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")
|
||||||
|
@ -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",
|
||||||
)
|
)
|
||||||
|
23
tests/components/adc/test.nrf52-adafruit.yaml
Normal file
23
tests/components/adc/test.nrf52-adafruit.yaml
Normal 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
|
23
tests/components/adc/test.nrf52-mcumgr.yaml
Normal file
23
tests/components/adc/test.nrf52-mcumgr.yaml
Normal 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
|
Loading…
x
Reference in New Issue
Block a user