Working on directly publishing from ISR for ads1115_int. WIP (controller hangs)

This commit is contained in:
Chris Stuurman 2026-01-11 23:35:35 +02:00
parent a105cf0748
commit 20d2e5b4ef
6 changed files with 137 additions and 90 deletions

View File

@ -20,7 +20,7 @@ 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_CONTINUOUS_MODE, default=False): cv.boolean,
}
)
.extend(cv.COMPONENT_SCHEMA)
@ -37,4 +37,4 @@ async def to_code(config):
)
if alert_rdy is not None:
cg.add(var.set_alert_pin(alert_rdy))
cg.add(var.set_continuous_mode(config[CONF_CONTINUOUS_MODE]))
# cg.add(var.set_continuous_mode(config[CONF_CONTINUOUS_MODE]))

View File

@ -19,6 +19,7 @@ void ADS1115Component::setup() {
this->mark_failed();
return;
}
// setup with default values
uint16_t config = 0;
// Clear single-shot bit
// 0b0xxxxxxxxxxxxxxx
@ -71,6 +72,28 @@ void ADS1115Component::setup() {
return;
}
}
// Method to set mux data for later use, i.e. when data is to be read
void ADS1115Component::set_mux_data(ADS1115Multiplexer multiplexer, ADS1115Gain gain, ADS1115Resolution resolution, ADS1115Samplerate samplerate, sensor::Sensor *sensor_ptr)
{
int index = -1;
for(int i=0; i<4; i++) {
if(this->muxdata_[i].multiplexer == multiplexer) {
index = i;
break;
}
if(this->muxdata_[i].multiplexer == ADS1115_MULTIPLEXER_INVALID && index == -1) {
index = i;
}
}
if(index != -1) {
this->muxdata_[index].multiplexer = multiplexer;
this->muxdata_[index].gain = gain;
this->muxdata_[index].resolution = resolution;
this->muxdata_[index].samplerate = samplerate;
this->muxdata_[index].sensor_ptr = sensor_ptr;
}
}
bool ADS1115Component::set_data_ready_mode()
{
uint16_t config = this->prev_config_;
@ -88,36 +111,44 @@ bool ADS1115Component::set_data_ready_mode()
if(!this->write_byte_16(ADS1115_REGISTER_LO_TRESH, 0x0000)) {
return false;
}
// also clear all data_ready_ flags
for (int i = 0; i < 8; i++)
this->data_ready_[i] = false;
return true;
}
// ISR function to handle interrupt from alert pin
// only function that sets data_ready_ flags
void IRAM_ATTR ADS1115Component::isr(ADS1115Component *arg)
{
for (int i = 0; i < 8; i++)
arg->data_ready_[i] = true;
}
bool ADS1115Component::is_data_ready(ADS1115Multiplexer multiplexer)
{
int index = static_cast<int>(multiplexer);
if(index < 0 || index >= 8)
return false;
return this->data_ready_[index];
}
void ADS1115Component::clear_data_ready(ADS1115Multiplexer multiplexer)
{
int index = static_cast<int>(multiplexer);
if(index < 0 || index >= 8)
return;
this->data_ready_[index] = false;
arg->update_sensor();
}
// call only when data_ready_ is true
double ADS1115Component::read_data(int16_t& raw_value, ADS1115Multiplexer multiplexer, ADS1115Gain gain, ADS1115Resolution resolution, ADS1115Samplerate samplerate)
void ADS1115Component::update_sensor() {
int mux_index = this->mux_data_index_;
ADS1115Multiplexer multiplexer = this->muxdata_[mux_index].multiplexer;
if (multiplexer != ADS1115_MULTIPLEXER_INVALID) {
int16_t raw_value = 0;
double v = this->read_data(raw_value, mux_index);
auto sensor_ptr = this->muxdata_[mux_index].sensor_ptr;
if (!std::isnan(v)) {
if(this->count_ % 10 == 0) {
ESP_LOGI(TAG, "'% -18s': Raw=% 6d %fV", sensor_ptr->get_name().c_str(), raw_value, v);
}
this->count_++;
ESP_LOGD(TAG, "'%s': Got Voltage=%fV", sensor_ptr->get_name().c_str(), v);
sensor_ptr->publish_state(v);
}
}
if(next_mux_data_index() != -1) {
this->start_single_shot_conversion();
}
}
bool ADS1115Component::start_single_shot_conversion()
{
int mux_index = this->mux_data_index_;
ADS1115Multiplexer multiplexer = this->muxdata_[mux_index].multiplexer;
if (multiplexer != ADS1115_MULTIPLEXER_INVALID) {
ADS1115Gain gain = this->muxdata_[mux_index].gain;
ADS1115Resolution resolution = this->muxdata_[mux_index].resolution;
ADS1115Samplerate samplerate = this->muxdata_[mux_index].samplerate;
uint16_t config = this->prev_config_;
// Multiplexer
// 0bxBBBxxxxxxxxxxxx
@ -134,36 +165,41 @@ double ADS1115Component::read_data(int16_t& raw_value, ADS1115Multiplexer multip
config &= 0b1111111100011111;
config |= (samplerate & 0b111) << 5;
if (!this->continuous_mode_) {
// Start conversion
config |= 0b1000000000000000;
}
this->clear_data_ready(multiplexer);
//ESP_LOGI(TAG, "'mux:%d': Gain=%d, Res=%d, CONF=<0x%04X>", static_cast<int>(multiplexer), static_cast<int>(gain), static_cast<int>(resolution), config);
// write config if in single-shot mode or config has changed
if (!this->continuous_mode_ || this->prev_config_ != config) {
if (!this->write_byte_16(ADS1115_REGISTER_CONFIG, config)) {
this->status_set_warning();
return false;
}
this->prev_config_ = config;
//
// removed delay
//
if (!this->continuous_mode_) {
uint32_t start = millis();
// wait for conversion to complete
while (this->read_byte_16(ADS1115_REGISTER_CONFIG, &config) && (config >> 15) == 0) {
if (millis() - start > 100) {
ESP_LOGW(TAG, "Reading ADS1115 timed out");
this->status_set_warning();
return true;
}
return false;
}
int ADS1115Component::next_mux_data_index()
{
int start_index = this->mux_data_index_;
do {
this->mux_data_index_ = (this->mux_data_index_ + 1) % 4;
if(this->muxdata_[this->mux_data_index_].multiplexer != ADS1115_MULTIPLEXER_INVALID) {
return this->mux_data_index_;
}
} while(this->mux_data_index_ != start_index);
return -1; // no valid mux data found
}
// call only when data_ready_ is true
double ADS1115Component::read_data(int16_t& raw_value, int mux_index)
{
ADS1115Multiplexer multiplexer = this->muxdata_[mux_index].multiplexer;
raw_value = 0;
if(multiplexer == ADS1115_MULTIPLEXER_INVALID) {
ESP_LOGE(TAG, "MUX data not set for index %d", mux_index);
return NAN;
}
yield();
}
}
}
ADS1115Gain gain = this->muxdata_[mux_index].gain;
ADS1115Resolution resolution = this->muxdata_[mux_index].resolution;
uint16_t raw_conversion;
if (!this->read_byte_16(ADS1115_REGISTER_CONVERSION, &raw_conversion)) {
this->status_set_warning();

View File

@ -2,6 +2,7 @@
#include "esphome/components/i2c/i2c.h" // Ensure this header defines i2c::I2CDevice
#include "esphome/core/component.h" // Ensure this header defines Component
#include "esphome/components/sensor/sensor.h"
#include <vector>
#include <esphome/core/gpio.h>
@ -18,6 +19,7 @@ enum ADS1115Multiplexer {
ADS1115_MULTIPLEXER_P1_NG = 0b101,
ADS1115_MULTIPLEXER_P2_NG = 0b110,
ADS1115_MULTIPLEXER_P3_NG = 0b111,
ADS1115_MULTIPLEXER_INVALID = 0xFF
};
enum ADS1115Gain {
@ -46,25 +48,41 @@ enum ADS1115Samplerate {
};
class ADS1115Component : public Component, public i2c::I2CDevice {
private:
struct mux_data_item {
ADS1115Multiplexer multiplexer;
ADS1115Gain gain;
ADS1115Resolution resolution;
ADS1115Samplerate samplerate;
sensor::Sensor *sensor_ptr; // ADS1115Sensor *sensor_ptr
mux_data_item() { multiplexer = ADS1115_MULTIPLEXER_INVALID; }
};
void request_data();
int next_mux_data_index();
void update_sensor();
int mux_data_index_{0};
mux_data_item muxdata_[4];
uint64_t count_{0};
public:
void setup() override;
void dump_config() override;
/// HARDWARE_LATE setup priority
void set_continuous_mode(bool continuous_mode) { continuous_mode_ = continuous_mode; }
// we will force single-shot mode for interrupt operation; kept continuous_mode_ for ease of reversal if needed
//void set_continuous_mode(bool continuous_mode) { continuous_mode_ = continuous_mode; }
// Method to read converted data
double read_data(int16_t& raw_value, ADS1115Multiplexer multiplexer, ADS1115Gain gain, ADS1115Resolution resolution, ADS1115Samplerate samplerate);
double read_data(int16_t& raw_value, int mux_index);
bool start_single_shot_conversion();
void set_alert_pin(InternalGPIOPin *pin) { alert_pin_ = pin; }
bool set_data_ready_mode();
bool is_data_ready(ADS1115Multiplexer multiplexer);
void clear_data_ready(ADS1115Multiplexer multiplexer);
void set_mux_data(ADS1115Multiplexer multiplexer, ADS1115Gain gain, ADS1115Resolution resolution, ADS1115Samplerate samplerate, sensor::Sensor *sensor_ptr);
protected:
uint16_t prev_config_{0};
bool continuous_mode_;
bool continuous_mode_{false};
InternalGPIOPin *alert_pin_;
static void isr(ADS1115Component *arg);
volatile bool data_ready_[8]{false};
};

View File

@ -8,19 +8,13 @@ namespace ads1115_int {
static const char *const TAG = "ads1115_int.sensor";
void ADS1115Sensor::loop() {
if (parent_->is_data_ready(this->multiplexer_)) {
this->parent_->clear_data_ready(this->multiplexer_);
int16_t raw_value = -1000;
double v = this->parent_->read_data(raw_value, this->multiplexer_, this->gain_, this->resolution_, this->samplerate_);
if (!std::isnan(v)) {
if(this->count_ % 10 == 0) {
ESP_LOGI(TAG, "'% -18s': Raw=% 6d %fV", this->get_name().c_str(), raw_value, v);
}
this->count_++;
ESP_LOGD(TAG, "'%s': Got Voltage=%fV", this->get_name().c_str(), v);
this->publish_state(v);
}
if(this->first_reading_) {
this->parent_->set_mux_data(this->multiplexer_, this->gain_, this->resolution_, this->samplerate_, this);
//delay(100);
this->first_reading_ = false;
this->parent_->start_single_shot_conversion(); // this will set off the first conversion; subsequent conversions will be triggered by the ISR
}
// Data reading and publishing is handled in the parent component's update_sensor() method
}
void ADS1115Sensor::dump_config() {

View File

@ -30,7 +30,7 @@ class ADS1115Sensor : public sensor::Sensor,
ADS1115Gain gain_;
ADS1115Resolution resolution_;
ADS1115Samplerate samplerate_;
uint64_t count_{0};
bool first_reading_{true};
};
} // namespace ads1115

View File

@ -274,7 +274,6 @@ ads1115_pol:
ads1115_int:
- address: 0x48
id: ads1115_48
continuous_mode: true
alert_rdy_pin:
number: GPIO3
mode: