diff --git a/code/espurna/sensor.ino b/code/espurna/sensor.ino index 4e7419d6..ddf05867 100644 --- a/code/espurna/sensor.ino +++ b/code/espurna/sensor.ino @@ -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 diff --git a/code/espurna/sensors/EmonADC121Sensor.h b/code/espurna/sensors/EmonADC121Sensor.h index dd1ae1e5..05857136 100644 --- a/code/espurna/sensors/EmonADC121Sensor.h +++ b/code/espurna/sensors/EmonADC121Sensor.h @@ -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 diff --git a/code/espurna/sensors/EmonADS1X15Sensor.h b/code/espurna/sensors/EmonADS1X15Sensor.h index 982a1e95..1ddbd27c 100644 --- a/code/espurna/sensors/EmonADS1X15Sensor.h +++ b/code/espurna/sensors/EmonADS1X15Sensor.h @@ -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 }; diff --git a/code/espurna/sensors/EmonAnalogSensor.h b/code/espurna/sensors/EmonAnalogSensor.h index c6de3357..d3c5ba76 100644 --- a/code/espurna/sensors/EmonAnalogSensor.h +++ b/code/espurna/sensors/EmonAnalogSensor.h @@ -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 - - }; diff --git a/code/espurna/sensors/EmonSensor.h b/code/espurna/sensors/EmonSensor.h index 05ac28af..17d5e1ba 100644 --- a/code/espurna/sensors/EmonSensor.h +++ b/code/espurna/sensors/EmonSensor.h @@ -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