Suggestions on how to proceed with new EMON homestead installation using ESP32

I am looking here: https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf
On page 22, Table 7 tells me that there are 4 attenuator settings which effectively give you four measurement ranges, the usable voltages being 100 - 950 mV, 100–1,250 mV, 150–1,750 mV and 150–2,450 mV.

Using your ZMPT101B with its 150 Ω burden, you are getting 288 mV rms, giving you a peak to peak voltage of about 815 mV. This entire voltage swing must lie between the voltage limits in Table 7. I would choose atten = 1 which gives you plenty of headroom (atten = 0 gives only 4% which is not enough, in my opinion) and then you need to calculate resistor values for R1 & R2 in the diagram in ‘Learn’ to give a voltage at their junction of 675 mV, which is mid-way between 100 mV and 1250 mV. This 675 mV is your mid-point bias voltage. If the input voltage at the ADC input goes outside this range, it will start to distort and eventually be limited (“clipped”) to a constant number out of the ADC irrespective of the actual voltage at the input.

If R1 connects to 3.3 V and R2 to GND, I’d suggest values of 4700 Ω for R1 and 1200 Ω for R2.

No. It depends on three quantities - the phase error of your voltage transformer (we know this is small now), the phase error of your current transformer and the time difference between reading current and voltage samples (both the last two are unknown to me). So you will have to determine it experimentally. You might find swapping the order of reading in emonLib helps - ideally, the number should be between 0 and 1, but changing emonLib would give us a backwards-compatibility nightmare, though it’s fine for you to change your personal version.

Don’t do this until you are reasonably certain that everything is working correctly, and you’ve calibrated it to your best ability. You don’t need to go inside your panel for a high current, because your c.t. responds to ampere turns - turns being the number of times the wire passes through the c.t. I can create a 100 A reading with a 100 A c.t. with 5 A flowing in a wire wound into a 20 turns coil, you can quite easily do the same - on 120 V, a 800 W toaster will draw 6.67 A, so 10 turns will give you 66.7 A, which sounds a good number to calibrate at for a 200 A service.

Mr. Wall,
Referencing the two images I provided in this post I need to get a clarification. R1 & R2 you are discussing is in regard to the current transformer input shown in the Arduino Learn drawing? I did not create my current board off the Learn drawing setup. Are you recommending I change the existing configuration referenced from the ESP32-Emon Current:Voltage Schematic? If so, the current board is going to need a overhaul. Lol…good thing I have lots of solder.

Learn: ct-sensors-circuit1

Is the lower diagram what you have? If it is, this is the first time you have mentioned anything about the circuit you are using. I have had absolutely no idea until now what your input conditioning circuit had been, nor what inputs you have been using on the ESP32.

Further reading (ADC - - — Arduino ESP32 latest documentation) suggests that the input voltage range is also different for various variants of ESP32, so you need to establish what is the usable voltage range of the ADC in the ESP32 you are using. I can’t see one that has a centre voltage of 1.65 V, which is what the “ESP32 Energy Monitor” circuit implies. In any case, the arrangement of this is deprecated in favour of the emonTx4/emonPi2 arrangement from ‘Learn’ which will be the same for all your inputs, both voltage (using the ZMPT101B) and current using whichever c.t. you have obtained (and you’re unlikely to be able to use a SCT-013 on your service entrance wires - they won’t be big enough if the wires are aluminium).

Yes, the lower diagram is what I referenced for the current input. We only discussed the voltage board and I never suspicioned Dan Peig’s current board setup.
One thing to keep in mind is that the Arduino Learn drawing is referencing a 5vDC supply to the circuit while the Peig’s drawing is working off 3.2vDC. I do have a 5vDC pin on the ESP32 that produces about 4.6 volts.

I need to verify that certainly.

So is the Peig’s current board schematic I used workable or do you suggest I go ahead and change it? The components needed I may not have on hand so I would see an ordering delay to get the right value resistors if that is the case.

The SCT-013 just barely fits over my service feed wires. I knew it was gonna be tight when I ordered them because I put a dial caliper on them.

I know, I drew it. I have done my calculations for a 3.3 V supply. The source I used made no mention of the availability of 5 V. Which to use would depend on which is the cleaner, because any digital noise making its way into the the input via the bias circuitry is trouble.

