Browse Source

Move multiple channel support in EMON to base class

fastled
Xose Pérez 7 years ago
parent
commit
feeebf9fb3
5 changed files with 123 additions and 80 deletions
  1. +6
    -3
      code/espurna/sensor.ino
  2. +9
    -2
      code/espurna/sensors/EmonADC121Sensor.h
  3. +9
    -7
      code/espurna/sensors/EmonADS1X15Sensor.h
  4. +21
    -24
      code/espurna/sensors/EmonAnalogSensor.h
  5. +78
    -44
      code/espurna/sensors/EmonSensor.h

+ 6
- 3
code/espurna/sensor.ino View File

@ -249,7 +249,7 @@ void _sensorInit() {
sensor->setAddress(EMON_ADC121_I2C_ADDRESS);
sensor->setVoltage(EMON_MAINS_VOLTAGE);
sensor->setReference(EMON_REFERENCE_VOLTAGE);
sensor->setCurrentRatio(EMON_CURRENT_RATIO);
sensor->setCurrentRatio(0, EMON_CURRENT_RATIO);
_sensorRegister(sensor);
}
#endif
@ -262,7 +262,10 @@ void _sensorInit() {
sensor->setMask(EMON_ADS1X15_MASK);
sensor->setGain(EMON_ADS1X15_GAIN);
sensor->setVoltage(EMON_MAINS_VOLTAGE);
sensor->setCurrentRatio(EMON_CURRENT_RATIO);
sensor->setCurrentRatio(0, EMON_CURRENT_RATIO);
sensor->setCurrentRatio(1, EMON_CURRENT_RATIO);
sensor->setCurrentRatio(2, EMON_CURRENT_RATIO);
sensor->setCurrentRatio(3, EMON_CURRENT_RATIO);
_sensorRegister(sensor);
}
#endif
@ -272,7 +275,7 @@ void _sensorInit() {
EmonAnalogSensor * sensor = new EmonAnalogSensor();
sensor->setVoltage(EMON_MAINS_VOLTAGE);
sensor->setReference(EMON_REFERENCE_VOLTAGE);
sensor->setCurrentRatio(EMON_CURRENT_RATIO);
sensor->setCurrentRatio(0, EMON_CURRENT_RATIO);
_sensorRegister(sensor);
}
#endif


+ 9
- 2
code/espurna/sensors/EmonADC121Sensor.h View File

