Added continuous mode to ADS1115_int

This commit is contained in:
Chris Stuurman 2026-01-15 21:37:28 +02:00
parent 391630fd06
commit 27a62002fa
5 changed files with 144 additions and 92 deletions

View File

@ -12,7 +12,8 @@ MULTI_CONF = True
ads1115_int_ns = cg.esphome_ns.namespace("ads1115_int")
ADS1115Component = ads1115_int_ns.class_("ADS1115Component", cg.Component, i2c.I2CDevice)
#CONF_CONTINUOUS_MODE = "continuous_mode"
CONF_CONTINUOUS_MODE = "continuous_mode"
CONF_INTERLEAVED_MODE = "interleaved_mode"
CONF_ADS1115_ID = "ads1115_id"
CONFIG_SCHEMA = (
@ -20,7 +21,8 @@ CONFIG_SCHEMA = (
{
cv.GenerateID(): cv.declare_id(ADS1115Component),
cv.Required(CONF_ALERT_RDY_PIN): pins.internal_gpio_input_pin_schema,
# cv.Optional(CONF_CONTINUOUS_MODE, default=False): cv.boolean,
cv.Optional(CONF_INTERLEAVED_MODE, default=False): cv.boolean,
cv.Optional(CONF_CONTINUOUS_MODE, default=False): cv.boolean,
}
)
.extend(cv.COMPONENT_SCHEMA)
@ -36,4 +38,5 @@ async def to_code(config):
config.get(CONF_ALERT_RDY_PIN)
)
cg.add(var.set_alert_pin(alert_rdy))
# cg.add(var.set_continuous_mode(config[CONF_CONTINUOUS_MODE]))
cg.add(var.set_interleaved_mode(config[CONF_INTERLEAVED_MODE]))
cg.add(var.set_continuous_mode(config[CONF_CONTINUOUS_MODE]))

View File

@ -35,38 +35,39 @@ void ADS1115Component::setup()
// 0bxxxx000xxxxxxxxx
config |= ADS1115_GAIN_6P144 << 9;
// Set singleshot mode - continuous mode does not make sense with interrupt
// 0bxxxxxxx1xxxxxxxx
config |= 0b0000000100000000;
if(!this->continuous_mode_) {
// 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;
// // 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->write_byte_16(ADS1115_REGISTER_CONFIG, config)) {
this->mark_failed("Could not write to ADS1115 config register on setup.");
this->mark_failed("ADS1115 config register write failed on setup.");
return;
}
this->prev_config_ = config;
if(!this->set_data_ready_mode()) {
this->mark_failed("Could not set ADS1115 data ready mode on setup.");
this->mark_failed("ADS1115 data ready mode setup failed.");
return;
}
}
@ -95,16 +96,24 @@ void ADS1115Component::read_request_next()
{
uint16_t config = 0x0000;
// We do not support multiple ads1115 sharing the same alert/rdy pin, however we still read the ads1115 config register to get the multiplexer value. This allows us to also check the operational status (OS) bit.
// If OS bit set, i.e. the conversion is done meaning we can read the data; this is valid only in single shot mode, in continuous mode (which is not supported by this code) we can read data without checking the status bit
// If OS bit set, i.e. the conversion is done meaning we can read the data; this is valid only in single shot mode, in continuous mode we can read data without checking the status bit
//ESP_LOGD(TAG, "Config: 0x%04X", this->prev_config_);
//ESP_LOGD(TAG, "I2C bus frequency: %.1f kHz", bus_->bus_frequency_ / 1000.0f);
if(this->read_byte_16(ADS1115_REGISTER_CONFIG, &config)) {
//ESP_LOGD(TAG, "Config register: 0x%04X", config);
int ads_mux = (config >> 12) & 0b111;
bool conversion_done = (config >> 15) == 1;
bool conversion_done = (config >> 15) == 1 || this->continuous_mode_;
//ESP_LOGD(TAG, "Current MUX setting according to ADS1115: %d", ads_mux);
int ads_mux_index = get_mux_index(static_cast<ADS1115Multiplexer>(ads_mux));
//ESP_LOGD(TAG, "Current MUX index according to ADS1115: %d", ads_mux_index);
if (conversion_done && ads_mux_index != -1) {
if (conversion_done && ads_mux_index != -1) {
if(this->interleaved_mode_) {
// in interleaved mode, we request the next conversion already here
int next_index = next_mux_data_index(ads_mux_index);
if(next_index != -1) {
this->start_conversion(next_index);
}
}
int16_t raw_value = 0;
double v = this->read_data(raw_value, ads_mux_index);
if (!std::isnan(v)) {
@ -112,10 +121,12 @@ void ADS1115Component::read_request_next()
ESP_LOGV(TAG, "'% -18s': Raw=% 6d %fV", sensor_ptr->get_name().c_str(), raw_value, v);
sensor_ptr->publish_state(v);
}
// we have read data for the current mux index, move to next
int next_index = next_mux_data_index(ads_mux_index);
if(next_index != -1) {
this->start_single_shot_conversion(next_index);
if(!this->interleaved_mode_) {
// we have read data for the current mux index, move to next
int next_index = next_mux_data_index(ads_mux_index);
if(next_index != -1) {
this->start_conversion(next_index);
}
}
}
}
@ -205,40 +216,32 @@ int ADS1115Component::set_mux_data(ADS1115Multiplexer multiplexer, ADS1115Gain g
this->muxdata_[index].gain = gain;
this->muxdata_[index].resolution = resolution;
this->muxdata_[index].samplerate = samplerate;
this->muxdata_[index].sensor_ptr = sensor_ptr;
}
this->muxdata_[index].sensor_ptr = sensor_ptr;
return index;
}
bool ADS1115Component::set_data_ready_mode()
{
bool success = true;
uint16_t config = this->prev_config_;
// Set comparator que mode - assert after one conversion
// 0bxxxxxxxxxxxxxx00
config &= 0b1111111111111100;
if (!this->write_byte_16(ADS1115_REGISTER_CONFIG, config)) {
ESP_LOGE(TAG, "Failed to write to ads1115 to set comparator que mode");
success = false;
return false;
}
else {
this->prev_config_ = config;
if(!this->write_byte_16(ADS1115_REGISTER_HI_TRESH, 0x8000)) {
ESP_LOGE(TAG, "Failed to write to ads1115 to set high threshold register for alert pin");
success = false;
}
else {
if(!this->write_byte_16(ADS1115_REGISTER_LO_TRESH, 0x0000)) {
ESP_LOGE(TAG, "Failed to write to ads1115 to set low threshold register for alert pin");
success = false;
}
}
this->prev_config_ = config;
if(!this->write_byte_16(ADS1115_REGISTER_HI_TRESH, 0x8000)) {
return false;
}
return success;
if(!this->write_byte_16(ADS1115_REGISTER_LO_TRESH, 0x0000)) {
return false;
}
return true;
}
bool ADS1115Component::start_single_shot_conversion(int mux_index)
bool ADS1115Component::start_conversion(int mux_index)
{
auto& muxdata = this->muxdata_[mux_index];
auto multiplexer = muxdata.multiplexer;
@ -262,7 +265,17 @@ bool ADS1115Component::start_single_shot_conversion(int mux_index)
config &= 0b1111111100011111;
config |= (samplerate & 0b111) << 5;
// Start conversion
if(!this->continuous_mode_) {
// Set single-shot mode
// 0bxxxxxxx1xxxxxxxx
config |= 0b0000000100000000;
}
else {
// Clear single-shot bit
// 0b0xxxxxxxxxxxxxxx
config |= 0b0000000000000000;
}
// Start conversion; this has no effect in continuous mode
config |= 0b1000000000000000;
if (!this->write_byte_16(ADS1115_REGISTER_CONFIG, config)) {
@ -291,6 +304,8 @@ void ADS1115Component::dump_config()
{
ESP_LOGCONFIG(TAG, "ADS1115_INT:");
LOG_I2C_DEVICE(this);
ESP_LOGCONFIG(TAG, " Interleaved mode: %s", YESNO(this->interleaved_mode_));
ESP_LOGCONFIG(TAG, " Continuous mode: %s", YESNO(this->continuous_mode_));
LOG_PIN(" ALERT Pin:", this->alert_pin_);
if (this->is_failed()) {
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);

View File

@ -73,15 +73,20 @@ class ADS1115Component : public Component, public i2c::I2CDevice {
void loop() override;
void dump_config() override;
double read_data(int16_t& raw_value, int mux_index);
bool start_single_shot_conversion(int mux_index);
bool start_conversion(int mux_index);
void set_alert_pin(InternalGPIOPin *pin) { alert_pin_ = pin; }
void set_interleaved_mode(bool interleaved) { this->interleaved_mode_ = interleaved; }
void set_continuous_mode(bool continuous) { this->continuous_mode_ = continuous; }
bool set_data_ready_mode();
int set_mux_data(ADS1115Multiplexer multiplexer, ADS1115Gain gain, ADS1115Resolution resolution, ADS1115Samplerate samplerate, sensor::Sensor *sensor_ptr);
protected:
bool interleaved_mode_{false};
bool continuous_mode_{false};
uint16_t prev_config_{0};
InternalGPIOPin *alert_pin_;
static void IRAM_ATTR isr_handler(ADS1115Component *arg);
};
} // namespace ads1115_int

View File

@ -12,7 +12,7 @@ void ADS1115Sensor::loop()
if(this->first_reading_) {
int mux_index = this->parent_->set_mux_data(this->multiplexer_, this->gain_, this->resolution_, this->samplerate_, this);
this->first_reading_ = false;
this->parent_->start_single_shot_conversion(mux_index); // this will set off the first conversion; subsequent conversions will be triggered by the ISR
this->parent_->start_conversion(mux_index); // this will set off the first conversion; subsequent conversions will be triggered by the ISR
}
}

View File

@ -26,6 +26,8 @@ esphome:
#
esp32:
board: esp32dev
cpu_frequency: 240MHz
flash_size: 4MB
framework:
type: esp-idf #arduino # esp-idf
@ -98,40 +100,40 @@ time:
# - logger.log: "Synchronized system clock"
#
#tlc59208f:
# address: 0x20
# id: tlc59208f_1
# i2c_id: bus_a
#
#output:
# - platform: ledc
# pin:
# number: GPIO12 #GPIO26 # LED_LOW_BAT
# inverted: false #true
# id: led_inverter_battery_low
#
# - platform: tlc59208f
# channel: 0
# tlc59208f_id: 'tlc59208f_1'
# id: led0
#
# - platform: tlc59208f
# channel: 1
# tlc59208f_id: 'tlc59208f_1'
# id: led1
#
#light:
# - platform: monochromatic
# output: led0
# name: "LED Geyser Temperature 0"
# id: led_geyser_temp0
# default_transition_length: 20ms
#
# - platform: monochromatic
# output: led1
# name: "LED Geyser Temperature 1"
# id: led_geyser_temp1
# default_transition_length: 20ms
tlc59208f:
address: 0x50
id: tlc59208f_1
i2c_id: bus_b
output:
- platform: ledc
pin:
number: GPIO12 #GPIO26 # LED_LOW_BAT
inverted: false #true
id: led_inverter_battery_low
- platform: tlc59208f
channel: 0
tlc59208f_id: 'tlc59208f_1'
id: led0
- platform: tlc59208f
channel: 1
tlc59208f_id: 'tlc59208f_1'
id: led1
light:
- platform: monochromatic
output: led0
name: "LED Geyser Temperature 0"
id: led_geyser_temp0
default_transition_length: 20ms
- platform: monochromatic
output: led1
name: "LED Geyser Temperature 1"
id: led_geyser_temp1
default_transition_length: 20ms
binary_sensor:
@ -142,15 +144,23 @@ binary_sensor:
i2c:
sda: GPIO21
scl: GPIO22
scan: true
id: bus_a
frequency: 400kHz
- id: bus_a
sda: GPIO21
scl: GPIO22
scan: true
frequency: 400kHz
- id: bus_b
sda: GPIO26
scl: GPIO1
scan: true
frequency: 100kHz
ads1115_int:
- address: 0x4A
id: ads1115_4A
i2c_id: bus_a
interleaved_mode: true
continuous_mode: false
alert_rdy_pin:
number: GPIO27
mode:
@ -159,6 +169,9 @@ ads1115_int:
- address: 0x49
id: ads1115_49
i2c_id: bus_a
interleaved_mode: true
continuous_mode: false
alert_rdy_pin:
number: GPIO5
mode:
@ -167,6 +180,9 @@ ads1115_int:
- address: 0x48
id: ads1115_48
i2c_id: bus_a
interleaved_mode: true
continuous_mode: false
alert_rdy_pin:
number: GPIO3
mode:
@ -212,7 +228,7 @@ sensor:
send_every: 104 #208 #416
send_first_at: 104 #208 #416
- lambda: return sqrt(x);
- multiply: 555 #92.1 #91.1 #88.44
- multiply: 100 #92.1 #91.1 #88.44
- offset: 0.0 #-0.2
# - lambda: |-
# if(abs(x) < 0.1)
@ -257,6 +273,19 @@ sensor:
# mod ###########################
unit_of_measurement: "A"
icon: "mdi:current"
filters:
- lambda: return x * x;
- sliding_window_moving_average:
window_size: 625 #1250 #5000
send_every: 104 #208 #416
send_first_at: 104 #208 #416
- lambda: return sqrt(x);
- multiply: 100 #92.1 #91.1 #88.44
- offset: 0.0 #-0.2
# - lambda: |-
# if(abs(x) < 0.1)
# return 0.0;
# return x;
# ads1115_4A
# Inverter voltage sensor