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") ads1115_int_ns = cg.esphome_ns.namespace("ads1115_int")
ADS1115Component = ads1115_int_ns.class_("ADS1115Component", cg.Component, i2c.I2CDevice) 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" CONF_ADS1115_ID = "ads1115_id"
CONFIG_SCHEMA = ( CONFIG_SCHEMA = (
@ -20,7 +21,8 @@ CONFIG_SCHEMA = (
{ {
cv.GenerateID(): cv.declare_id(ADS1115Component), cv.GenerateID(): cv.declare_id(ADS1115Component),
cv.Required(CONF_ALERT_RDY_PIN): pins.internal_gpio_input_pin_schema, 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) .extend(cv.COMPONENT_SCHEMA)
@ -36,4 +38,5 @@ async def to_code(config):
config.get(CONF_ALERT_RDY_PIN) config.get(CONF_ALERT_RDY_PIN)
) )
cg.add(var.set_alert_pin(alert_rdy)) 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 // 0bxxxx000xxxxxxxxx
config |= ADS1115_GAIN_6P144 << 9; config |= ADS1115_GAIN_6P144 << 9;
// Set singleshot mode - continuous mode does not make sense with interrupt if(!this->continuous_mode_) {
// Set singleshot mode
// 0bxxxxxxx1xxxxxxxx // 0bxxxxxxx1xxxxxxxx
config |= 0b0000000100000000; config |= 0b0000000100000000;
}
// Set data rate - 860 samples per second // Set data rate - 860 samples per second
// 0bxxxxxxxx100xxxxx // 0bxxxxxxxx100xxxxx
config |= ADS1115_860SPS << 5; config |= ADS1115_860SPS << 5;
// Set comparator mode - hysteresis // // Set comparator mode - hysteresis
// 0bxxxxxxxxxxx0xxxx // // 0bxxxxxxxxxxx0xxxx
config |= 0b0000000000000000; // config |= 0b0000000000000000;
//
// Set comparator polarity - active low // // Set comparator polarity - active low
// 0bxxxxxxxxxxxx0xxx // // 0bxxxxxxxxxxxx0xxx
config |= 0b0000000000000000; // config |= 0b0000000000000000;
//
// Set comparator latch enabled - false // // Set comparator latch enabled - false
// 0bxxxxxxxxxxxxx0xx // // 0bxxxxxxxxxxxxx0xx
config |= 0b0000000000000000; // config |= 0b0000000000000000;
//
// Set comparator que mode - disabled // // Set comparator que mode - disabled
// 0bxxxxxxxxxxxxxx11 // // 0bxxxxxxxxxxxxxx11
config |= 0b0000000000000011; // config |= 0b0000000000000011;
if (!this->write_byte_16(ADS1115_REGISTER_CONFIG, config)) { 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; return;
} }
this->prev_config_ = config; this->prev_config_ = config;
if(!this->set_data_ready_mode()) { 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; return;
} }
} }
@ -95,16 +96,24 @@ void ADS1115Component::read_request_next()
{ {
uint16_t config = 0x0000; 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. // 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, "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)) { if(this->read_byte_16(ADS1115_REGISTER_CONFIG, &config)) {
//ESP_LOGD(TAG, "Config register: 0x%04X", config); //ESP_LOGD(TAG, "Config register: 0x%04X", config);
int ads_mux = (config >> 12) & 0b111; 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); //ESP_LOGD(TAG, "Current MUX setting according to ADS1115: %d", ads_mux);
int ads_mux_index = get_mux_index(static_cast<ADS1115Multiplexer>(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); //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; int16_t raw_value = 0;
double v = this->read_data(raw_value, ads_mux_index); double v = this->read_data(raw_value, ads_mux_index);
if (!std::isnan(v)) { 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); ESP_LOGV(TAG, "'% -18s': Raw=% 6d %fV", sensor_ptr->get_name().c_str(), raw_value, v);
sensor_ptr->publish_state(v); sensor_ptr->publish_state(v);
} }
if(!this->interleaved_mode_) {
// we have read data for the current mux index, move to next // we have read data for the current mux index, move to next
int next_index = next_mux_data_index(ads_mux_index); int next_index = next_mux_data_index(ads_mux_index);
if(next_index != -1) { if(next_index != -1) {
this->start_single_shot_conversion(next_index); 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].gain = gain;
this->muxdata_[index].resolution = resolution; this->muxdata_[index].resolution = resolution;
this->muxdata_[index].samplerate = samplerate; this->muxdata_[index].samplerate = samplerate;
}
this->muxdata_[index].sensor_ptr = sensor_ptr; this->muxdata_[index].sensor_ptr = sensor_ptr;
}
return index; return index;
} }
bool ADS1115Component::set_data_ready_mode() bool ADS1115Component::set_data_ready_mode()
{ {
bool success = true;
uint16_t config = this->prev_config_; uint16_t config = this->prev_config_;
// Set comparator que mode - assert after one conversion // Set comparator que mode - assert after one conversion
// 0bxxxxxxxxxxxxxx00 // 0bxxxxxxxxxxxxxx00
config &= 0b1111111111111100; config &= 0b1111111111111100;
if (!this->write_byte_16(ADS1115_REGISTER_CONFIG, config)) { if (!this->write_byte_16(ADS1115_REGISTER_CONFIG, config)) {
ESP_LOGE(TAG, "Failed to write to ads1115 to set comparator que mode"); return false;
success = false;
} }
else {
this->prev_config_ = config; this->prev_config_ = config;
if(!this->write_byte_16(ADS1115_REGISTER_HI_TRESH, 0x8000)) { 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"); return false;
success = false;
} }
else {
if(!this->write_byte_16(ADS1115_REGISTER_LO_TRESH, 0x0000)) { 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"); return false;
success = false;
} }
} return true;
}
return success;
} }
bool ADS1115Component::start_single_shot_conversion(int mux_index) bool ADS1115Component::start_conversion(int mux_index)
{ {
auto& muxdata = this->muxdata_[mux_index]; auto& muxdata = this->muxdata_[mux_index];
auto multiplexer = muxdata.multiplexer; auto multiplexer = muxdata.multiplexer;
@ -262,7 +265,17 @@ bool ADS1115Component::start_single_shot_conversion(int mux_index)
config &= 0b1111111100011111; config &= 0b1111111100011111;
config |= (samplerate & 0b111) << 5; 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; config |= 0b1000000000000000;
if (!this->write_byte_16(ADS1115_REGISTER_CONFIG, config)) { if (!this->write_byte_16(ADS1115_REGISTER_CONFIG, config)) {
@ -291,6 +304,8 @@ void ADS1115Component::dump_config()
{ {
ESP_LOGCONFIG(TAG, "ADS1115_INT:"); ESP_LOGCONFIG(TAG, "ADS1115_INT:");
LOG_I2C_DEVICE(this); 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_); LOG_PIN(" ALERT Pin:", this->alert_pin_);
if (this->is_failed()) { if (this->is_failed()) {
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); 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 loop() override;
void dump_config() override; void dump_config() override;
double read_data(int16_t& raw_value, int mux_index); 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_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(); bool set_data_ready_mode();
int set_mux_data(ADS1115Multiplexer multiplexer, ADS1115Gain gain, ADS1115Resolution resolution, ADS1115Samplerate samplerate, sensor::Sensor *sensor_ptr); int set_mux_data(ADS1115Multiplexer multiplexer, ADS1115Gain gain, ADS1115Resolution resolution, ADS1115Samplerate samplerate, sensor::Sensor *sensor_ptr);
protected: protected:
bool interleaved_mode_{false};
bool continuous_mode_{false};
uint16_t prev_config_{0}; uint16_t prev_config_{0};
InternalGPIOPin *alert_pin_; InternalGPIOPin *alert_pin_;
static void IRAM_ATTR isr_handler(ADS1115Component *arg); static void IRAM_ATTR isr_handler(ADS1115Component *arg);
}; };
} // namespace ads1115_int } // namespace ads1115_int

View File

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