Compare commits

...
Author SHA1 Message Date
J. Nick Koston 94152b718f Merge remote-tracking branch 'origin/dev' into HEAD 2026-11-14 08:37:01 -06:00
J. Nick Koston 1942887031 Merge branch 'dev' into ads1115-simplify 2026-11-12 15:47:32 -06:00
J. Nick Koston 5a43a6cbcf Merge remote-tracking branch 'upstream/dev' into ads1115-simplify
# Conflicts:
#	esphome/components/ads1115/ads1115.cpp
2026-11-09 08:55:06 -08:00
J. Nick Koston 10ce4c6862 [ads1115] Clear the warning only after a successful conversion read 2026-11-06 22:51:02 -06:00
J. Nick Koston 7356dd3eaf [ads1xxx] Build configs from named fields, emit the TS bit from C++ 2026-11-06 22:39:56 -06:00
J. Nick Koston 00a3d45614 [ads1xxx] Share ADS1115 and ADS1118 register code
Python now works out each sensor's config register bits and conversion delay at build time, so the hubs no longer rebuild them with enum switches on every read.
2026-11-06 22:36:24 -06:00
21 changed files with 141 additions and 457 deletions

No files matched your search

+1
View File
@@ -26,6 +26,7 @@ esphome/components/ade7953_base/* @angelnu
esphome/components/ade7953_i2c/* @angelnu
esphome/components/ade7953_spi/* @angelnu
esphome/components/ads1118/* @solomondg1
esphome/components/ads1xxx/* @bdraco
esphome/components/ags10/* @mak-42
esphome/components/aic3204/* @kbx81
esphome/components/airthings_ble/* @jeromelaban
+2 -3
View File
@@ -3,6 +3,7 @@ from esphome.components import i2c
import esphome.config_validation as cv
from esphome.types import ConfigType
AUTO_LOAD = ["ads1xxx"]
DEPENDENCIES = ["i2c"]
DOMAIN = "ads1115"
MULTI_CONF = True
@@ -26,6 +27,4 @@ CONFIG_SCHEMA = (
async def to_code(config: ConfigType) -> None:
var = await i2c.new_i2c_component(config)
cg.add(var.set_continuous_mode(config[CONF_CONTINUOUS_MODE]))
await i2c.new_i2c_component(config, config[CONF_CONTINUOUS_MODE])
+19 -163
View File
@@ -1,61 +1,19 @@
#include "ads1115.h"
#include "esphome/components/ads1xxx/ads1xxx.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
namespace esphome::ads1115 {
ESPHOME_LOG_TAG(TAG, "ads1115");
static const uint8_t ADS1115_REGISTER_CONVERSION = 0x00;
static const uint8_t ADS1115_REGISTER_CONFIG = 0x01;
static constexpr uint8_t REGISTER_CONVERSION = 0x00;
static constexpr uint8_t REGISTER_CONFIG = 0x01;
static constexpr uint16_t CONFIG_COMPARATOR_DISABLE = 0b11;
void ADS1115Component::setup() {
uint16_t value;
if (!this->setup_check(this->read_byte_16(ADS1115_REGISTER_CONVERSION, &value)))
return;
uint16_t config = 0;
// Clear single-shot bit
// 0b0xxxxxxxxxxxxxxx
config |= 0b0000000000000000;
// Setup multiplexer
// 0bx000xxxxxxxxxxxx
config |= ADS1115_MULTIPLEXER_P0_N1 << 12;
// Setup Gain
// 0bxxxx000xxxxxxxxx
config |= ADS1115_GAIN_6P144 << 9;
if (this->continuous_mode_) {
// Set continuous mode
// 0bxxxxxxx0xxxxxxxx
config |= 0b0000000000000000;
} else {
// Set singleshot mode
// 0bxxxxxxx1xxxxxxxx
config |= 0b0000000100000000;
}
// Set data rate - 860 samples per second
// 0bxxxxxxxx100xxxxx
config |= ADS1115_860SPS << 5;
// Set comparator mode - hysteresis
// 0bxxxxxxxxxxx0xxxx
config |= 0b0000000000000000;
// Set comparator polarity - active low
// 0bxxxxxxxxxxxx0xxx
config |= 0b0000000000000000;
// Set comparator latch enabled - false
// 0bxxxxxxxxxxxxx0xx
config |= 0b0000000000000000;
// Set comparator que mode - disabled
// 0bxxxxxxxxxxxxxx11
config |= 0b0000000000000011;
if (!this->setup_check(this->write_byte_16(ADS1115_REGISTER_CONFIG, config)))
const uint16_t config = ads1xxx::CONFIG_DATA_RATE | CONFIG_COMPARATOR_DISABLE |
(this->continuous_mode_ ? 0 : ads1xxx::CONFIG_SINGLE_SHOT);
if (!this->setup_check(this->write_byte_16(REGISTER_CONFIG, config)))
return;
this->prev_config_ = config;
}
@@ -63,92 +21,23 @@ void ADS1115Component::dump_config() {
ESP_LOGCONFIG(TAG, "ADS1115:");
LOG_I2C_DEVICE(this);
}
float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain,
ADS1115Resolution resolution, ADS1115Samplerate samplerate) {
uint16_t config = this->prev_config_;
// Multiplexer
// 0bxBBBxxxxxxxxxxxx
config &= 0b1000111111111111;
config |= (multiplexer & 0b111) << 12;
// Gain
// 0bxxxxBBBxxxxxxxxx
config &= 0b1111000111111111;
config |= (gain & 0b111) << 9;
// Sample rate
// 0bxxxxxxxxBBBxxxxx
config &= 0b1111111100011111;
config |= (samplerate & 0b111) << 5;
if (!this->continuous_mode_) {
// Start conversion
config |= 0b1000000000000000;
}
float ADS1115Component::request_measurement(uint16_t config_bits, uint8_t conversion_delay_ms) {
const uint16_t config = CONFIG_COMPARATOR_DISABLE | config_bits |
(this->continuous_mode_ ? 0 : ads1xxx::CONFIG_SINGLE_SHOT | ads1xxx::CONFIG_START);
if (!this->continuous_mode_ || this->prev_config_ != config) {
if (!this->status_comm_check(this->write_byte_16(ADS1115_REGISTER_CONFIG, config)))
if (!this->status_comm_check(this->write_byte_16(REGISTER_CONFIG, config)))
return NAN;
this->prev_config_ = config;
delay(conversion_delay_ms);
// Delay calculated as: ceil((1000/SPS)+.5)
if (resolution == ADS1015_12_BITS) {
switch (samplerate) {
case ADS1115_8SPS:
delay(9);
break;
case ADS1115_16SPS:
delay(5);
break;
case ADS1115_32SPS:
delay(3);
break;
case ADS1115_64SPS:
case ADS1115_128SPS:
delay(2);
break;
default:
delay(1);
break;
}
} else {
switch (samplerate) {
case ADS1115_8SPS:
delay(126); // NOLINT
break;
case ADS1115_16SPS:
delay(63); // NOLINT
break;
case ADS1115_32SPS:
delay(32);
break;
case ADS1115_64SPS:
delay(17);
break;
case ADS1115_128SPS:
delay(9);
break;
case ADS1115_250SPS:
delay(5);
break;
case ADS1115_475SPS:
delay(3);
break;
case ADS1115_860SPS:
delay(2);
break;
}
}
// in continuous mode, conversion will always be running, rely on the delay
// to ensure conversion is taking place with the correct settings
// can we use the rdy pin to trigger when a conversion is done?
// In continuous mode the delay alone covers a conversion with the new settings
if (!this->continuous_mode_) {
uint32_t start = millis();
while (this->read_byte_16(ADS1115_REGISTER_CONFIG, &config) && (config >> 15) == 0) {
uint16_t status;
while (this->read_byte_16(REGISTER_CONFIG, &status) && (status & ads1xxx::CONFIG_START) == 0) {
if (millis() - start > 100) {
ESP_LOGW(TAG, "Reading ADS1115 timed out");
this->status_set_warning();
this->status_comm_result(false, LOG_STR("Conversion timed out"));
return NAN;
}
yield();
@@ -156,43 +45,10 @@ float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1
}
}
uint16_t raw_conversion;
if (!this->status_comm_result(this->read_byte_16(ADS1115_REGISTER_CONVERSION, &raw_conversion)))
uint16_t raw;
if (!this->status_comm_result(this->read_byte_16(REGISTER_CONVERSION, &raw)))
return NAN;
if (resolution == ADS1015_12_BITS) {
// ADS1015 returns 12-bit value left-justified in 16 bits; shift right and sign-extend
raw_conversion = static_cast<uint16_t>(static_cast<int16_t>(raw_conversion) >> (16 - ADS1015_12_BITS));
}
auto signed_conversion = static_cast<int16_t>(raw_conversion);
float millivolts;
float divider = (resolution == ADS1115_16_BITS) ? 32768.0f : 2048.0f;
switch (gain) {
case ADS1115_GAIN_6P144:
millivolts = (signed_conversion * 6144) / divider;
break;
case ADS1115_GAIN_4P096:
millivolts = (signed_conversion * 4096) / divider;
break;
case ADS1115_GAIN_2P048:
millivolts = (signed_conversion * 2048) / divider;
break;
case ADS1115_GAIN_1P024:
millivolts = (signed_conversion * 1024) / divider;
break;
case ADS1115_GAIN_0P512:
millivolts = (signed_conversion * 512) / divider;
break;
case ADS1115_GAIN_0P256:
millivolts = (signed_conversion * 256) / divider;
break;
default:
millivolts = NAN;
}
return millivolts / 1e3f;
return ads1xxx::to_volts(static_cast<int16_t>(raw), config_bits);
}
} // namespace esphome::ads1115
+4 -44
View File
@@ -3,60 +3,20 @@
#include "esphome/components/i2c/i2c.h"
#include "esphome/core/component.h"
#include <vector>
namespace esphome::ads1115 {
enum ADS1115Multiplexer {
ADS1115_MULTIPLEXER_P0_N1 = 0b000,
ADS1115_MULTIPLEXER_P0_N3 = 0b001,
ADS1115_MULTIPLEXER_P1_N3 = 0b010,
ADS1115_MULTIPLEXER_P2_N3 = 0b011,
ADS1115_MULTIPLEXER_P0_NG = 0b100,
ADS1115_MULTIPLEXER_P1_NG = 0b101,
ADS1115_MULTIPLEXER_P2_NG = 0b110,
ADS1115_MULTIPLEXER_P3_NG = 0b111,
};
enum ADS1115Gain {
ADS1115_GAIN_6P144 = 0b000,
ADS1115_GAIN_4P096 = 0b001,
ADS1115_GAIN_2P048 = 0b010,
ADS1115_GAIN_1P024 = 0b011,
ADS1115_GAIN_0P512 = 0b100,
ADS1115_GAIN_0P256 = 0b101,
};
enum ADS1115Resolution {
ADS1115_16_BITS = 16,
ADS1015_12_BITS = 12,
};
enum ADS1115Samplerate {
ADS1115_8SPS = 0b000,
ADS1115_16SPS = 0b001,
ADS1115_32SPS = 0b010,
ADS1115_64SPS = 0b011,
ADS1115_128SPS = 0b100,
ADS1115_250SPS = 0b101,
ADS1115_475SPS = 0b110,
ADS1115_860SPS = 0b111
};
class ADS1115Component final : public Component, public i2c::I2CDevice {
public:
explicit ADS1115Component(bool continuous_mode) : continuous_mode_(continuous_mode) {}
void setup() override;
void dump_config() override;
/// HARDWARE_LATE setup priority
void set_continuous_mode(bool continuous_mode) { continuous_mode_ = continuous_mode; }
/// Helper method to request a measurement from a sensor.
float request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain, ADS1115Resolution resolution,
ADS1115Samplerate samplerate);
/// Returns volts for `config_bits` (multiplexer, gain and data rate fields of the config register).
float request_measurement(uint16_t config_bits, uint8_t conversion_delay_ms);
protected:
uint16_t prev_config_{0};
bool continuous_mode_;
const bool continuous_mode_;
};
} // namespace esphome::ads1115
+14 -50
View File
@@ -1,10 +1,10 @@
import math
import esphome.codegen as cg
from esphome.components import sensor, voltage_sampler
from esphome.components.ads1xxx import MUX_GAIN_SCHEMA, mux_gain_bits
import esphome.config_validation as cv
from esphome.const import (
CONF_GAIN,
CONF_ID,
CONF_MULTIPLEXER,
CONF_RESOLUTION,
CONF_SAMPLE_RATE,
DEVICE_CLASS_VOLTAGE,
@@ -18,45 +18,13 @@ from .. import CONF_ADS1115_ID, ADS1115Component, ads1115_ns
AUTO_LOAD = ["voltage_sampler"]
DEPENDENCIES = ["ads1115"]
ADS1115Multiplexer = ads1115_ns.enum("ADS1115Multiplexer")
MUX = {
"A0_A1": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_N1,
"A0_A3": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_N3,
"A1_A3": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P1_N3,
"A2_A3": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P2_N3,
"A0_GND": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_NG,
"A1_GND": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P1_NG,
"A2_GND": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P2_NG,
"A3_GND": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P3_NG,
}
ADS1115Gain = ads1115_ns.enum("ADS1115Gain")
GAIN = {
"6.144": ADS1115Gain.ADS1115_GAIN_6P144,
"4.096": ADS1115Gain.ADS1115_GAIN_4P096,
"2.048": ADS1115Gain.ADS1115_GAIN_2P048,
"1.024": ADS1115Gain.ADS1115_GAIN_1P024,
"0.512": ADS1115Gain.ADS1115_GAIN_0P512,
"0.256": ADS1115Gain.ADS1115_GAIN_0P256,
}
ADS1115Resolution = ads1115_ns.enum("ADS1115Resolution")
RESOLUTION = {
"16_BITS": ADS1115Resolution.ADS1115_16_BITS,
"12_BITS": ADS1115Resolution.ADS1015_12_BITS,
}
ADS1115Samplerate = ads1115_ns.enum("ADS1115Samplerate")
SAMPLERATE = {
"8": ADS1115Samplerate.ADS1115_8SPS,
"16": ADS1115Samplerate.ADS1115_16SPS,
"32": ADS1115Samplerate.ADS1115_32SPS,
"64": ADS1115Samplerate.ADS1115_64SPS,
"128": ADS1115Samplerate.ADS1115_128SPS,
"250": ADS1115Samplerate.ADS1115_250SPS,
"475": ADS1115Samplerate.ADS1115_475SPS,
"860": ADS1115Samplerate.ADS1115_860SPS,
RESOLUTION = {"16_BITS": 16, "12_BITS": 12}
# Samples per second of each data rate code; the ADS1015 (12 bits) runs faster
DATA_RATES = {
16: (8, 16, 32, 64, 128, 250, 475, 860),
12: (128, 250, 490, 920, 1600, 2400, 3300, 3300),
}
SAMPLERATE = {str(sps): i for i, sps in enumerate(DATA_RATES[16])}
ADS1115Sensor = ads1115_ns.class_(
"ADS1115Sensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler
@@ -73,8 +41,7 @@ CONFIG_SCHEMA = (
.extend(
{
cv.GenerateID(CONF_ADS1115_ID): cv.use_id(ADS1115Component),
cv.Required(CONF_MULTIPLEXER): cv.enum(MUX, upper=True, space="_"),
cv.Required(CONF_GAIN): cv.enum(GAIN, string=True),
**MUX_GAIN_SCHEMA,
cv.Optional(CONF_RESOLUTION, default="16_BITS"): cv.enum(
RESOLUTION, upper=True, space="_"
),
@@ -88,12 +55,9 @@ CONFIG_SCHEMA = (
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await sensor.register_sensor(var, config)
rate = config[CONF_SAMPLE_RATE].enum_value
sps = DATA_RATES[config[CONF_RESOLUTION].enum_value][rate]
delay_ms = math.ceil(1000 / sps + 0.5)
var = await sensor.new_sensor(config, mux_gain_bits(config) | rate << 5, delay_ms)
await cg.register_component(var, config)
await cg.register_parented(var, config[CONF_ADS1115_ID])
cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER]))
cg.add(var.set_gain(config[CONF_GAIN]))
cg.add(var.set_resolution(config[CONF_RESOLUTION]))
cg.add(var.set_samplerate(config[CONF_SAMPLE_RATE]))
@@ -7,7 +7,7 @@ namespace esphome::ads1115 {
ESPHOME_LOG_TAG(TAG, "ads1115.sensor");
float ADS1115Sensor::sample() {
return this->parent_->request_measurement(this->multiplexer_, this->gain_, this->resolution_, this->samplerate_);
return this->parent_->request_measurement(this->config_bits_, this->conversion_delay_ms_);
}
void ADS1115Sensor::update() {
@@ -21,11 +21,9 @@ void ADS1115Sensor::update() {
void ADS1115Sensor::dump_config() {
LOG_SENSOR(" ", "ADS1115 Sensor", this);
ESP_LOGCONFIG(TAG,
" Multiplexer: %u\n"
" Gain: %u\n"
" Resolution: %u\n"
" Sample rate: %u",
this->multiplexer_, this->gain_, this->resolution_, this->samplerate_);
" Config: 0x%04X\n"
" Conversion delay: %u ms",
this->config_bits_, this->conversion_delay_ms_);
}
} // namespace esphome::ads1115
@@ -10,26 +10,21 @@
namespace esphome::ads1115 {
/// Internal holder class that is in instance of Sensor so that the hub can create individual sensors.
class ADS1115Sensor final : public sensor::Sensor,
public PollingComponent,
public voltage_sampler::VoltageSampler,
public Parented<ADS1115Component> {
public:
ADS1115Sensor(uint16_t config_bits, uint8_t conversion_delay_ms)
: config_bits_(config_bits), conversion_delay_ms_(conversion_delay_ms) {}
void update() override;
void set_multiplexer(ADS1115Multiplexer multiplexer) { this->multiplexer_ = multiplexer; }
void set_gain(ADS1115Gain gain) { this->gain_ = gain; }
void set_resolution(ADS1115Resolution resolution) { this->resolution_ = resolution; }
void set_samplerate(ADS1115Samplerate samplerate) { this->samplerate_ = samplerate; }
float sample() override;
void dump_config() override;
protected:
ADS1115Multiplexer multiplexer_;
ADS1115Gain gain_;
ADS1115Resolution resolution_;
ADS1115Samplerate samplerate_;
const uint16_t config_bits_;
const uint8_t conversion_delay_ms_;
};
} // namespace esphome::ads1115
+1
View File
@@ -4,6 +4,7 @@ import esphome.config_validation as cv
from esphome.types import ConfigType
CODEOWNERS = ["@solomondg1"]
AUTO_LOAD = ["ads1xxx"]
DEPENDENCIES = ["spi"]
DOMAIN = "ads1118"
MULTI_CONF = True
+11 -96
View File
@@ -1,81 +1,25 @@
#include "ads1118.h"
#include "esphome/components/ads1xxx/ads1xxx.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome::ads1118 {
ESPHOME_LOG_TAG(TAG, "ads1118");
static const uint8_t ADS1118_DATA_RATE_860_SPS = 0b111;
// DOUT pull-up, NOP 01 (write the config), reserved bit set
static constexpr uint16_t CONFIG_BASE = ads1xxx::CONFIG_SINGLE_SHOT | ads1xxx::CONFIG_DATA_RATE | 0b1011;
void ADS1118::setup() {
this->spi_setup();
this->config_ = 0;
// Setup multiplexer
// 0bx000xxxxxxxxxxxx
this->config_ |= ADS1118_MULTIPLEXER_P0_NG << 12;
// Setup Gain
// 0bxxxx000xxxxxxxxx
this->config_ |= ADS1118_GAIN_6P144 << 9;
// Set singleshot mode
// 0bxxxxxxx1xxxxxxxx
this->config_ |= 0b0000000100000000;
// Set data rate - 860 samples per second (we're in singleshot mode)
// 0bxxxxxxxx100xxxxx
this->config_ |= ADS1118_DATA_RATE_860_SPS << 5;
// Set temperature sensor mode - ADC
// 0bxxxxxxxxxxx0xxxx
this->config_ |= 0b0000000000000000;
// Set DOUT pull up - enable
// 0bxxxxxxxxxxxx0xxx
this->config_ |= 0b0000000000001000;
// NOP - must be 01
// 0bxxxxxxxxxxxxx01x
this->config_ |= 0b0000000000000010;
// Not used - can be 0 or 1, lets be positive
// 0bxxxxxxxxxxxxxxx1
this->config_ |= 0b0000000000000001;
}
void ADS1118::setup() { this->spi_setup(); }
void ADS1118::dump_config() {
ESP_LOGCONFIG(TAG, "ADS1118:");
LOG_PIN(" CS Pin:", this->cs_);
}
float ADS1118::request_measurement(ADS1118Multiplexer multiplexer, ADS1118Gain gain, bool temperature_mode) {
uint16_t temp_config = this->config_;
// Multiplexer
// 0bxBBBxxxxxxxxxxxx
temp_config &= 0b1000111111111111;
temp_config |= (multiplexer & 0b111) << 12;
// Gain
// 0bxxxxBBBxxxxxxxxx
temp_config &= 0b1111000111111111;
temp_config |= (gain & 0b111) << 9;
if (temperature_mode) {
// Set temperature sensor mode
// 0bxxxxxxxxxxx1xxxx
temp_config |= 0b0000000000010000;
} else {
// Set ADC mode
// 0bxxxxxxxxxxx0xxxx
temp_config &= 0b1111111111101111;
}
// Start conversion
temp_config |= 0b1000000000000000;
float ADS1118::request_measurement(uint16_t config_bits) {
this->enable();
this->write_byte16(temp_config);
this->write_byte16(CONFIG_BASE | ads1xxx::CONFIG_START | config_bits);
this->disable();
// about 1.2 ms with 860 samples per second
@@ -85,40 +29,11 @@ float ADS1118::request_measurement(ADS1118Multiplexer multiplexer, ADS1118Gain g
uint8_t adc_first_byte = this->read_byte();
uint8_t adc_second_byte = this->read_byte();
this->disable();
uint16_t raw_conversion = encode_uint16(adc_first_byte, adc_second_byte);
auto raw = static_cast<int16_t>(encode_uint16(adc_first_byte, adc_second_byte));
auto signed_conversion = static_cast<int16_t>(raw_conversion);
if (temperature_mode) {
return (signed_conversion >> 2) * 0.03125f;
} else {
float millivolts;
float divider = 32768.0f;
switch (gain) {
case ADS1118_GAIN_6P144:
millivolts = (signed_conversion * 6144) / divider;
break;
case ADS1118_GAIN_4P096:
millivolts = (signed_conversion * 4096) / divider;
break;
case ADS1118_GAIN_2P048:
millivolts = (signed_conversion * 2048) / divider;
break;
case ADS1118_GAIN_1P024:
millivolts = (signed_conversion * 1024) / divider;
break;
case ADS1118_GAIN_0P512:
millivolts = (signed_conversion * 512) / divider;
break;
case ADS1118_GAIN_0P256:
millivolts = (signed_conversion * 256) / divider;
break;
default:
millivolts = NAN;
}
return millivolts / 1e3f;
}
if (config_bits & CONFIG_TEMPERATURE)
return (raw >> 2) * 0.03125f;
return ads1xxx::to_volts(raw, config_bits);
}
} // namespace esphome::ads1118
+3 -26
View File
@@ -2,42 +2,19 @@
#include "esphome/components/spi/spi.h"
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
namespace esphome::ads1118 {
enum ADS1118Multiplexer {
ADS1118_MULTIPLEXER_P0_N1 = 0b000,
ADS1118_MULTIPLEXER_P0_N3 = 0b001,
ADS1118_MULTIPLEXER_P1_N3 = 0b010,
ADS1118_MULTIPLEXER_P2_N3 = 0b011,
ADS1118_MULTIPLEXER_P0_NG = 0b100,
ADS1118_MULTIPLEXER_P1_NG = 0b101,
ADS1118_MULTIPLEXER_P2_NG = 0b110,
ADS1118_MULTIPLEXER_P3_NG = 0b111,
};
enum ADS1118Gain {
ADS1118_GAIN_6P144 = 0b000,
ADS1118_GAIN_4P096 = 0b001,
ADS1118_GAIN_2P048 = 0b010,
ADS1118_GAIN_1P024 = 0b011,
ADS1118_GAIN_0P512 = 0b100,
ADS1118_GAIN_0P256 = 0b101,
};
static constexpr uint16_t CONFIG_TEMPERATURE = 1 << 4;
class ADS1118 final : public Component,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
spi::CLOCK_PHASE_TRAILING, spi::DATA_RATE_1MHZ> {
public:
ADS1118() = default;
void setup() override;
void dump_config() override;
/// Helper method to request a measurement from a sensor.
float request_measurement(ADS1118Multiplexer multiplexer, ADS1118Gain gain, bool temperature_mode);
protected:
uint16_t config_{0};
/// Returns volts, or degrees Celsius when `config_bits` has CONFIG_TEMPERATURE.
float request_measurement(uint16_t config_bits);
};
} // namespace esphome::ads1118
+8 -34
View File
@@ -1,9 +1,8 @@
import esphome.codegen as cg
from esphome.components import sensor, voltage_sampler
from esphome.components.ads1xxx import MUX_GAIN_SCHEMA, mux_gain_bits
import esphome.config_validation as cv
from esphome.const import (
CONF_GAIN,
CONF_MULTIPLEXER,
CONF_TYPE,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_VOLTAGE,
@@ -18,29 +17,6 @@ from .. import ADS1118, CONF_ADS1118_ID, ads1118_ns
AUTO_LOAD = ["voltage_sampler"]
DEPENDENCIES = ["ads1118"]
ADS1118Multiplexer = ads1118_ns.enum("ADS1118Multiplexer")
MUX = {
"A0_A1": ADS1118Multiplexer.ADS1118_MULTIPLEXER_P0_N1,
"A0_A3": ADS1118Multiplexer.ADS1118_MULTIPLEXER_P0_N3,
"A1_A3": ADS1118Multiplexer.ADS1118_MULTIPLEXER_P1_N3,
"A2_A3": ADS1118Multiplexer.ADS1118_MULTIPLEXER_P2_N3,
"A0_GND": ADS1118Multiplexer.ADS1118_MULTIPLEXER_P0_NG,
"A1_GND": ADS1118Multiplexer.ADS1118_MULTIPLEXER_P1_NG,
"A2_GND": ADS1118Multiplexer.ADS1118_MULTIPLEXER_P2_NG,
"A3_GND": ADS1118Multiplexer.ADS1118_MULTIPLEXER_P3_NG,
}
ADS1118Gain = ads1118_ns.enum("ADS1118Gain")
GAIN = {
"6.144": ADS1118Gain.ADS1118_GAIN_6P144,
"4.096": ADS1118Gain.ADS1118_GAIN_4P096,
"2.048": ADS1118Gain.ADS1118_GAIN_2P048,
"1.024": ADS1118Gain.ADS1118_GAIN_1P024,
"0.512": ADS1118Gain.ADS1118_GAIN_0P512,
"0.256": ADS1118Gain.ADS1118_GAIN_0P256,
}
ADS1118Sensor = ads1118_ns.class_(
"ADS1118Sensor",
cg.PollingComponent,
@@ -64,8 +40,7 @@ CONFIG_SCHEMA = cv.typed_schema(
.extend(
{
cv.GenerateID(CONF_ADS1118_ID): cv.use_id(ADS1118),
cv.Required(CONF_MULTIPLEXER): cv.enum(MUX, upper=True, space="_"),
cv.Required(CONF_GAIN): cv.enum(GAIN, string=True),
**MUX_GAIN_SCHEMA,
}
)
.extend(cv.polling_component_schema("60s")),
@@ -88,12 +63,11 @@ CONFIG_SCHEMA = cv.typed_schema(
async def to_code(config: ConfigType) -> None:
var = await sensor.new_sensor(config)
bits = (
mux_gain_bits(config)
if config[CONF_TYPE] == TYPE_ADC
else ads1118_ns.CONFIG_TEMPERATURE
)
var = await sensor.new_sensor(config, bits)
await cg.register_component(var, config)
await cg.register_parented(var, config[CONF_ADS1118_ID])
if config[CONF_TYPE] == TYPE_ADC:
cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER]))
cg.add(var.set_gain(config[CONF_GAIN]))
if config[CONF_TYPE] == TYPE_TEMPERATURE:
cg.add(var.set_temperature_mode(True))
@@ -8,15 +8,10 @@ ESPHOME_LOG_TAG(TAG, "ads1118.sensor");
void ADS1118Sensor::dump_config() {
LOG_SENSOR(" ", "ADS1118 Sensor", this);
ESP_LOGCONFIG(TAG,
" Multiplexer: %u\n"
" Gain: %u",
this->multiplexer_, this->gain_);
ESP_LOGCONFIG(TAG, " Config: 0x%04X", this->config_bits_);
}
float ADS1118Sensor::sample() {
return this->parent_->request_measurement(this->multiplexer_, this->gain_, this->temperature_mode_);
}
float ADS1118Sensor::sample() { return this->parent_->request_measurement(this->config_bits_); }
void ADS1118Sensor::update() {
float v = this->sample();
@@ -15,20 +15,15 @@ class ADS1118Sensor final : public PollingComponent,
public voltage_sampler::VoltageSampler,
public Parented<ADS1118> {
public:
explicit ADS1118Sensor(uint16_t config_bits) : config_bits_(config_bits) {}
void update() override;
void set_multiplexer(ADS1118Multiplexer multiplexer) { this->multiplexer_ = multiplexer; }
void set_gain(ADS1118Gain gain) { this->gain_ = gain; }
void set_temperature_mode(bool temp) { this->temperature_mode_ = temp; }
float sample() override;
void dump_config() override;
protected:
ADS1118Multiplexer multiplexer_{ADS1118_MULTIPLEXER_P0_NG};
ADS1118Gain gain_{ADS1118_GAIN_6P144};
bool temperature_mode_;
const uint16_t config_bits_;
};
} // namespace esphome::ads1118
+27
View File
@@ -0,0 +1,27 @@
import esphome.config_validation as cv
from esphome.const import CONF_GAIN, CONF_MULTIPLEXER
from esphome.types import ConfigType
CODEOWNERS = ["@bdraco"]
DOMAIN = "ads1xxx"
_MUX = {
name: i
for i, name in enumerate(
("A0_A1", "A0_A3", "A1_A3", "A2_A3", "A0_GND", "A1_GND", "A2_GND", "A3_GND")
)
}
_GAIN = {
name: i
for i, name in enumerate(("6.144", "4.096", "2.048", "1.024", "0.512", "0.256"))
}
MUX_GAIN_SCHEMA = {
cv.Required(CONF_MULTIPLEXER): cv.enum(_MUX, upper=True, space="_"),
cv.Required(CONF_GAIN): cv.enum(_GAIN, string=True),
}
def mux_gain_bits(config: ConfigType) -> int:
"""Return the multiplexer and gain fields of the config register."""
return config[CONF_MULTIPLEXER].enum_value << 12 | config[CONF_GAIN].enum_value << 9
+19
View File
@@ -0,0 +1,19 @@
#pragma once
#include <cstdint>
namespace esphome::ads1xxx {
// Config register fields shared by the ADS101x, ADS111x and ADS1118
static constexpr uint16_t CONFIG_START = 1 << 15; // reads back as 1 once the conversion is done
static constexpr uint16_t CONFIG_DATA_RATE = 0x00E0; // all bits set is the fastest rate
static constexpr uint16_t CONFIG_SINGLE_SHOT = 1 << 8;
/// Volts for a conversion result taken with the gain field of `config`; 12-bit results are left-justified.
inline float to_volts(int16_t raw, uint16_t config) {
const uint8_t gain = (config >> 9) & 0b111;
// Volts per count: 6.144 V full scale for gain 0, else 8.192 V >> gain
return raw * (gain ? 0.00025f : 0.0001875f) / (1 << gain);
}
} // namespace esphome::ads1xxx
+15 -2
View File
@@ -1,10 +1,23 @@
ads1115:
i2c_id: i2c_bus
address: 0x48
- id: ads1115_hub
i2c_id: i2c_bus
address: 0x48
- id: ads1015_hub
i2c_id: i2c_bus
address: 0x49
continuous_mode: true
sensor:
- platform: ads1115
ads1115_id: ads1115_hub
multiplexer: A0_A1
gain: 1.024
sample_rate: 128
id: ads1115_sensor
- platform: ads1115
ads1115_id: ads1015_hub
multiplexer: A3_GND
gain: 6.144
resolution: 12_BITS
sample_rate: 8
id: ads1015_sensor
+1 -2
View File
@@ -1,4 +1,3 @@
packages:
i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml
<<: !include common.yaml
ads1115: !include common.yaml
@@ -1,4 +1,3 @@
packages:
i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml
<<: !include common.yaml
ads1115: !include common.yaml
@@ -1,4 +1,3 @@
packages:
i2c: !include ../../test_build_components/common/i2c/nrf52.yaml
<<: !include common.yaml
ads1115: !include common.yaml
@@ -1,4 +1,3 @@
packages:
i2c: !include ../../test_build_components/common/i2c/nrf52.yaml
<<: !include common.yaml
ads1115: !include common.yaml
@@ -1,4 +1,3 @@
packages:
i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml
<<: !include common.yaml
ads1115: !include common.yaml