It is workable but not ideal. Ever since I joined up with OEM, I’ve never understood why the non-grounded input configuration (I’ll call it the “V2” configuration because it was used in the emonTx V2) was used rather than the much more common grounded input arrangement we now use in the emonTx4. It was only recently that a contributor with deep knowledge of the design of switched-mode power supplies provided an explanation of the increased noise, which manifests itself as a standing current, that the V2 configuration is susceptible to.

The actual values of the resistors aren’t critical, it’s the ratio which is important so that the mid-point voltage is as close as reasonably possible to the centre of the ADC’s input range - which you’ll find out when you know the variant you have.

You must ensure there’s no pressure on the ferrite core of the c.t. It’s a very brittle material and it is likely to snap, and totally destroy itself, if there’s any.

The botttom most pin on the left side next to the reset button and usb port is the 5v pin. I have always used these mocrocontrollers with 5 volt wall warts so I am unsure if the internals are just routing the usb port voltage to the 5v pin or if it is being sourced from a 3.3v to 5v internal step up. You drew it! Well that’s embarrassing for me having tried to tell you about it. haha

I did find the spec on my ESP. It is an ESP-32S. [NodeMCU/WROOM-32 chip] at the Espressif site:

ESP-32S
page 58 Section 4-5 Table 4-6 has the ADC calibration results.

Attenuation value 1 seems to be the best choice through my amateur eyes.
[the copy/pasting of this table data is somewhat convoluted]

I haven’t decided what to do on whether to modify the current board componentry and use your calculations. I probably will. It is right here in my computer room now and I have a variety of resistors I could use that would be close. And you explained the ratio is what really matters.

A most excellent suggestion I must try.

I truly appreciate your ongoing support as I iron out the remaining issues related to this project. Thank you.

Edit - Inserted image of table taken from Espressif webdoc - BT Moderator

I would put my money on the first option, with the 3.3 V being derived from the 5 V.

Looking at my post no.21, you have 815 mV swing on your (just less than) nominal voltage. Atten 1 will give you more than plenty of headroom (35% - 10% should have been enough - 132 V) but 850 mV with 4% is definitely not enough. You must do the resistor calculations again. IF you believe the ADC is linear down to zero, then you can calculate the resistors to give a mid-point voltage of 550 mV, and you bottom ‘peak’ voltage will be 143 mV - still clear of the non-linearity below 100 mV I read about.
Now you can allow the “midpoint” to be 30 - 40 mV either side of the nominal value (this uses up headroom on either the positive peaks or the negative, which you can afford to lose), so you can play with the formula for the resistor values until you find (enough pairs of) resistors. I’d aim for a current of between 100 µA and 500 µA in the resistors, so R2 will be roughly 1 kΩ - 5.6 kΩ, and R1 will be roughly 5.6 kΩ - 27 kΩ. The lower values will attenuate the signal a little.

10-4…

I just discovered a software issue that has to be resolved. In displaying the Emon calculation values on a webpage or at the Serial Monitor an odd thing happens whenever I insert the code that handles the Wifi server/client routine. For some reason the voltage values are reading 0. The current values are unaffected. Remove the wifi code (wifi.h) and the display goes back to normal. ???
I noticed when first observing the Serial Monitor output that the first cycle will show a high voltage value (ex: 453.63) but then it goes to 0 and remains there. Typically when the processor first starts up the Emon displayed readings are high but then they stabilize and go to expected values in short order.
The only thing I have done is change the ADC count to 12 bits from 10 bits to take advantage of the ESPs larger register capbility. This was a modification I obtained from GitHub:
Savjee ADC mod
If that was the issue you would think it would affect the current reading as well. I do not see a relationship between the ADC registers and the wifi host/client code.
At any rate I will need to seek input from the Espressif and Arduino forums to try and get a handle on why this is happening. Without back luck I wouldn’t have any luck at all.
Update EDIT-
Here is the Serial Monitor output upon initialization:

OK, Got the time from NTP
Setting Timezone to CST6CDT,M3.2.0,M11.1.0
WiFi Status: 3
ESP Board MAC Address: C0:49:EF:D1:01:E4

Phase 1 Voltage: 210.95 Phase 1 Current: 33.74
Phase 2 Voltage: 201.46 Phase 2 Current: 71.78
Phase 1 Voltage: 0.00 Phase 1 Current: 1.72
Phase 2 Voltage: 0.00 Phase 2 Current: 2.70
Phase 1 Voltage: 0.00 Phase 1 Current: 1.76
Phase 2 Voltage: 0.00 Phase 2 Current: 2.78
Friday, July 26 2024 10:11:02 zone CDT -0500

