Got ISR method working with ads1115_int using semaphore.
This commit is contained in:
parent
20d2e5b4ef
commit
602cd9dc0d
@ -12,7 +12,7 @@ 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_ADS1115_ID = "ads1115_id"
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
@ -35,6 +35,5 @@ async def to_code(config):
|
||||
alert_rdy = await cg.gpio_pin_expression(
|
||||
config.get(CONF_ALERT_RDY_PIN)
|
||||
)
|
||||
if alert_rdy is not None:
|
||||
cg.add(var.set_alert_pin(alert_rdy))
|
||||
# cg.add(var.set_continuous_mode(config[CONF_CONTINUOUS_MODE]))
|
||||
|
||||
@ -11,10 +11,13 @@ static const uint8_t ADS1115_REGISTER_CONFIG = 0x01;
|
||||
static const uint8_t ADS1115_REGISTER_LO_TRESH = 0x02;
|
||||
static const uint8_t ADS1115_REGISTER_HI_TRESH = 0x03;
|
||||
|
||||
void ADS1115Component::setup() {
|
||||
void ADS1115Component::setup()
|
||||
{
|
||||
uint16_t value;
|
||||
// 1. Create a binary semaphore
|
||||
data_ready_sem = xSemaphoreCreateBinary();
|
||||
this->alert_pin_->setup();
|
||||
this->alert_pin_->attach_interrupt(&ADS1115Component::isr, this, gpio::INTERRUPT_FALLING_EDGE);
|
||||
this->alert_pin_->attach_interrupt(&ADS1115Component::isr_handler, this, gpio::INTERRUPT_FALLING_EDGE);
|
||||
if (!this->read_byte_16(ADS1115_REGISTER_CONVERSION, &value)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
@ -32,15 +35,9 @@ void ADS1115Component::setup() {
|
||||
// 0bxxxx000xxxxxxxxx
|
||||
config |= ADS1115_GAIN_6P144 << 9;
|
||||
|
||||
if (this->continuous_mode_) {
|
||||
// Set continuous mode
|
||||
// 0bxxxxxxx0xxxxxxxx
|
||||
config |= 0b0000000000000000;
|
||||
} else {
|
||||
// Set singleshot mode
|
||||
// Set singleshot mode - continuous mode does not make sense with interrupt
|
||||
// 0bxxxxxxx1xxxxxxxx
|
||||
config |= 0b0000000100000000;
|
||||
}
|
||||
|
||||
// Set data rate - 860 samples per second
|
||||
// 0bxxxxxxxx100xxxxx
|
||||
@ -72,8 +69,106 @@ void ADS1115Component::setup() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// ISR function to handle interrupt from alert pin
|
||||
// MUST be fast and non-blocking
|
||||
void IRAM_ATTR ADS1115Component::isr_handler(ADS1115Component *arg)
|
||||
{
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
xSemaphoreGiveFromISR(arg->data_ready_sem, &xHigherPriorityTaskWoken);
|
||||
if (xHigherPriorityTaskWoken == pdTRUE) {
|
||||
portYIELD_FROM_ISR(); // Switch to the waiting task immediately
|
||||
}
|
||||
}
|
||||
|
||||
void ADS1115Component::loop()
|
||||
{
|
||||
// Check the semaphore (0 timeout means non-blocking)
|
||||
if (xSemaphoreTake(data_ready_sem, 0) == pdTRUE) {
|
||||
// Perform the I2C read here safely outside of ISR
|
||||
this->read_request_next();
|
||||
}
|
||||
}
|
||||
|
||||
// we read data for the current mux index, then set up the next conversion
|
||||
void ADS1115Component::read_request_next()
|
||||
{
|
||||
int mux_index = this->mux_data_index_ % 4;
|
||||
auto multiplexer = this->muxdata_[mux_index].multiplexer;
|
||||
if(multiplexer == ADS1115_MULTIPLEXER_INVALID) {
|
||||
ESP_LOGE(TAG, "MUX data not set for index %d", mux_index);
|
||||
}
|
||||
else {
|
||||
int16_t raw_value = 0;
|
||||
double v = this->read_data(raw_value, mux_index);
|
||||
if (!std::isnan(v)) {
|
||||
auto sensor_ptr = this->muxdata_[mux_index].sensor_ptr;
|
||||
ESP_LOGV(TAG, "'% -18s': Raw=% 6d %fV", sensor_ptr->get_name().c_str(), raw_value, v);
|
||||
sensor_ptr->publish_state(v);
|
||||
}
|
||||
}
|
||||
if(next_mux_data_index() != -1) {
|
||||
this->start_single_shot_conversion();
|
||||
}
|
||||
}
|
||||
|
||||
double ADS1115Component::read_data(int16_t& raw_value, int mux_index)
|
||||
{
|
||||
raw_value = 0;
|
||||
uint16_t raw_conversion;
|
||||
if (!this->read_byte_16(ADS1115_REGISTER_CONVERSION, &raw_conversion)) {
|
||||
this->status_set_warning();
|
||||
return NAN;
|
||||
}
|
||||
ADS1115Resolution resolution = this->muxdata_[mux_index].resolution;
|
||||
// Adjust for 12-bit resolution if needed
|
||||
if (resolution == ADS1015_12_BITS) {
|
||||
bool negative = (raw_conversion >> 15) == 1;
|
||||
|
||||
// shift raw_conversion as it's only 12-bits, left justified
|
||||
raw_conversion = raw_conversion >> (16 - ADS1015_12_BITS);
|
||||
|
||||
// check if number was negative in order to keep the sign
|
||||
if (negative) {
|
||||
// the number was negative
|
||||
// 1) set the negative bit back
|
||||
raw_conversion |= 0x8000;
|
||||
// 2) reset the former (shifted) negative bit
|
||||
raw_conversion &= 0xF7FF;
|
||||
}
|
||||
}
|
||||
auto signed_conversion = static_cast<int16_t>(raw_conversion);
|
||||
raw_value = signed_conversion;
|
||||
double millivolts;
|
||||
double divider = (resolution == ADS1115_16_BITS) ? 32768.0f : 2048.0f;
|
||||
switch (this->muxdata_[mux_index].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;
|
||||
}
|
||||
|
||||
this->status_clear_warning();
|
||||
return millivolts / 1e3f;
|
||||
}
|
||||
|
||||
// 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 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++) {
|
||||
@ -90,8 +185,9 @@ void ADS1115Component::set_mux_data(ADS1115Multiplexer multiplexer, ADS1115Gain
|
||||
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()
|
||||
@ -113,42 +209,17 @@ bool ADS1115Component::set_data_ready_mode()
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// ISR function to handle interrupt from alert pin
|
||||
// only function that sets data_ready_ flags
|
||||
void IRAM_ATTR ADS1115Component::isr(ADS1115Component *arg)
|
||||
{
|
||||
arg->update_sensor();
|
||||
}
|
||||
|
||||
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;
|
||||
auto& muxdata = this->muxdata_[mux_index];
|
||||
auto multiplexer = muxdata.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;
|
||||
auto gain = muxdata.gain;
|
||||
auto resolution = muxdata.resolution;
|
||||
auto samplerate = muxdata.samplerate;
|
||||
uint16_t config = this->prev_config_;
|
||||
// Multiplexer
|
||||
// 0bxBBBxxxxxxxxxxxx
|
||||
@ -188,70 +259,6 @@ int ADS1115Component::next_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;
|
||||
}
|
||||
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();
|
||||
return NAN;
|
||||
}
|
||||
//ESP_LOGI(TAG, "'mux:%d': raw=<0x%04X>", static_cast<int>(multiplexer), raw_conversion);
|
||||
|
||||
if (resolution == ADS1015_12_BITS) {
|
||||
bool negative = (raw_conversion >> 15) == 1;
|
||||
|
||||
// shift raw_conversion as it's only 12-bits, left justified
|
||||
raw_conversion = raw_conversion >> (16 - ADS1015_12_BITS);
|
||||
|
||||
// check if number was negative in order to keep the sign
|
||||
if (negative) {
|
||||
// the number was negative
|
||||
// 1) set the negative bit back
|
||||
raw_conversion |= 0x8000;
|
||||
// 2) reset the former (shifted) negative bit
|
||||
raw_conversion &= 0xF7FF;
|
||||
}
|
||||
}
|
||||
auto signed_conversion = static_cast<int16_t>(raw_conversion);
|
||||
raw_value = signed_conversion;
|
||||
double millivolts;
|
||||
double 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;
|
||||
}
|
||||
|
||||
this->status_clear_warning();
|
||||
return millivolts / 1e3f;
|
||||
}
|
||||
|
||||
void ADS1115Component::dump_config()
|
||||
{
|
||||
|
||||
@ -6,6 +6,8 @@
|
||||
|
||||
#include <vector>
|
||||
#include <esphome/core/gpio.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/semphr.h>
|
||||
|
||||
namespace esphome {
|
||||
namespace ads1115_int {
|
||||
@ -47,6 +49,7 @@ enum ADS1115Samplerate {
|
||||
ADS1115_860SPS = 0b111
|
||||
};
|
||||
|
||||
// we will force single-shot mode for interrupt operation
|
||||
class ADS1115Component : public Component, public i2c::I2CDevice {
|
||||
private:
|
||||
struct mux_data_item {
|
||||
@ -54,36 +57,30 @@ class ADS1115Component : public Component, public i2c::I2CDevice {
|
||||
ADS1115Gain gain;
|
||||
ADS1115Resolution resolution;
|
||||
ADS1115Samplerate samplerate;
|
||||
sensor::Sensor *sensor_ptr; // ADS1115Sensor *sensor_ptr
|
||||
sensor::Sensor *sensor_ptr;
|
||||
mux_data_item() { multiplexer = ADS1115_MULTIPLEXER_INVALID; }
|
||||
};
|
||||
void request_data();
|
||||
int next_mux_data_index();
|
||||
void update_sensor();
|
||||
void read_request_next();
|
||||
int mux_data_index_{0};
|
||||
mux_data_item muxdata_[4];
|
||||
uint64_t count_{0};
|
||||
|
||||
public:
|
||||
// Semaphore handle
|
||||
SemaphoreHandle_t data_ready_sem = NULL;
|
||||
void setup() override;
|
||||
void loop() override;
|
||||
void dump_config() override;
|
||||
/// HARDWARE_LATE setup priority
|
||||
// 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, int mux_index);
|
||||
bool start_single_shot_conversion();
|
||||
void set_alert_pin(InternalGPIOPin *pin) { alert_pin_ = pin; }
|
||||
bool set_data_ready_mode();
|
||||
void 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:
|
||||
uint16_t prev_config_{0};
|
||||
bool continuous_mode_{false};
|
||||
InternalGPIOPin *alert_pin_;
|
||||
static void isr(ADS1115Component *arg);
|
||||
|
||||
static void IRAM_ATTR isr_handler(ADS1115Component *arg);
|
||||
};
|
||||
|
||||
} // namespace ads1115_int
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import sensor, voltage_sampler
|
||||
from esphome.components import sensor #, voltage_sampler
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_GAIN,
|
||||
@ -14,7 +14,7 @@ from esphome.const import (
|
||||
|
||||
from .. import CONF_ADS1115_ID, ADS1115Component, ads1115_int_ns
|
||||
|
||||
AUTO_LOAD = ["voltage_sampler"]
|
||||
#AUTO_LOAD = ["voltage_sampler"]
|
||||
DEPENDENCIES = ["ads1115_int"]
|
||||
|
||||
ADS1115Multiplexer = ads1115_int_ns.enum("ADS1115Multiplexer")
|
||||
@ -58,7 +58,7 @@ SAMPLERATE = {
|
||||
}
|
||||
|
||||
ADS1115Sensor = ads1115_int_ns.class_(
|
||||
"ADS1115Sensor", sensor.Sensor, cg.Component, voltage_sampler.VoltageSampler
|
||||
"ADS1115Sensor", sensor.Sensor, cg.Component #, voltage_sampler.VoltageSampler
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
|
||||
@ -7,14 +7,13 @@ namespace ads1115_int {
|
||||
|
||||
static const char *const TAG = "ads1115_int.sensor";
|
||||
|
||||
void ADS1115Sensor::loop() {
|
||||
void ADS1115Sensor::loop()
|
||||
{
|
||||
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() {
|
||||
|
||||
@ -22,7 +22,6 @@ class ADS1115Sensor : public sensor::Sensor,
|
||||
void set_gain(ADS1115Gain gain) { this->gain_ = gain; }
|
||||
void set_resolution(ADS1115Resolution resolution) { this->resolution_ = resolution; }
|
||||
void set_samplerate(ADS1115Samplerate samplerate) { this->samplerate_ = samplerate; }
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
protected:
|
||||
@ -31,6 +30,7 @@ class ADS1115Sensor : public sensor::Sensor,
|
||||
ADS1115Resolution resolution_;
|
||||
ADS1115Samplerate samplerate_;
|
||||
bool first_reading_{true};
|
||||
|
||||
};
|
||||
|
||||
} // namespace ads1115
|
||||
|
||||
@ -15,9 +15,9 @@ void ADS1115Sensor::update() {
|
||||
int16_t raw_value = -1000;
|
||||
float v = this->sample(raw_value);
|
||||
if (!std::isnan(v)) {
|
||||
if(this->count_ % 10 == 0) {
|
||||
// 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);
|
||||
|
||||
@ -211,18 +211,20 @@ debug:
|
||||
|
||||
# Enable logging
|
||||
logger:
|
||||
level: INFO
|
||||
level: DEBUG
|
||||
initial_level: INFO
|
||||
logs:
|
||||
canbus: INFO
|
||||
i2c: INFO
|
||||
i2c.idf: INFO
|
||||
i2c: DEBUG
|
||||
i2c.idf: DEBUG
|
||||
uart: INFO
|
||||
light: INFO
|
||||
sensor: INFO
|
||||
ds1307: INFO
|
||||
text_sensor: INFO
|
||||
ads1115.sensor: INFO
|
||||
ads1115_pol.sensor: INFO
|
||||
ads1115_int.sensor: DEBUG
|
||||
modbus: INFO
|
||||
modbus_controller: INFO
|
||||
modbus_controller.sensor: INFO
|
||||
@ -266,7 +268,6 @@ ads1115:
|
||||
id: ads1115_4A
|
||||
continuous_mode: true
|
||||
|
||||
ads1115_pol:
|
||||
- address: 0x49
|
||||
id: ads1115_49
|
||||
continuous_mode: true
|
||||
@ -274,12 +275,15 @@ ads1115_pol:
|
||||
ads1115_int:
|
||||
- address: 0x48
|
||||
id: ads1115_48
|
||||
# continuous_mode: true
|
||||
alert_rdy_pin:
|
||||
number: GPIO3
|
||||
mode:
|
||||
input: true
|
||||
pullup: true
|
||||
|
||||
|
||||
|
||||
spi:
|
||||
- id: spi_bus0
|
||||
clk_pin: GPIO18
|
||||
@ -1548,7 +1552,7 @@ sensor:
|
||||
|
||||
# NB! Keep all ads1115 sample rates the same. Update intervals should be more than or equal to 1/sample_rate
|
||||
# ads1115_48
|
||||
- platform: ads1115_pol
|
||||
- platform: ads1115
|
||||
multiplexer: 'A2_A3'
|
||||
gain: 2.048 # 4.096
|
||||
ads1115_id: ads1115_49
|
||||
@ -1641,7 +1645,7 @@ sensor:
|
||||
# - lambda: |-
|
||||
# ESP_LOGI("geyser", "Geyser current detected. Geyser was energised."); #
|
||||
|
||||
- platform: ads1115_pol
|
||||
- platform: ads1115
|
||||
multiplexer: A0_A1
|
||||
gain: 2.048 # 4.096
|
||||
ads1115_id: ads1115_49
|
||||
|
||||
Loading…
Reference in New Issue
Block a user