@ -27,6 +27,7 @@
#define ADC121_REG_CONVH 0x07
#define ADC121_RESOLUTION 12
#define ADC121_CHANNELS 1
class EmonADC121Sensor : public EmonAnalogSensor {
@ -36,6 +37,11 @@ class EmonADC121Sensor : public EmonAnalogSensor {
// Public
// ---------------------------------------------------------------------
EmonADC121Sensor(): EmonAnalogSensor() {
_channels = ADC121_CHANNELS;
init();
}
void setAddress(unsigned char address) {
if (_address != address) _dirty = true;
_address = address;
@ -86,8 +92,7 @@ class EmonADC121Sensor : public EmonAnalogSensor {
EmonSensor::begin();
// warmup channel 0 (the only one)
_pivot = _adc_counts >> 1;
read(0, _pivot);
read(0);
}
@ -118,6 +123,8 @@ class EmonADC121Sensor : public EmonAnalogSensor {
unsigned int readADC(unsigned char channel) {
(void) channel;
unsigned int value;
#if I2C_USE_BRZO


+ 9
- 7
code/espurna/sensors/EmonADS1X15Sensor.h View File

@ -105,6 +105,11 @@ class EmonADS1X15Sensor : public EmonSensor {
// Public
// ---------------------------------------------------------------------
EmonADS1X15Sensor(): EmonSensor() {
_channels = ADS1X15_CHANNELS;
init();
}
void setAddress(unsigned char address) {
if (_address != address) _dirty = true;
_address = address;
@ -299,7 +304,7 @@ class EmonADS1X15Sensor : public EmonSensor {
config |= ((channel + 4) << 12); // Set single-ended input channel (0x4000 - 0x7000)
#if SENSOR_DEBUG
Serial.printf("[EMON] ADS1X115 Config Registry: %04X\n", config);
//Serial.printf("[EMON] ADS1X115 Config Registry: %04X\n", config);
#endif
// Write config register to the ADC
@ -335,12 +340,14 @@ class EmonADS1X15Sensor : public EmonSensor {
}
setConfigRegistry(channel, true, true);
return read(channel, _pivot[channel]);
return read(channel);
}
unsigned int readADC(unsigned char channel) {
(void) channel;
unsigned int value = 0;
#if I2C_USE_BRZO
@ -375,11 +382,6 @@ class EmonADS1X15Sensor : public EmonSensor {
unsigned char _mask = 0x0F;
unsigned int _gain = ADS1X15_REG_CONFIG_PGA_4_096V;
unsigned char _ports;
double _pivot[ADS1X15_CHANNELS] = {0};
double _current[ADS1X15_CHANNELS] = {0};
#if EMON_REPORT_ENERGY
unsigned long _energy[ADS1X15_CHANNELS] = {0};
#endif
};

+ 21
- 24
code/espurna/sensors/EmonAnalogSensor.h View File

@ -9,7 +9,8 @@
#include "BaseSensor.h"
#include "EmonSensor.h"
#define INTERNAL_ADC_RESOLUTION 10
#define EMON_ANALOG_RESOLUTION 10
#define EMON_ANALOG_CHANNELS 1
class EmonAnalogSensor : public EmonSensor {
@ -19,6 +20,11 @@ class EmonAnalogSensor : public EmonSensor {
// Public
// ---------------------------------------------------------------------
EmonAnalogSensor(): EmonSensor() {
_channels = EMON_ANALOG_CHANNELS;
init();
}
// ---------------------------------------------------------------------
// Sensor API
// ---------------------------------------------------------------------
@ -28,30 +34,27 @@ class EmonAnalogSensor : public EmonSensor {
if (!_dirty) return;
_dirty = false;
// Just one channel
// Number of slots
_count = _magnitudes;
// Bit depth
_resolution = INTERNAL_ADC_RESOLUTION;
_resolution = EMON_ANALOG_RESOLUTION;
// Init analog PIN)
pinMode(_gpio, INPUT);
pinMode(0, INPUT);
// Call the parent class method
EmonSensor::begin();
// warmup channel 0 (the only one)
_pivot = _adc_counts >> 1;
read(_gpio, _pivot);
read(0);
}
// Descriptive name of the sensor
String name() {
char buffer[20];
snprintf(buffer, sizeof(buffer), "EMON @ GPIO%d", _gpio);
return String(buffer);
return String("EMON @ ANALOG @ GPIO0");
}
// Descriptive name of the slot # index
@ -79,12 +82,12 @@ class EmonAnalogSensor : public EmonSensor {
// Pre-read hook (usually to populate registers with up-to-date data)
void pre() {
_current = read(0, _pivot);
_current[0] = read(0);
#if EMON_REPORT_ENERGY
static unsigned long last = 0;
if (last > 0) {
_energy += (_current * _voltage * (millis() - last) / 1000);
_energy[0] += (_current[0] * _voltage * (millis() - last) / 1000);
}
last = millis();
#endif
@ -95,16 +98,17 @@ class EmonAnalogSensor : public EmonSensor {
double value(unsigned char index) {
_error = SENSOR_ERROR_OK;
unsigned char channel = index / _magnitudes;
unsigned char i=0;
#if EMON_REPORT_CURRENT
if (index == i++) return _current;
if (index == i++) return _current[channel];
#endif
#if EMON_REPORT_POWER
if (index == i++) return _current * _voltage;
if (index == i++) return _current[channel] * _voltage;
#endif
#if EMON_REPORT_ENERGY
if (index == i) return _energy;
if (index == i) return _energy[channel];
#endif
_error = SENSOR_ERROR_OUT_OF_RANGE;
@ -115,15 +119,8 @@ class EmonAnalogSensor : public EmonSensor {
protected:
unsigned int readADC(unsigned char channel) {
return analogRead(_gpio);
(void) channel;
return analogRead(0);
}
unsigned char _gpio = 0;
double _pivot = 0;
double _current;
#if EMON_REPORT_ENERGY
unsigned long _energy = 0;
#endif
};

+ 78
- 44
code/espurna/sensors/EmonSensor.h View File

@ -17,6 +17,8 @@ class EmonSensor : public BaseSensor {
// ---------------------------------------------------------------------
EmonSensor(): BaseSensor() {
// Calculate # of magnitudes
#if EMON_REPORT_CURRENT
++_magnitudes;
#endif
@ -26,6 +28,7 @@ class EmonSensor : public BaseSensor {
#if EMON_REPORT_ENERGY
++_magnitudes;
#endif
}
void setVoltage(double voltage) {
@ -38,9 +41,10 @@ class EmonSensor : public BaseSensor {
_reference = reference;
}
void setCurrentRatio(double current_ratio) {
if (_current_ratio != current_ratio) _dirty = true;
_current_ratio = current_ratio;
void setCurrentRatio(unsigned char channel, double current_ratio) {
if (channel >= _channels) return;
if (_current_ratio[channel] != current_ratio) _dirty = true;
_current_ratio[channel] = current_ratio;
}
// ---------------------------------------------------------------------
@ -52,26 +56,22 @@ class EmonSensor : public BaseSensor {
// Resolution
_adc_counts = 1 << _resolution;
// Calculate factor
_current_factor = _current_ratio * _reference / _adc_counts;
// Calculate multiplier
unsigned int s = 1;
unsigned int i = 1;
unsigned int m = s * i;
while (m * _current_factor < 1) {
_multiplier = m;
i = (i == 1) ? 2 : (i == 2) ? 5 : 1;
if (i == 1) s *= 10;
m = s * i;
// Calculations
for (unsigned char i=0; i<_channels; i++) {
_energy[i] = _current[i] = 0;
_pivot[i] = _adc_counts >> 1;
_current_factor[i] = _current_ratio[i] * _reference / _adc_counts;
_multiplier[i] = calculateMultiplier(_current_factor[i]);
}
#if SENSOR_DEBUG
Serial.print("[EMON] Current ratio: "); Serial.println(_current_ratio);
Serial.print("[EMON] Ref. Voltage: "); Serial.println(_reference);
Serial.print("[EMON] ADC Counts: "); Serial.println(_adc_counts);
Serial.print("[EMON] Current factor: "); Serial.println(_current_factor);
Serial.print("[EMON] Multiplier: "); Serial.println(_multiplier);
DEBUG_MSG("[EMON] Reference (mV): %d\n", int(1000 * _reference));
DEBUG_MSG("[EMON] ADC counts: %d\n", _adc_counts);
for (unsigned char i=0; i<_channels; i++) {
DEBUG_MSG("[EMON] Channel #%d current ratio (mA/V): %d\n", i, int(1000 * _current_ratio[i]));
DEBUG_MSG("[EMON] Channel #%d current factor (mA/bit): %d\n", i, int(1000 * _current_factor[i]));
DEBUG_MSG("[EMON] Channel #%d Multiplier: %d\n", i, int(_multiplier[i]));
}
#endif
}
@ -82,9 +82,35 @@ class EmonSensor : public BaseSensor {
// Protected
// ---------------------------------------------------------------------
// Initializes internal variables
void init() {
_current_ratio = new double[_channels];
_current_factor = new double[_channels];
_multiplier = new uint16_t[_channels];
_pivot = new double[_channels];
_current = new double[_channels];
#if EMON_REPORT_ENERGY
_energy = new uint32_t[_channels];
#endif
}
virtual unsigned int readADC(unsigned char channel) {}
double read(unsigned char channel, double &pivot) {
unsigned int calculateMultiplier(double current_factor) {
unsigned int s = 1;
unsigned int i = 1;
unsigned int m = s * i;
unsigned int multiplier;
while (m * current_factor < 1) {
multiplier = m;
i = (i == 1) ? 2 : (i == 2) ? 5 : 1;
if (i == 1) s *= 10;
m = s * i;
}
return multiplier;
}
double read(unsigned char channel) {
int sample;
int max = 0;
@ -101,8 +127,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[channel] = (_pivot[channel] + (sample - _pivot[channel]) / EMON_FILTER_SPEED);
filtered = sample - _pivot[channel];
// Root-mean-square method
sum += (filtered * filtered);
@ -111,25 +137,26 @@ class EmonSensor : public BaseSensor {
time_span = millis() - time_span;
// Quick fix
if (pivot < min || max < pivot) {
pivot = (max + min) / 2.0;
if (_pivot[channel] < min || max < _pivot[channel]) {
_pivot[channel] = (max + min) / 2.0;
}
// Calculate current
double rms = _samples > 0 ? sqrt(sum / _samples) : 0;
double current = _current_factor * rms;
current = (double) (int(current * _multiplier) - 1) / _multiplier;
double current = _current_factor[channel] * rms;
current = (double) (int(current * _multiplier[channel]) - 1) / _multiplier[channel];
if (current < 0) current = 0;
#if SENSOR_DEBUG
Serial.print("[EMON] Total samples: "); Serial.println(_samples);
Serial.print("[EMON] Total time (ms): "); Serial.println(time_span);
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] RMS value: "); Serial.println(rms);
Serial.print("[EMON] Current: "); Serial.println(current);
DEBUG_MSG("[EMON] Channel: %d\n", channel);
DEBUG_MSG("[EMON] Total samples: %d\n", _samples);
DEBUG_MSG("[EMON] Total time (ms): %d\n", time_span);
DEBUG_MSG("[EMON] Sample frequency (Hz): %d\n", int(1000 * _samples / time_span));
DEBUG_MSG("[EMON] Max value: %d\n", max);
DEBUG_MSG("[EMON] Min value: %d\n", min);
DEBUG_MSG("[EMON] Midpoint value: %d\n", int(_pivot[channel]));
DEBUG_MSG("[EMON] RMS value: %d\n", int(rms));
DEBUG_MSG("[EMON] Current (mA): %d\n", int(current));
#endif
// Check timing
@ -142,18 +169,25 @@ class EmonSensor : public BaseSensor {
}
unsigned char _magnitudes = 0;
unsigned long _samples = EMON_MAX_SAMPLES;
unsigned char _channels = 0; // Number of ADC channels available
unsigned char _magnitudes = 0; // Number of magnitudes per channel
unsigned long _samples = EMON_MAX_SAMPLES; // Samples (dynamically modificable)
unsigned char _resolution = 10; // ADC resolution in bits
unsigned long _adc_counts; // Max count
unsigned int _multiplier = 1;
unsigned char _resolution = 10;
double _voltage = EMON_MAINS_VOLTAGE; // Mains voltage
double _reference = EMON_REFERENCE_VOLTAGE; // ADC reference voltage (100%)
double _voltage = EMON_MAINS_VOLTAGE;
double _reference = EMON_REFERENCE_VOLTAGE;
double _current_ratio = EMON_CURRENT_RATIO;
double * _current_ratio; // Ratio ampers in main loop to voltage in secondary (per channel)
double * _current_factor; // Calculated, reads (RMS) to current (per channel)
uint16_t * _multiplier; // Calculated, error (per channel)
unsigned long _adc_counts;
double _current_factor;
double * _pivot; // Moving average mid point (per channel)
double * _current; // Last current reading (per channel)
#if EMON_REPORT_ENERGY
uint32_t * _energy; // Aggregated energy (per channel)
#endif


Loading…
Cancel
Save