diff --git a/code/espurna/config/prototypes.h b/code/espurna/config/prototypes.h index f9a2afbc..1d5b0a65 100644 --- a/code/espurna/config/prototypes.h +++ b/code/espurna/config/prototypes.h @@ -78,9 +78,7 @@ template bool idbSend(const char * topic, unsigned char id, T payloa #if DS18B20_SUPPORT #include #endif -#if EMON_ADS1115_SUPPORT -#include -#endif + #if PMSX003_SUPPORT #include #include diff --git a/code/espurna/config/sensors.h b/code/espurna/config/sensors.h index f60e6e8f..06c83919 100644 --- a/code/espurna/config/sensors.h +++ b/code/espurna/config/sensors.h @@ -143,9 +143,12 @@ //-------------------------------------------------------------------------------- #define EMON_MAX_SAMPLES 1000 // Max number of samples to get -#define EMON_MAX_TIME 500 // Max time in ms to sample +#define EMON_MAX_TIME 250 // Max time in ms to sample #define EMON_FILTER_SPEED 512 // Mobile average filter speed #define EMON_MAINS_VOLTAGE 230 // Mains voltage +#define EMON_REPORT_CURRENT 0 // Calculate current +#define EMON_REPORT_POWER 1 // Calculate power +#define EMON_REPORT_ENERGY 1 // Calculate energy //-------------------------------------------------------------------------------- // Energy Monitor based on interval analog GPIO @@ -181,21 +184,21 @@ #define EMON_ADC121_REFERENCE_VOLTAGE 3.3 // Reference voltage of the ADC //-------------------------------------------------------------------------------- -// Energy Monitor based on ADS1115 -// Enable support by passing EMON_ADS1115_SUPPORT=1 build flag +// Energy Monitor based on ADS1X15 +// Enable support by passing EMON_ADS1X15_SUPPORT=1 build flag //-------------------------------------------------------------------------------- -#ifndef EMON_ADS1115_SUPPORT -#define EMON_ADS1115_SUPPORT 0 // Do not build support by default +#ifndef EMON_ADS1X15_SUPPORT +#define EMON_ADS1X15_SUPPORT 0 // Do not build support by default #endif -#define EMON_ADS1115_PORT_MASK 0x08 // A0=1 A1=2 A2=4 A4=8 -#define EMON_ADS1115_I2C_ADDRESS 0x48 // I2C address of the ADS1115 +#define EMON_ADS1X15_ADS1115 1 // 0 for ADS10115, 1 for ADS1115 +#define EMON_ADS1X15_PORT_MASK 0x08 // A0=1 A1=2 A2=4 A4=8 +#define EMON_ADS1X15_I2C_ADDRESS 0x48 // I2C address of the ADS1115 -#define EMON_ADS1115_CURRENT_RATIO 30 // Current ratio in the clamp (30V/1A) -#define EMON_ADS1115_ADC_BITS 16 // ADC depth -#define EMON_ADS1115_GAIN ADS1115_PGA_4P096 -#define EMON_ADS1115_REFERENCE_VOLTAGE 8.192 // Double the gain for peak-to-peak +#define EMON_ADS1X15_CURRENT_RATIO 30 // Current ratio in the clamp (30V/1A) +#define EMON_ADS1X15_ADC_BITS 16 // ADC depth +#define EMON_ADS1X15_REFERENCE_VOLTAGE 8.192 // Double the gain for peak-to-peak //-------------------------------------------------------------------------------- // Particle Monitor based on Plantower PMSX003 diff --git a/code/espurna/filters/BaseFilter.h b/code/espurna/filters/BaseFilter.h index e86dac37..d11e99d8 100644 --- a/code/espurna/filters/BaseFilter.h +++ b/code/espurna/filters/BaseFilter.h @@ -11,29 +11,27 @@ class BaseFilter { public: BaseFilter() { - _data = new std::vector(); } ~BaseFilter() { - if (_data) delete _data; } virtual void add(double value) { - _data->push_back(value); + _data.push_back(value); } virtual unsigned char count() { - return _data->size(); + return _data.size(); } virtual void reset() { - _data->clear(); + _data.clear(); } virtual double max() { double max = 0; - for (unsigned char i = 1; i < _data->size(); i++) { - if (max < _data->at(i)) max = _data->at(i); + for (unsigned char i = 1; i < _data.size(); i++) { + if (max < _data.at(i)) max = _data.at(i); } return max; } @@ -44,6 +42,6 @@ class BaseFilter { protected: - std::vector *_data; + std::vector _data; }; diff --git a/code/espurna/filters/MedianFilter.h b/code/espurna/filters/MedianFilter.h index 298de511..e85e3793 100644 --- a/code/espurna/filters/MedianFilter.h +++ b/code/espurna/filters/MedianFilter.h @@ -11,8 +11,8 @@ class MedianFilter : public BaseFilter { public: virtual void reset() { - double last = _data->empty() ? 0 : _data->back(); - _data->clear(); + double last = _data.empty() ? 0 : _data.back(); + _data.clear(); add(last); } @@ -20,13 +20,13 @@ class MedianFilter : public BaseFilter { double sum = 0; - if (_data->size() > 2) { + if (_data.size() > 2) { - for (unsigned char i = 1; i <= _data->size() - 2; i++) { + for (unsigned char i = 1; i <= _data.size() - 2; i++) { - double previous = _data->at(i-1); - double current = _data->at(i); - double next = _data->at(i+1); + double previous = _data.at(i-1); + double current = _data.at(i); + double next = _data.at(i+1); if (previous > current) std::swap(previous, current); if (current > next) std::swap(current, next); @@ -36,11 +36,11 @@ class MedianFilter : public BaseFilter { } - sum /= (_data->size() - 2); + sum /= (_data.size() - 2); - } else if (_data->size() > 0) { + } else if (_data.size() > 0) { - sum = _data->front(); + sum = _data.front(); } diff --git a/code/espurna/filters/MovingAverageFilter.h b/code/espurna/filters/MovingAverageFilter.h index a5614164..f6eb40d3 100644 --- a/code/espurna/filters/MovingAverageFilter.h +++ b/code/espurna/filters/MovingAverageFilter.h @@ -14,13 +14,13 @@ class MovingAverageFilter : public BaseFilter { MovingAverageFilter(unsigned char size) { _size = size; for (unsigned char i=0; ipush_back(0); + _data.push_back(0); } } virtual void add(double value) { - _sum = _sum + value - _data->at(_pointer); - _data->at(_pointer) = value; + _sum = _sum + value - _data.at(_pointer); + _data.at(_pointer) = value; _pointer = (_pointer + 1) % _size; } diff --git a/code/espurna/sensor.ino b/code/espurna/sensor.ino index 8540dd1e..88e237ed 100644 --- a/code/espurna/sensor.ino +++ b/code/espurna/sensor.ino @@ -257,9 +257,9 @@ void sensorInit() { sensorRegister(new EmonADC121Sensor(EMON_ADC121_I2C_ADDRESS, EMON_MAINS_VOLTAGE, EMON_ADC121_ADC_BITS, EMON_ADC121_REFERENCE_VOLTAGE, EMON_ADC121_CURRENT_RATIO)); #endif - #if EMON_ADS1115_SUPPORT - #include "sensors/EmonADS1115Sensor.h" - sensorRegister(new EmonADS1115Sensor(EMON_ADS1115_I2C_ADDRESS, EMON_ADS1115_PORT_MASK, EMON_MAINS_VOLTAGE, EMON_ADS1115_ADC_BITS, EMON_ADS1115_REFERENCE_VOLTAGE, EMON_ADS1115_CURRENT_RATIO)); + #if EMON_ADS1X15_SUPPORT + #include "sensors/EmonADS1X15Sensor.h" + sensorRegister(new EmonADS1X15Sensor(EMON_ADS1X15_I2C_ADDRESS, EMON_ADS1X15_ADS1115, EMON_ADS1X15_PORT_MASK, EMON_MAINS_VOLTAGE, EMON_ADS1X15_ADC_BITS, EMON_ADS1X15_REFERENCE_VOLTAGE, EMON_ADS1X15_CURRENT_RATIO)); #endif #if PMSX003_SUPPORT diff --git a/code/espurna/sensors/EmonADC121Sensor.h b/code/espurna/sensors/EmonADC121Sensor.h index 1395d6cf..ae44d91e 100644 --- a/code/espurna/sensors/EmonADC121Sensor.h +++ b/code/espurna/sensors/EmonADC121Sensor.h @@ -32,7 +32,7 @@ class EmonADC121Sensor : public EmonSensor { // Cache _address = address; - _count = 4; + _count = _magnitudes; // Init sensor #if I2C_USE_BRZO @@ -50,7 +50,7 @@ class EmonADC121Sensor : public EmonSensor { #endif // warmup - read(_address); + read(_address, _pivot); } @@ -69,10 +69,16 @@ class EmonADC121Sensor : public EmonSensor { // Type for slot # index magnitude_t type(unsigned char index) { _error = SENSOR_ERROR_OK; - if (index == 0) return MAGNITUDE_CURRENT; - if (index == 1) return MAGNITUDE_POWER_APPARENT; - if (index == 2) return MAGNITUDE_ENERGY; - if (index == 3) return MAGNITUDE_ENERGY_DELTA; + unsigned char i = 0; + #if EMON_REPORT_CURRENT + if (index == i++) return MAGNITUDE_CURRENT; + #endif + #if EMON_REPORT_POWER + if (index == i++) return MAGNITUDE_POWER_APPARENT; + #endif + #if EMON_REPORT_ENERGY + if (index == i) return MAGNITUDE_ENERGY; + #endif _error = SENSOR_ERROR_OUT_OF_RANGE; return MAGNITUDE_NONE; } @@ -84,20 +90,25 @@ class EmonADC121Sensor : public EmonSensor { // Cache the value static unsigned long last = 0; - static double current = 0; - static unsigned long energy_delta = 0; - if ((last == 0) || (millis() - last > 1000)) { - current = read(_address); - energy_delta = current * _voltage * (millis() - last) / 1000; - _energy += energy_delta; + _current = read(0, _pivot); + #if EMON_REPORT_ENERGY + _energy += (_current * _voltage * (millis() - last) / 1000); + #endif last = millis(); } - if (index == 0) return current; - if (index == 1) return current * _voltage; - if (index == 2) return _energy; - if (index == 3) return energy_delta; + // Report + unsigned char i = 0; + #if EMON_REPORT_CURRENT + if (index == i++) return _current; + #endif + #if EMON_REPORT_POWER + if (index == i++) return _current * _voltage; + #endif + #if EMON_REPORT_ENERGY + if (index == i) return _energy; + #endif _error = SENSOR_ERROR_OUT_OF_RANGE; return 0; @@ -113,17 +124,17 @@ class EmonADC121Sensor : public EmonSensor { #if I2C_USE_BRZO uint8_t buffer[2]; buffer[0] = ADC121_REG_RESULT; - brzo_i2c_start_transaction(channel, I2C_SCL_FREQUENCY); + brzo_i2c_start_transaction(_address, I2C_SCL_FREQUENCY); brzo_i2c_write(buffer, 1, false); brzo_i2c_read(buffer, 2, false); brzo_i2c_end_transaction(); value = (buffer[0] & 0x0F) << 8; value |= buffer[1]; #else - Wire.beginTransmission(channel); + Wire.beginTransmission(_address); Wire.write(ADC121_REG_RESULT); Wire.endTransmission(); - Wire.requestFrom(channel, (unsigned char) 2); + Wire.requestFrom(_address, (unsigned char) 2); value = (Wire.read() & 0x0F) << 8; value = value + Wire.read(); #endif @@ -133,7 +144,10 @@ class EmonADC121Sensor : public EmonSensor { } unsigned char _address; - unsigned long _energy = 0; - + double _pivot = 0; + double _current = 0; + #if EMON_REPORT_ENERGY + unsigned long _energy = 0; + #endif }; diff --git a/code/espurna/sensors/EmonADS1115Sensor.h b/code/espurna/sensors/EmonADS1115Sensor.h deleted file mode 100644 index 889b0bac..00000000 --- a/code/espurna/sensors/EmonADS1115Sensor.h +++ /dev/null @@ -1,280 +0,0 @@ -// ----------------------------------------------------------------------------- -// Energy monitor sensor -// ----------------------------------------------------------------------------- - -#pragma once - -#include "Arduino.h" -#include "BaseSensor.h" -#include "EmonSensor.h" - -#include - -/* -#if I2C_USE_BRZO -#include -#else -#include -#endif - -#define ADS1015_CONVERSIONDELAY (1) -#define ADS1115_CONVERSIONDELAY (8) - -#define ADS1015_BIT_SHIFT (4) -#define ADS1115_BIT_SHIFT (0) - -#define ADS1015_REG_POINTER_MASK (0x03) -#define ADS1015_REG_POINTER_CONVERT (0x00) -#define ADS1015_REG_POINTER_CONFIG (0x01) -#define ADS1015_REG_POINTER_LOWTHRESH (0x02) -#define ADS1015_REG_POINTER_HITHRESH (0x03) - -#define ADS1015_REG_CONFIG_OS_MASK (0x8000) -#define ADS1015_REG_CONFIG_OS_SINGLE (0x8000) // Write: Set to start a single-conversion -#define ADS1015_REG_CONFIG_OS_BUSY (0x0000) // Read: Bit = 0 when conversion is in progress -#define ADS1015_REG_CONFIG_OS_NOTBUSY (0x8000) // Read: Bit = 1 when device is not performing a conversion - -#define ADS1015_REG_CONFIG_MUX_MASK (0x7000) -#define ADS1015_REG_CONFIG_MUX_DIFF_0_1 (0x0000) // Differential P = AIN0, N = AIN1 (default) -#define ADS1015_REG_CONFIG_MUX_DIFF_0_3 (0x1000) // Differential P = AIN0, N = AIN3 -#define ADS1015_REG_CONFIG_MUX_DIFF_1_3 (0x2000) // Differential P = AIN1, N = AIN3 -#define ADS1015_REG_CONFIG_MUX_DIFF_2_3 (0x3000) // Differential P = AIN2, N = AIN3 -#define ADS1015_REG_CONFIG_MUX_SINGLE_0 (0x4000) // Single-ended AIN0 -#define ADS1015_REG_CONFIG_MUX_SINGLE_1 (0x5000) // Single-ended AIN1 -#define ADS1015_REG_CONFIG_MUX_SINGLE_2 (0x6000) // Single-ended AIN2 -#define ADS1015_REG_CONFIG_MUX_SINGLE_3 (0x7000) // Single-ended AIN3 - -#define ADS1015_REG_CONFIG_PGA_MASK (0x0E00) -#define ADS1015_REG_CONFIG_PGA_6_144V (0x0000) // +/-6.144V range = Gain 2/3 -#define ADS1015_REG_CONFIG_PGA_4_096V (0x0200) // +/-4.096V range = Gain 1 -#define ADS1015_REG_CONFIG_PGA_2_048V (0x0400) // +/-2.048V range = Gain 2 (default) -#define ADS1015_REG_CONFIG_PGA_1_024V (0x0600) // +/-1.024V range = Gain 4 -#define ADS1015_REG_CONFIG_PGA_0_512V (0x0800) // +/-0.512V range = Gain 8 -#define ADS1015_REG_CONFIG_PGA_0_256V (0x0A00) // +/-0.256V range = Gain 16 - -#define ADS1015_REG_CONFIG_MODE_MASK (0x0100) -#define ADS1015_REG_CONFIG_MODE_CONTIN (0x0000) // Continuous conversion mode -#define ADS1015_REG_CONFIG_MODE_SINGLE (0x0100) // Power-down single-shot mode (default) - -#define ADS1015_REG_CONFIG_DR_MASK (0x00E0) -#define ADS1015_REG_CONFIG_DR_128SPS (0x0000) // 128 samples per second -#define ADS1015_REG_CONFIG_DR_250SPS (0x0020) // 250 samples per second -#define ADS1015_REG_CONFIG_DR_490SPS (0x0040) // 490 samples per second -#define ADS1015_REG_CONFIG_DR_920SPS (0x0060) // 920 samples per second -#define ADS1015_REG_CONFIG_DR_1600SPS (0x0080) // 1600 samples per second (default) -#define ADS1015_REG_CONFIG_DR_2400SPS (0x00A0) // 2400 samples per second -#define ADS1015_REG_CONFIG_DR_3300SPS (0x00C0) // 3300 samples per second - -#define ADS1015_REG_CONFIG_CMODE_MASK (0x0010) -#define ADS1015_REG_CONFIG_CMODE_TRAD (0x0000) // Traditional comparator with hysteresis (default) -#define ADS1015_REG_CONFIG_CMODE_WINDOW (0x0010) // Window comparator - -#define ADS1015_REG_CONFIG_CPOL_MASK (0x0008) -#define ADS1015_REG_CONFIG_CPOL_ACTVLOW (0x0000) // ALERT/RDY pin is low when active (default) -#define ADS1015_REG_CONFIG_CPOL_ACTVHI (0x0008) // ALERT/RDY pin is high when active - -#define ADS1015_REG_CONFIG_CLAT_MASK (0x0004) // Determines if ALERT/RDY pin latches once asserted -#define ADS1015_REG_CONFIG_CLAT_NONLAT (0x0000) // Non-latching comparator (default) -#define ADS1015_REG_CONFIG_CLAT_LATCH (0x0004) // Latching comparator - -#define ADS1015_REG_CONFIG_CQUE_MASK (0x0003) -#define ADS1015_REG_CONFIG_CQUE_1CONV (0x0000) // Assert ALERT/RDY after one conversions -#define ADS1015_REG_CONFIG_CQUE_2CONV (0x0001) // Assert ALERT/RDY after two conversions -#define ADS1015_REG_CONFIG_CQUE_4CONV (0x0002) // Assert ALERT/RDY after four conversions -#define ADS1015_REG_CONFIG_CQUE_NONE (0x0003) // Disable the comparator and put ALERT/RDY in high state (default) -*/ - -#define EMON_ADS1115_CHANNELS 4 -#define EMON_ADS1115_MAGNITUDES_PER_PORT 2 - -class EmonADS1115Sensor : public EmonSensor { - - public: - - EmonADS1115Sensor(unsigned char address, unsigned char mask, double voltage, unsigned char bits, double ref, double ratio): EmonSensor(voltage, bits, ref, ratio) { - - // Cache - _address = address; - _mask = mask; - _ports = 0; - while (mask) { - if (mask & 0x01) ++_ports; - mask = mask >> 1; - } - _count = _ports * EMON_ADS1115_MAGNITUDES_PER_PORT; - - // Initialize - _ads = new ADS1115(_address); - _ads->initialize(); - _ads->setMode(ADS1115_MODE_SINGLESHOT); - _ads->setRate(ADS1115_RATE_860); - _ads->setGain(ADS1115_PGA_4P096); - _ads->setConversionReadyPinMode(); - - // warmup - read(_address); - - } - - // Descriptive name of the sensor - String name() { - char buffer[30]; - snprintf(buffer, sizeof(buffer), "EMON @ ADS1115 @ I2C (0x%02X)", _address); - return String(buffer); - } - - // Descriptive name of the slot # index - String slot(unsigned char index) { - char buffer[35]; - unsigned char port = getChannel(index / EMON_ADS1115_MAGNITUDES_PER_PORT); - snprintf(buffer, sizeof(buffer), "EMON @ ADS1115 (A%d) @ I2C (0x%02X)", port, _address); - return String(buffer); - } - - // Type for slot # index - magnitude_t type(unsigned char index) { - if (index < _count) { - _error = SENSOR_ERROR_OK; - unsigned char port = getChannel(index / EMON_ADS1115_MAGNITUDES_PER_PORT); - unsigned char magnitude = index % EMON_ADS1115_MAGNITUDES_PER_PORT; - if (magnitude == 0) return MAGNITUDE_CURRENT; - if (magnitude == 1) return MAGNITUDE_POWER_APPARENT; - //if (magnitude == 2) return MAGNITUDE_ENERGY; - //if (magnitude == 3) return MAGNITUDE_ENERGY_DELTA; - } - _error = SENSOR_ERROR_OUT_OF_RANGE; - return MAGNITUDE_NONE; - } - - void pre() { - //static unsigned long last = 0; - for (unsigned char index=0; index<_ports; index++) { - unsigned char port = getChannel(index); - _current[port] = read(port); - //if (last > 0) { - // _delta[port] = _current[port] * _voltage * (millis() - last) / 1000; - //} - //_energy[port] += _delta[port]; - } - //last = millis(); - } - - // Current value for slot # index - double value(unsigned char index) { - - if (index < _count) { - _error = SENSOR_ERROR_OK; - unsigned char port = getChannel(index / EMON_ADS1115_MAGNITUDES_PER_PORT); - unsigned char magnitude = index % EMON_ADS1115_MAGNITUDES_PER_PORT; - if (magnitude == 0) return _current[port]; - if (magnitude == 1) return _current[port] * _voltage; - //if (magnitude == 2) return _energy[port]; - //if (magnitude == 3) return _delta[port]; - } - - _error = SENSOR_ERROR_OUT_OF_RANGE; - return 0; - - } - - protected: - - unsigned char getChannel(unsigned char port) { - unsigned char count = 0; - unsigned char bit = 1; - for (unsigned char channel=0; channelsetMultiplexer(channel + 4); - return _ads->getConversion(true); - } - return 0; - } - - /* - unsigned int readADC(unsigned char channel) { - - if (channel > 3) return 0; - channel = 3; - unsigned int value; - - // Start with default values - uint16_t config = 0; - - config |= ADS1015_REG_CONFIG_CQUE_NONE; // Disable the comparator (default val) - config |= ADS1015_REG_CONFIG_CLAT_NONLAT; // Non-latching (default val) - config |= ADS1015_REG_CONFIG_CPOL_ACTVLOW; // Alert/Rdy active low (default val) - config |= ADS1015_REG_CONFIG_CMODE_TRAD; // Traditional comparator (default val) - config |= ADS1015_REG_CONFIG_DR_1600SPS; // 1600 samples per second (default) - config |= ADS1015_REG_CONFIG_MODE_SINGLE; // Single-shot mode (default) - config |= ADS1015_REG_CONFIG_OS_SINGLE; // Set 'start single-conversion' bit - config |= EMON_ADS1115_GAIN; // Set PGA/voltage range - config |= ((channel + 4) << 12); // Set single-ended input channel - - Serial.println(config); - - // Write config register to the ADC - #if I2C_USE_BRZO - uint8_t buffer[3]; - buffer[0] = ADS1015_REG_POINTER_CONFIG; - buffer[1] = config >> 8; - buffer[2] = config & 0xFF; - brzo_i2c_start_transaction(_address, I2C_SCL_FREQUENCY); - brzo_i2c_write(buffer, 3, false); - //brzo_i2c_end_transaction(); - #else - Wire.beginTransmission(_address); - Wire.write((uint8_t) ADS1015_REG_POINTER_CONFIG); - Wire.write((uint8_t) (config >> 8)); - Wire.write((uint8_t) (config & 0xFF)); - Wire.endTransmission(); - #endif - - // Wait for the conversion to complete - unsigned long start = millis(); - while (millis() - start < ADS1115_CONVERSIONDELAY) delay(1); - - // Read the conversion results - // Shift 12-bit results right 4 bits for the ADS1015 - #if I2C_USE_BRZO - buffer[0] = ADS1015_REG_POINTER_CONVERT; - //brzo_i2c_start_transaction(_address, I2C_SCL_FREQUENCY); - brzo_i2c_write(buffer, 1, false); - brzo_i2c_read(buffer, 2, false); - brzo_i2c_end_transaction(); - value = (buffer[0] & 0x0F) << 8; - value |= buffer[1]; - #else - Wire.beginTransmission(_address); - Wire.write(ADS1015_REG_POINTER_CONVERT); - Wire.endTransmission(); - Wire.requestFrom(_address, (unsigned char) 2); - value = Wire.read() << 8; - value |= Wire.read(); - #endif - - return value; - - } - */ - - ADS1115 * _ads; - - unsigned char _address; - unsigned char _mask; - unsigned char _ports; - double _current[EMON_ADS1115_CHANNELS] = {0, 0, 0, 0}; - //unsigned long _energy[EMON_ADS1115_CHANNELS] = {0, 0, 0, 0}; - //unsigned long _delta[EMON_ADS1115_CHANNELS] = {0, 0, 0, 0}; - - -}; diff --git a/code/espurna/sensors/EmonADS1X15Sensor.h b/code/espurna/sensors/EmonADS1X15Sensor.h new file mode 100644 index 00000000..65ac4e55 --- /dev/null +++ b/code/espurna/sensors/EmonADS1X15Sensor.h @@ -0,0 +1,319 @@ +// ----------------------------------------------------------------------------- +// Energy monitor sensor +// ----------------------------------------------------------------------------- + +#pragma once + +#include "Arduino.h" +#include "BaseSensor.h" +#include "EmonSensor.h" + +#if I2C_USE_BRZO + #include +#else + #include +#endif + +#define ADS1X15_CHANNELS (4) + +#define ADS1015_CONVERSIONDELAY (1) +#define ADS1115_CONVERSIONDELAY (8) + +#define ADS1015_BIT_SHIFT (4) +#define ADS1115_BIT_SHIFT (0) + +#define ADS1X15_REG_POINTER_MASK (0x03) +#define ADS1X15_REG_POINTER_CONVERT (0x00) +#define ADS1X15_REG_POINTER_CONFIG (0x01) +#define ADS1X15_REG_POINTER_LOWTHRESH (0x02) +#define ADS1X15_REG_POINTER_HITHRESH (0x03) + +#define ADS1X15_REG_CONFIG_OS_MASK (0x8000) +#define ADS1X15_REG_CONFIG_OS_SINGLE (0x8000) // Write: Set to start a single-conversion +#define ADS1X15_REG_CONFIG_OS_BUSY (0x0000) // Read: Bit = 0 when conversion is in progress +#define ADS1X15_REG_CONFIG_OS_NOTBUSY (0x8000) // Read: Bit = 1 when device is not performing a conversion + +#define ADS1X15_REG_CONFIG_MUX_MASK (0x7000) +#define ADS1X15_REG_CONFIG_MUX_DIFF_0_1 (0x0000) // Differential P = AIN0, N = AIN1 (default) +#define ADS1X15_REG_CONFIG_MUX_DIFF_0_3 (0x1000) // Differential P = AIN0, N = AIN3 +#define ADS1X15_REG_CONFIG_MUX_DIFF_1_3 (0x2000) // Differential P = AIN1, N = AIN3 +#define ADS1X15_REG_CONFIG_MUX_DIFF_2_3 (0x3000) // Differential P = AIN2, N = AIN3 +#define ADS1X15_REG_CONFIG_MUX_SINGLE_0 (0x4000) // Single-ended AIN0 +#define ADS1X15_REG_CONFIG_MUX_SINGLE_1 (0x5000) // Single-ended AIN1 +#define ADS1X15_REG_CONFIG_MUX_SINGLE_2 (0x6000) // Single-ended AIN2 +#define ADS1X15_REG_CONFIG_MUX_SINGLE_3 (0x7000) // Single-ended AIN3 + +#define ADS1X15_REG_CONFIG_PGA_MASK (0x0E00) +#define ADS1X15_REG_CONFIG_PGA_6_144V (0x0000) // +/-6.144V range = Gain 2/3 +#define ADS1X15_REG_CONFIG_PGA_4_096V (0x0200) // +/-4.096V range = Gain 1 +#define ADS1X15_REG_CONFIG_PGA_2_048V (0x0400) // +/-2.048V range = Gain 2 (default) +#define ADS1X15_REG_CONFIG_PGA_1_024V (0x0600) // +/-1.024V range = Gain 4 +#define ADS1X15_REG_CONFIG_PGA_0_512V (0x0800) // +/-0.512V range = Gain 8 +#define ADS1X15_REG_CONFIG_PGA_0_256V (0x0A00) // +/-0.256V range = Gain 16 + +#define ADS1X15_REG_CONFIG_MODE_MASK (0x0100) +#define ADS1X15_REG_CONFIG_MODE_CONTIN (0x0000) // Continuous conversion mode +#define ADS1X15_REG_CONFIG_MODE_SINGLE (0x0100) // Power-down single-shot mode (default) + +#define ADS1X15_REG_CONFIG_DR_MASK (0x00E0) +#define ADS1015_REG_CONFIG_DR_128SPS (0x0000) // 128 samples per second +#define ADS1015_REG_CONFIG_DR_250SPS (0x0020) // 250 samples per second +#define ADS1015_REG_CONFIG_DR_490SPS (0x0040) // 490 samples per second +#define ADS1015_REG_CONFIG_DR_920SPS (0x0060) // 920 samples per second +#define ADS1015_REG_CONFIG_DR_1600SPS (0x0080) // 1600 samples per second (default) +#define ADS1015_REG_CONFIG_DR_2400SPS (0x00A0) // 2400 samples per second +#define ADS1015_REG_CONFIG_DR_3300SPS (0x00C0) // 3300 samples per second +#define ADS1115_REG_CONFIG_DR_8SPS (0x0000) // 8 samples per second +#define ADS1115_REG_CONFIG_DR_16SPS (0x0020) // 16 samples per second +#define ADS1115_REG_CONFIG_DR_32SPS (0x0040) // 32 samples per second +#define ADS1115_REG_CONFIG_DR_64SPS (0x0060) // 64 samples per second +#define ADS1115_REG_CONFIG_DR_128SPS (0x0080) // 128 samples per second (default) +#define ADS1115_REG_CONFIG_DR_250SPS (0x00A0) // 250 samples per second +#define ADS1115_REG_CONFIG_DR_475SPS (0x00C0) // 475 samples per second +#define ADS1115_REG_CONFIG_DR_860SPS (0x00E0) // 860 samples per second + +#define ADS1X15_REG_CONFIG_CMODE_MASK (0x0010) +#define ADS1X15_REG_CONFIG_CMODE_TRAD (0x0000) // Traditional comparator with hysteresis (default) +#define ADS1X15_REG_CONFIG_CMODE_WINDOW (0x0010) // Window comparator + +#define ADS1X15_REG_CONFIG_CPOL_MASK (0x0008) +#define ADS1X15_REG_CONFIG_CPOL_ACTVLOW (0x0000) // ALERT/RDY pin is low when active (default) +#define ADS1X15_REG_CONFIG_CPOL_ACTVHI (0x0008) // ALERT/RDY pin is high when active + +#define ADS1X15_REG_CONFIG_CLAT_MASK (0x0004) // Determines if ALERT/RDY pin latches once asserted +#define ADS1X15_REG_CONFIG_CLAT_NONLAT (0x0000) // Non-latching comparator (default) +#define ADS1X15_REG_CONFIG_CLAT_LATCH (0x0004) // Latching comparator + +#define ADS1X15_REG_CONFIG_CQUE_MASK (0x0003) +#define ADS1X15_REG_CONFIG_CQUE_1CONV (0x0000) // Assert ALERT/RDY after one conversions +#define ADS1X15_REG_CONFIG_CQUE_2CONV (0x0001) // Assert ALERT/RDY after two conversions +#define ADS1X15_REG_CONFIG_CQUE_4CONV (0x0002) // Assert ALERT/RDY after four conversions +#define ADS1X15_REG_CONFIG_CQUE_NONE (0x0003) // Disable the comparator and put ALERT/RDY in high state (default) + + +class EmonADS1X15Sensor : public EmonSensor { + + public: + + EmonADS1X15Sensor(unsigned char address, bool is_ads1115, unsigned char mask, double voltage, unsigned char bits, double ref, double ratio): EmonSensor(voltage, bits, ref, ratio) { + + // Cache + _is_ads1115 = is_ads1115; + _address = address; + _mask = mask; + _ports = 0; + while (mask) { + if (mask & 0x01) ++_ports; + mask = mask >> 1; + } + _count = _ports * _magnitudes; + + // warmup + warmup(); + + } + + // Descriptive name of the sensor + String name() { + char buffer[30]; + snprintf(buffer, sizeof(buffer), "EMON @ ADS1%d15 @ I2C (0x%02X)", _is_ads1115 ? 1 : 0, _address); + return String(buffer); + } + + // Descriptive name of the slot # index + String slot(unsigned char index) { + char buffer[35]; + unsigned char channel = getChannel(index % _ports); + snprintf(buffer, sizeof(buffer), "EMON @ ADS1%d15 (A%d) @ I2C (0x%02X)", _is_ads1115 ? 1 : 0, channel, _address); + return String(buffer); + } + + // Type for slot # index + magnitude_t type(unsigned char index) { + if (index < _count) { + _error = SENSOR_ERROR_OK; + unsigned char magnitude = index / _ports; + unsigned char i=0; + #if EMON_REPORT_CURRENT + if (magnitude == i++) return MAGNITUDE_CURRENT; + #endif + #if EMON_REPORT_POWER + if (magnitude == i++) return MAGNITUDE_POWER_APPARENT; + #endif + #if EMON_REPORT_ENERGY + if (magnitude == i) return MAGNITUDE_ENERGY; + #endif + } + _error = SENSOR_ERROR_OUT_OF_RANGE; + return MAGNITUDE_NONE; + } + + void pre() { + static unsigned long last = 0; + for (unsigned char port=0; port<_ports; port++) { + unsigned char channel = getChannel(port); + _current[port] = getCurrent(channel); + #if EMON_REPORT_ENERGY + _energy[port] += (_current[port] * _voltage * (millis() - last) / 1000); + #endif + } + last = millis(); + } + + // Current value for slot # index + double value(unsigned char index) { + + if (index < _count) { + + _error = SENSOR_ERROR_OK; + unsigned char port = index % _ports; + unsigned char magnitude = index / _ports; + + unsigned char i=0; + #if EMON_REPORT_CURRENT + if (magnitude == i++) return _current[port]; + #endif + #if EMON_REPORT_POWER + if (magnitude == i++) return _current[port] * _voltage; + #endif + #if EMON_REPORT_ENERGY + if (magnitude == i) return _energy[port]; + #endif + + } + + _error = SENSOR_ERROR_OUT_OF_RANGE; + return 0; + + } + + protected: + + unsigned char getChannel(unsigned char port) { + unsigned char count = 0; + unsigned char bit = 1; + for (unsigned char channel=0; channel> 8; + buffer[2] = config & 0xFF; + brzo_i2c_start_transaction(_address, I2C_SCL_FREQUENCY); + brzo_i2c_write(buffer, 3, false); + brzo_i2c_end_transaction(); + #else + Wire.beginTransmission(_address); + Wire.write((uint8_t) ADS1X15_REG_POINTER_CONFIG); + Wire.write((uint8_t) (config >> 8)); + Wire.write((uint8_t) (config & 0xFF)); + Wire.endTransmission(); + #endif + + } + + double getCurrent(unsigned char channel) { + + // Force stop by setting single mode and back to continuous + static unsigned char previous = 9; + if (previous != channel) { + setConfigRegistry(channel, true, false); + setConfigRegistry(channel, false, false); + setConfigRegistry(channel, false, true); + delay(10); + readADC(channel); + previous = channel; + } + setConfigRegistry(channel, true, true); + + return read(channel, _pivot[channel]); + + } + + unsigned int readADC(unsigned char channel) { + + unsigned int value = 0; + + #if I2C_USE_BRZO + uint8_t buffer[3]; + buffer[0] = ADS1X15_REG_POINTER_CONVERT; + brzo_i2c_start_transaction(_address, I2C_SCL_FREQUENCY); + brzo_i2c_write(buffer, 1, false); + brzo_i2c_read(buffer, 2, false); + brzo_i2c_end_transaction(); + value |= buffer[0] << 8; + value |= buffer[1]; + + #else + Wire.beginTransmission(_address); + Wire.write(ADS1X15_REG_POINTER_CONVERT); + Wire.endTransmission(); + Wire.requestFrom(_address, (unsigned char) 2); + value |= Wire.read() << 8; + value |= Wire.read(); + #endif + + if (!_is_ads1115) value >>= ADS1015_BIT_SHIFT; + + delayMicroseconds(500); + + return value; + + } + + bool _is_ads1115 = true; + unsigned char _address; + unsigned char _mask; + unsigned char _ports; + double _pivot[ADS1X15_CHANNELS] = {0}; + double _current[ADS1X15_CHANNELS] = {0}; + #if EMON_REPORT_ENERGY + unsigned long _energy[ADS1X15_CHANNELS] = {0}; + #endif + + +}; diff --git a/code/espurna/sensors/EmonAnalogSensor.h b/code/espurna/sensors/EmonAnalogSensor.h index 3e6e977a..8e83dee4 100644 --- a/code/espurna/sensors/EmonAnalogSensor.h +++ b/code/espurna/sensors/EmonAnalogSensor.h @@ -16,13 +16,13 @@ class EmonAnalogSensor : public EmonSensor { // Cache _gpio = gpio; - _count = 4; + _count = _magnitudes; // Prepare GPIO pinMode(gpio, INPUT); // warmup - read(_gpio); + read(_gpio, _pivot); } @@ -41,10 +41,16 @@ class EmonAnalogSensor : public EmonSensor { // Type for slot # index magnitude_t type(unsigned char index) { _error = SENSOR_ERROR_OK; - if (index == 0) return MAGNITUDE_CURRENT; - if (index == 1) return MAGNITUDE_POWER_APPARENT; - if (index == 2) return MAGNITUDE_ENERGY; - if (index == 3) return MAGNITUDE_ENERGY_DELTA; + unsigned char i=0; + #if EMON_REPORT_CURRENT + if (index == i++) return MAGNITUDE_CURRENT; + #endif + #if EMON_REPORT_POWER + if (index == i++) return MAGNITUDE_POWER_APPARENT; + #endif + #if EMON_REPORT_ENERGY + if (index == i) return MAGNITUDE_ENERGY; + #endif _error = SENSOR_ERROR_OUT_OF_RANGE; return MAGNITUDE_NONE; } @@ -56,20 +62,25 @@ class EmonAnalogSensor : public EmonSensor { // Cache the value static unsigned long last = 0; - static double current = 0; - static unsigned long energy_delta = 0; - if ((last == 0) || (millis() - last > 1000)) { - current = read(_gpio); - energy_delta = current * _voltage * (millis() - last) / 1000; - _energy += energy_delta; + _current = read(0, _pivot); + #if EMON_REPORT_ENERGY + _energy += (_current * _voltage * (millis() - last) / 1000); + #endif last = millis(); } - if (index == 0) return current; - if (index == 1) return current * _voltage; - if (index == 2) return _energy; - if (index == 3) return energy_delta; + // Report + unsigned char i=0; + #if EMON_REPORT_CURRENT + if (index == i++) return _current; + #endif + #if EMON_REPORT_POWER + if (index == i++) return _current * _voltage; + #endif + #if EMON_REPORT_ENERGY + if (index == i) return _energy; + #endif _error = SENSOR_ERROR_OUT_OF_RANGE; return 0; @@ -79,11 +90,15 @@ class EmonAnalogSensor : public EmonSensor { protected: unsigned int readADC(unsigned char channel) { - return analogRead(channel); + return analogRead(_gpio); } unsigned char _gpio; - unsigned long _energy = 0; + double _pivot = 0; + double _current; + #if EMON_REPORT_ENERGY + unsigned long _energy = 0; + #endif }; diff --git a/code/espurna/sensors/EmonSensor.h b/code/espurna/sensors/EmonSensor.h index 29912b0c..07b5fd48 100644 --- a/code/espurna/sensors/EmonSensor.h +++ b/code/espurna/sensors/EmonSensor.h @@ -18,7 +18,16 @@ class EmonSensor : public BaseSensor { // Cache _voltage = voltage; _adc_counts = 1 << bits; - _pivot = _adc_counts >> 1; + + #if EMON_REPORT_CURRENT + ++_magnitudes; + #endif + #if EMON_REPORT_POWER + ++_magnitudes; + #endif + #if EMON_REPORT_ENERGY + ++_magnitudes; + #endif // Calculate factor _current_factor = ratio * ref / _adc_counts; @@ -52,7 +61,7 @@ class EmonSensor : public BaseSensor { } } - double read(unsigned char channel) { + double read(unsigned char channel, double &pivot) { int sample; int max = 0; @@ -69,8 +78,8 @@ class EmonSensor : public BaseSensor { if (sample < min) min = sample; // Digital low pass filter extracts the VDC offset - _pivot = (_pivot + (sample - _pivot) / EMON_FILTER_SPEED); - filtered = sample - _pivot; + pivot = (pivot + (sample - pivot) / EMON_FILTER_SPEED); + filtered = sample - pivot; // Root-mean-square method sum += (filtered * filtered); @@ -79,8 +88,8 @@ class EmonSensor : public BaseSensor { time_span = millis() - time_span; // Quick fix - if (_pivot < min || max < _pivot) { - _pivot = (max + min) / 2.0; + if (pivot < min || max < pivot) { + pivot = (max + min) / 2.0; } // Calculate current @@ -95,7 +104,7 @@ class EmonSensor : public BaseSensor { Serial.print("[EMON] Sample frequency (Hz): "); Serial.println(1000 * _samples / time_span); Serial.print("[EMON] Max value: "); Serial.println(max); Serial.print("[EMON] Min value: "); Serial.println(min); - Serial.print("[EMON] Midpoint value: "); Serial.println(_pivot); + Serial.print("[EMON] Midpoint value: "); Serial.println(pivot); Serial.print("[EMON] RMS value: "); Serial.println(rms); Serial.print("[EMON] Current: "); Serial.println(current); #endif @@ -111,10 +120,10 @@ class EmonSensor : public BaseSensor { } double _voltage; + unsigned char _magnitudes = 0; unsigned long _adc_counts; unsigned int _multiplier = 1; double _current_factor; - double _pivot; unsigned long _samples = EMON_MAX_SAMPLES; diff --git a/code/platformio.ini b/code/platformio.ini index f9d99e04..1d7b1711 100644 --- a/code/platformio.ini +++ b/code/platformio.ini @@ -21,7 +21,6 @@ lib_deps = NtpClientLib OneWire Brzo I2C - I2Cdevlib-ADS1115 EspSoftwareSerial PMS Library https://bitbucket.org/xoseperez/justwifi.git#1.1.4