Phase 1 Voltage: 0.00 Phase 1 Current: 1.74
Phase 2 Voltage: 0.00 Phase 2 Current: 2.91
Phase 1 Voltage: 0.00 Phase 1 Current: 1.76
Phase 2 Voltage: 0.00 Phase 2 Current: 2.83

Just for giggles I did go in and change the ADC count back to ‘10’ from ‘12’ in Emon.h just to
remove any lingering suspicions. It had no effect on the issue.

This is exactly the same mechanism that is leading to the false spikes in the current - emonLib is establishing the number it needs to subtract to correctly remove the bias offset. It is preset, assuming the “mid-point” count is exactly half the ADC resolution, with
offsetV = ADC_COUNTS>>1;
You can change this to the actual quiescent count (no voltage, no current) if it’s a long way out by changing those two methods and passing in the appropriate value for each input.

Another memory problem - in this case between the Wi-Fi code and yours.

No need, it’s built-in. Look at the file emonLIb.h

If you are seeing a turn-on spike in the voltage, then it decays to zero, are you looking at the correct input pin? Can you “print” the rms voltage emon1.Vrms - or is this what “Phase 1” is?

[BTW, my understanding is you have a single phase supply, split into two legs, L1 & L2]

Yes, ‘Phase 1’ is emon1.Vrms. You are correct about my service being single phase or ‘split-phase’ as we sometimes refer to it here.

You think the ESP might be overtaxed on memory when adding the WiFi code? The program uses about 72% of flash memory IIRC. I suppose I could go in and comment out
the Emon2 instance which should theoretically free up memory space and then see if there is any change.

Not sure I am following you. Where do I reference this ‘actual quiescent count?’

True, but how much? it’s only the data for the instance that is repeated for each instance, the methods (the code itself) exists once only. By my reckoning, 13 doubles and 8 integers - 68 bytes, plus those in your software that accept and manipulate the data.

You’d need to write a special bit of software to read the raw number coming from the ADC (and hope it didn’t drift with temperature or time). It’s the result that appears in sample... when there’s no input - or the long-term average value when there is

    sampleV = analogRead(inPinV);            //Read in raw voltage signal
    sampleI = analogRead(inPinI);            //Read in raw current signal

and it’s the digital representation of the input bias voltage, so it’s subtracted to remove the effect of adding the bias voltage to the input signal in the hardware. Normally, it doesn’t pose a problem because it only happens a power-up, thereafter emonLib and the software controlling it and accepting the data from it is expected to run continuously.

Turns out the correct pin assignments are crucial! Evidently the WiFi section of the ESP uses one of the two ‘ADC peripherals’ to operate; not sure what a peripheral is. I switched over to the other side of the ESP and used a new set of voltage pins and the Voltage values no longer disappear.

I wanted to ask you if you have any exposure to Emon running on these little microcontrollers? I am getting around 4 seconds of execution time for each Emon instance. That is an incredibly long amount of time for code execution. If that is normal then it is what it is. I believe you can shorten/lengthen the number of cycles it looks at the inputs but that can possibly sacrifice accuracy I would think.

You only need to look at the second page of the data sheet:
image

Do you mean how long from calling one of the methods (calcIrms( ) or calcVI( )) to the method returning a result? That depends on what you ask it to do and how long it takes for the ADC to get a sample. The Atmel '328P is particularly good for its class in this respect at 104 µs (when free running). My measurements for the sampling rate of emonLib, measured with a standard emonTx V3.4 (and the emonPi is the same processor) are:
  calcIrms( ) - approx 5588 current samples per second.
  calcVI( ) - approx 2535 pairs of voltage & current samples per second.
(see Sampling rate of emonLib)
The data sheet (again :grinning: ) says
image
What I don’t know is what 3rd party libraries in the ESP might be doing in between emonLib and the ADC itself, so the sample rate you get will most likely be significantly less than this. This is certainly the case with the Arduino libraries as used in emonLib, as you can see from the numbers above. There is a faster way - the way we do it in emonLibCM and emonLibDB, where the ADC free runs and dumps the values into a memory location, then generates an interrupt to signal the main software loop to take the value and process it. Before you ask about converting either to run in the ESP32, it appears to be possible from what I read here esp32_technical_reference_manual_en.pdf, but I’m not going there.

Most people who use the ESP8266 and the ESP32 have a separate ADC and use the ESP to do the maths and send the results out - usually by Wi-Fi.

You are a repository of useful information Mr. Wall! And you are making me realize the wisdom in studying processor datasheets. :+1:

Yes.
Do you think shortening the number of cycles it polls is wise? The execution times currently are not necessarily an issue because Emon is the only task the ESP is dedicated to.

Are you referring to the separate ADC boards you can interface to the ESPs?

Whether it has been a second proprietary board, or the users’ own pcb, I don’t know.

As for data sheets, they should always, but always, be your first point of reference for any device. After all, the manufacturer should be the most reliable and accurate source of information. Unfortunately, I’m afraid Espressif don’t shine very brightly in this department, I’ve never found it easy to get a significant amount of information of substance from their data sheets.

There might be a cultural/political element at play. The CCP has never encouraged the unfettered dissemination of information to the western world. They definitely have a keen interest in ours. …lol…

I am in the process of assemblying a test coil like you suggested to bump the current input values up considerably.
Concerning:

emon1.calcVI (crossings, timeout)  // 120, 2000

Do you have any suggestions on adjusting these values?

I’m afraid not. My concern from what you wrote about the sampling time is that the sample rate is very slow indeed and emonLib is not able to work as it does with the Atmel '328P processor. If you are getting what I’d class as a ‘realistic’ sample rate of around 10 sample pairs per cycle (i.e. about 1200 samples per second), then 200 - 300 ms worth of samples should be enough.

You can find out reasonably easily how many sample pairs you get: temporarily edit your copy of emonLib to, at the end of the loop gathering the samples, get and save the time (millis() and report the difference between this and start - the time it recorded just before it started the loop; and the number of sample pairs numberOfSamples ). Do this for two different crossings (say 100 and 1100, you might need to change timeout too) and take the difference to eliminate the inevitable overheads. I’ll be very interested in the value you get.

Mr. Wall,

Hmmm… getting this data is proving a little more tricky than I thought it was going to be. I am running two instances of Emon.
I was going to check the execution time simply by timestamping Emon1 when it is called in the main sketch and then get the other timestamp when it exits Emon1.
The issue is with Emon.h’s ‘numberOfSamples’ which I think is what we want to print out to see how many samples were taken. It is not a global variable. So if I try to print it out through the main sketch I get the “not declared” error.
What is the recommended approach to retrieve this value? Do I print it out under the main sketch? This would seem necessary to my way of thinking.
There is this in the EmonLib.cpp code:

void EnergyMonitor::serialprint()

But was not sure how to call it from main code block.

From Emon.h

unsigned int crossCount = 0;       //Used to measure number of times threshold is crossed.
 unsigned int numberOfSamples = 0;  //This is now incremented

Emon.h code:


```cpp
#include "EmonLib.h"

#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif

//--------------------------------------------------------------------------------------
// Sets the pins to be used for voltage and current sensors
//--------------------------------------------------------------------------------------
void EnergyMonitor::voltage(unsigned int _inPinV, double _VCAL, double _PHASECAL) {
  inPinV = _inPinV;
  VCAL = _VCAL;
  PHASECAL = _PHASECAL;
  offsetV = ADC_COUNTS >> 1;
}

void EnergyMonitor::current(unsigned int _inPinI, double _ICAL) {
  inPinI = _inPinI;
  ICAL = _ICAL;
  offsetI = ADC_COUNTS >> 1;
}

//--------------------------------------------------------------------------------------
// Sets the pins to be used for voltage and current sensors based on emontx pin map
//--------------------------------------------------------------------------------------
void EnergyMonitor::voltageTX(double _VCAL, double _PHASECAL) {
  inPinV = 2;
  VCAL = _VCAL;
  PHASECAL = _PHASECAL;
  offsetV = ADC_COUNTS >> 1;
}

void EnergyMonitor::currentTX(unsigned int _channel, double _ICAL) {
  if (_channel == 1) inPinI = 3;
  if (_channel == 2) inPinI = 0;
  if (_channel == 3) inPinI = 1;
  ICAL = _ICAL;
  offsetI = ADC_COUNTS >> 1;
}

//--------------------------------------------------------------------------------------
// emon_calc procedure
// Calculates realPower,apparentPower,powerFactor,Vrms,Irms,kWh increment
// From a sample window of the mains AC voltage and current.
// The Sample window length is defined by the number of half wavelengths or crossings we choose to measure.
//--------------------------------------------------------------------------------------
void EnergyMonitor::calcVI(unsigned int crossings, unsigned int timeout) {
#if defined emonTxV3
  int SupplyVoltage = 3300;
#else
  int SupplyVoltage = readVcc();
#endif

  unsigned int crossCount = 0;       //Used to measure number of times threshold is crossed.
  unsigned int numberOfSamples = 0;  //This is now incremented

  //-------------------------------------------------------------------------------------------------------------------------
  // 1) Waits for the waveform to be close to 'zero' (mid-scale adc) part in sin curve.
  //-------------------------------------------------------------------------------------------------------------------------
  unsigned long start = millis();  //millis()-start makes sure it doesnt get stuck in the loop if there is an error.

  while (1)  //the while loop...
  {
    startV = analogRead(inPinV);                                                  //using the voltage waveform
    if ((startV < (ADC_COUNTS * 0.55)) && (startV > (ADC_COUNTS * 0.45))) break;  //check its within range
    if ((millis() - start) > timeout) break;
  }

  //-------------------------------------------------------------------------------------------------------------------------
  // 2) Main measurement loop
  //-------------------------------------------------------------------------------------------------------------------------
  start = millis();

  while ((crossCount < crossings) && ((millis() - start) < timeout)) {
    numberOfSamples++;          //Count number of times looped.
    lastFilteredV = filteredV;  //Used for delay/phase compensation

    //-----------------------------------------------------------------------------
    // A) Read in raw voltage and current samples
    //-----------------------------------------------------------------------------
    sampleV = analogRead(inPinV);  //Read in raw voltage signal
    sampleI = analogRead(inPinI);  //Read in raw current signal

    //-----------------------------------------------------------------------------
    // B) Apply digital low pass filters to extract the 2.5 V or 1.65 V dc offset,
    //     then subtract this - signal is now centred on 0 counts.
    //-----------------------------------------------------------------------------
    offsetV = offsetV + ((sampleV - offsetV) / ADC_COUNTS);
    filteredV = sampleV - offsetV;
    offsetI = offsetI + ((sampleI - offsetI) / ADC_COUNTS);
    filteredI = sampleI - offsetI;

    //-----------------------------------------------------------------------------
    // C) Root-mean-square method voltage
    //-----------------------------------------------------------------------------
    sqV = filteredV * filteredV;  //1) square voltage values
    sumV += sqV;                  //2) sum

    //-----------------------------------------------------------------------------
    // D) Root-mean-square method current
    //-----------------------------------------------------------------------------
    sqI = filteredI * filteredI;  //1) square current values
    sumI += sqI;                  //2) sum

    //-----------------------------------------------------------------------------
    // E) Phase calibration
    //-----------------------------------------------------------------------------
    phaseShiftedV = lastFilteredV + PHASECAL * (filteredV - lastFilteredV);

    //-----------------------------------------------------------------------------
    // F) Instantaneous power calc
    //-----------------------------------------------------------------------------
    instP = phaseShiftedV * filteredI;  //Instantaneous Power
    sumP += instP;                      //Sum

    //-----------------------------------------------------------------------------
    // G) Find the number of times the voltage has crossed the initial voltage
    //    - every 2 crosses we will have sampled 1 wavelength
    //    - so this method allows us to sample an integer number of half wavelengths which increases accuracy
    //-----------------------------------------------------------------------------
    lastVCross = checkVCross;
    if (sampleV > startV) checkVCross = true;
    else checkVCross = false;
    if (numberOfSamples == 1) lastVCross = checkVCross;

    if (lastVCross != checkVCross) crossCount++;
  }

  //-------------------------------------------------------------------------------------------------------------------------
  // 3) Post loop calculations
  //-------------------------------------------------------------------------------------------------------------------------
  //Calculation of the root of the mean of the voltage and current squared (rms)
  //Calibration coefficients applied.

  double V_RATIO = VCAL * ((SupplyVoltage / 1000.0) / (ADC_COUNTS));
  Vrms = V_RATIO * sqrt(sumV / numberOfSamples);

  double I_RATIO = ICAL * ((SupplyVoltage / 1000.0) / (ADC_COUNTS));
  Irms = I_RATIO * sqrt(sumI / numberOfSamples);

  //Calculation power values
  realPower = V_RATIO * I_RATIO * sumP / numberOfSamples;
  apparentPower = Vrms * Irms;
  powerFactor = realPower / apparentPower;

  //Reset accumulators
  sumV = 0;
  sumI = 0;
  sumP = 0;
  //--------------------------------------------------------------------------------------
}

//--------------------------------------------------------------------------------------
double EnergyMonitor::calcIrms(unsigned int Number_of_Samples) {

#if defined emonTxV3
  int SupplyVoltage = 3300;
#else
  int SupplyVoltage = readVcc();
#endif


  for (unsigned int n = 0; n < Number_of_Samples; n++) {
    sampleI = analogRead(inPinI);

    // Digital low pass filter extracts the 2.5 V or 1.65 V dc offset,
    //  then subtract this - signal is now centered on 0 counts.
    offsetI = (offsetI + (sampleI - offsetI) / ADC_COUNTS);
    filteredI = sampleI - offsetI;

    // Root-mean-square method current
    // 1) square current values
    sqI = filteredI * filteredI;
    // 2) sum
    sumI += sqI;
  }

  double I_RATIO = ICAL * ((SupplyVoltage / 1000.0) / (ADC_COUNTS));
  Irms = I_RATIO * sqrt(sumI / Number_of_Samples);

  //Reset accumulators
  sumI = 0;
  //--------------------------------------------------------------------------------------

  return Irms;
}

void EnergyMonitor::serialprint() {
 
  Serial.print(realPower);
  Serial.print(' ');
  Serial.print(apparentPower);
  Serial.print(' ');
  Serial.print(Vrms);
  Serial.print(' ');
  Serial.print(Irms);
  Serial.print(' ');
  Serial.print(powerFactor);
  Serial.println(' ');
  delay(100);
}

//thanks to http://hacking.majenko.co.uk/making-accurate-adc-readings-on-arduino
//and Jérôme who alerted us to http://provideyourown.com/2012/secret-arduino-voltmeter-measure-battery-voltage/

long EnergyMonitor::readVcc() {
  long result;

  //not used on emonTx V3 - as Vcc is always 3.3V - eliminates bandgap error and need for calibration http://harizanov.com/2013/09/thoughts-on-avr-adc-accuracy/

#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__)
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#elif defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__)
  ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#elif defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_AT90USB1286__)
  ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  ADCSRB &= ~_BV(MUX5);  // Without this the function always returns -1 on the ATmega2560 http://openenergymonitor.org/emon/node/2253#comment-11432
#elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
  ADMUX = _BV(MUX5) | _BV(MUX0);
#elif defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
  ADMUX = _BV(MUX3) | _BV(MUX2);

#endif


#if defined(__AVR__)
  delay(2);             // Wait for Vref to settle
  ADCSRA |= _BV(ADSC);  // Convert
  while (bit_is_set(ADCSRA, ADSC))
    ;
  result = ADCL;
  result |= ADCH << 8;
  result = READVCC_CALIBRATION_CONST / result;  //1100mV*1024 ADC steps http://openenergymonitor.org/emon/node/1186
  return result;
#elif defined(__arm__)
  return (3300);  //Arduino Due
#else
  return (3300);         //Guess that other un-supported architectures will be running a 3.3V!
#endif
}

EmonLib.cpp code:
/*
  Emon.cpp - Library for openenergymonitor
  Created by Trystan Lea, April 27 2010
  GNU GPL
  modified to use up to 12 bits ADC resolution (ex. Arduino Due)
  by [email protected] 26.12.2013
  Low Pass filter for offset removal replaces HP filter 1/1/2015 - RW
  Proboscide99 10/08/2016 - Added ADMUX settings for ATmega1284 e 1284P (644 / 644P also, but not tested) in readVcc function
  Updated for ESP32 (2020)
*/

#include "EmonLib.h"

#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif

//--------------------------------------------------------------------------------------
// Sets the pins to be used for voltage and current sensors
//--------------------------------------------------------------------------------------
void EnergyMonitor::voltage(unsigned int _inPinV, double _VCAL, double _PHASECAL) {
  inPinV = _inPinV;
  VCAL = _VCAL;
  PHASECAL = _PHASECAL;
  offsetV = ADC_COUNTS >> 1;
}

void EnergyMonitor::current(unsigned int _inPinI, double _ICAL) {
  inPinI = _inPinI;
  ICAL = _ICAL;
  offsetI = ADC_COUNTS >> 1;
}

//--------------------------------------------------------------------------------------
// Sets the pins to be used for voltage and current sensors based on emontx pin map
//--------------------------------------------------------------------------------------
void EnergyMonitor::voltageTX(double _VCAL, double _PHASECAL) {
  inPinV = 2;
  VCAL = _VCAL;
  PHASECAL = _PHASECAL;
  offsetV = ADC_COUNTS >> 1;
}

void EnergyMonitor::currentTX(unsigned int _channel, double _ICAL) {
  if (_channel == 1) inPinI = 3;
  if (_channel == 2) inPinI = 0;
  if (_channel == 3) inPinI = 1;
  ICAL = _ICAL;
  offsetI = ADC_COUNTS >> 1;
}

//--------------------------------------------------------------------------------------
// emon_calc procedure
// Calculates realPower,apparentPower,powerFactor,Vrms,Irms,kWh increment
// From a sample window of the mains AC voltage and current.
// The Sample window length is defined by the number of half wavelengths or crossings we choose to measure.
//--------------------------------------------------------------------------------------
void EnergyMonitor::calcVI(unsigned int crossings, unsigned int timeout) {
#if defined emonTxV3
  int SupplyVoltage = 3300;
#else
  int SupplyVoltage = readVcc();
#endif

  unsigned int crossCount = 0;       //Used to measure number of times threshold is crossed.
  unsigned int numberOfSamples = 0;  //This is now incremented

  //-------------------------------------------------------------------------------------------------------------------------
  // 1) Waits for the waveform to be close to 'zero' (mid-scale adc) part in sin curve.
  //-------------------------------------------------------------------------------------------------------------------------
  unsigned long start = millis();  //millis()-start makes sure it doesnt get stuck in the loop if there is an error.

  while (1)  //the while loop...
  {
    startV = analogRead(inPinV);                                                  //using the voltage waveform
    if ((startV < (ADC_COUNTS * 0.55)) && (startV > (ADC_COUNTS * 0.45))) break;  //check its within range
    if ((millis() - start) > timeout) break;
  }

  //-------------------------------------------------------------------------------------------------------------------------
  // 2) Main measurement loop
  //-------------------------------------------------------------------------------------------------------------------------
  start = millis();

  while ((crossCount < crossings) && ((millis() - start) < timeout)) {
    numberOfSamples++;          //Count number of times looped.
    lastFilteredV = filteredV;  //Used for delay/phase compensation

    //-----------------------------------------------------------------------------
    // A) Read in raw voltage and current samples
    //-----------------------------------------------------------------------------
    sampleV = analogRead(inPinV);  //Read in raw voltage signal
    sampleI = analogRead(inPinI);  //Read in raw current signal

    //-----------------------------------------------------------------------------
    // B) Apply digital low pass filters to extract the 2.5 V or 1.65 V dc offset,
    //     then subtract this - signal is now centred on 0 counts.
    //-----------------------------------------------------------------------------
    offsetV = offsetV + ((sampleV - offsetV) / ADC_COUNTS);
    filteredV = sampleV - offsetV;
    offsetI = offsetI + ((sampleI - offsetI) / ADC_COUNTS);
    filteredI = sampleI - offsetI;

    //-----------------------------------------------------------------------------
    // C) Root-mean-square method voltage
    //-----------------------------------------------------------------------------
    sqV = filteredV * filteredV;  //1) square voltage values
    sumV += sqV;                  //2) sum

    //-----------------------------------------------------------------------------
    // D) Root-mean-square method current
    //-----------------------------------------------------------------------------
    sqI = filteredI * filteredI;  //1) square current values
    sumI += sqI;                  //2) sum

    //-----------------------------------------------------------------------------
    // E) Phase calibration
    //-----------------------------------------------------------------------------
    phaseShiftedV = lastFilteredV + PHASECAL * (filteredV - lastFilteredV);

    //-----------------------------------------------------------------------------
    // F) Instantaneous power calc
    //-----------------------------------------------------------------------------
    instP = phaseShiftedV * filteredI;  //Instantaneous Power
    sumP += instP;                      //Sum

    //-----------------------------------------------------------------------------
    // G) Find the number of times the voltage has crossed the initial voltage
    //    - every 2 crosses we will have sampled 1 wavelength
    //    - so this method allows us to sample an integer number of half wavelengths which increases accuracy
    //-----------------------------------------------------------------------------
    lastVCross = checkVCross;
    if (sampleV > startV) checkVCross = true;
    else checkVCross = false;
    if (numberOfSamples == 1) lastVCross = checkVCross;

    if (lastVCross != checkVCross) crossCount++;
  }

  //-------------------------------------------------------------------------------------------------------------------------
  // 3) Post loop calculations
  //-------------------------------------------------------------------------------------------------------------------------
  //Calculation of the root of the mean of the voltage and current squared (rms)
  //Calibration coefficients applied.

  double V_RATIO = VCAL * ((SupplyVoltage / 1000.0) / (ADC_COUNTS));
  Vrms = V_RATIO * sqrt(sumV / numberOfSamples);

  double I_RATIO = ICAL * ((SupplyVoltage / 1000.0) / (ADC_COUNTS));
  Irms = I_RATIO * sqrt(sumI / numberOfSamples);

  //Calculation power values
  realPower = V_RATIO * I_RATIO * sumP / numberOfSamples;
  apparentPower = Vrms * Irms;
  powerFactor = realPower / apparentPower;

  //Reset accumulators
  sumV = 0;
  sumI = 0;
  sumP = 0;
  //--------------------------------------------------------------------------------------
}

//--------------------------------------------------------------------------------------
double EnergyMonitor::calcIrms(unsigned int Number_of_Samples) {

#if defined emonTxV3
  int SupplyVoltage = 3300;
#else
  int SupplyVoltage = readVcc();
#endif


  for (unsigned int n = 0; n < Number_of_Samples; n++) {
    sampleI = analogRead(inPinI);

    // Digital low pass filter extracts the 2.5 V or 1.65 V dc offset,
    //  then subtract this - signal is now centered on 0 counts.
    offsetI = (offsetI + (sampleI - offsetI) / ADC_COUNTS);
    filteredI = sampleI - offsetI;

    // Root-mean-square method current
    // 1) square current values
    sqI = filteredI * filteredI;
    // 2) sum
    sumI += sqI;
  }

  double I_RATIO = ICAL * ((SupplyVoltage / 1000.0) / (ADC_COUNTS));
  Irms = I_RATIO * sqrt(sumI / Number_of_Samples);

  //Reset accumulators
  sumI = 0;
  //--------------------------------------------------------------------------------------

  return Irms;
}

void EnergyMonitor::serialprint() {
 
  Serial.print(realPower);
  Serial.print(' ');
  Serial.print(apparentPower);
  Serial.print(' ');
  Serial.print(Vrms);
  Serial.print(' ');
  Serial.print(Irms);
  Serial.print(' ');
  Serial.print(powerFactor);
  Serial.println(' ');
  delay(100);
}

//thanks to http://hacking.majenko.co.uk/making-accurate-adc-readings-on-arduino
//and Jérôme who alerted us to http://provideyourown.com/2012/secret-arduino-voltmeter-measure-battery-voltage/

long EnergyMonitor::readVcc() {
  long result;

  //not used on emonTx V3 - as Vcc is always 3.3V - eliminates bandgap error and need for calibration http://harizanov.com/2013/09/thoughts-on-avr-adc-accuracy/

#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__)
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#elif defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__)
  ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#elif defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_AT90USB1286__)
  ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  ADCSRB &= ~_BV(MUX5);  // Without this the function always returns -1 on the ATmega2560 http://openenergymonitor.org/emon/node/2253#comment-11432
#elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
  ADMUX = _BV(MUX5) | _BV(MUX0);
#elif defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
  ADMUX = _BV(MUX3) | _BV(MUX2);

#endif


#if defined(__AVR__)
  delay(2);             // Wait for Vref to settle
  ADCSRA |= _BV(ADSC);  // Convert
  while (bit_is_set(ADCSRA, ADSC))
    ;
  result = ADCL;
  result |= ADCH << 8;
  result = READVCC_CALIBRATION_CONST / result;  //1100mV*1024 ADC steps http://openenergymonitor.org/emon/node/1186
  return result;
#elif defined(__arm__)
  return (3300);  //Arduino Due
#else
  return (3300);         //Guess that other un-supported architectures will be running a 3.3V!
#endif
}

This is why I said you must edit your version of emonLib.cpp Put the print etc statements in temporarily, take them out when you have a few values. Don’t expect exactly the same number each time, but all should be close.

So? It’s just reading a different pin for each instance. It should not make a significant difference.

Ok, I modified EmonLib.cpp like you suggested and here is the data result:

emon1.calcVI (crossings, timeout);
emon2.calcVI (crossings, timeout);

Number of Samples is PER emon instance.

Number of Samples … Crossings … Timeout (ms)

       10700+-               120                   2000
         5400+-               60                   1000
         2700+-               30                   500
         1400+-               15                   300