Energy Monitor Average Amps

Hello All,

This is my first post here and am looking for some help with a three phase energy meter/calculator I am working on. I have the project sort of working and visually its fine, however I need some help with how to do the calculations. what I am trying to do is take a reading of amps with a current transformer. I want to take a reading say 20 times and then calculate the average for those readings this is where I am stuck. then with those readings I want to convert it into KWH.PerKG which is not a problem. Next problem would be is it possible for me to have a permanent calculation running so I can keep updating kWh per kg without refreshing the unit? so like a live reading of Amps being converted and then the screen constantly updating with the new values? any ideas or pointers would be amazing



         #if 1

#include <Adafruit_GFX.h>
#include <MCUFRIEND_kbv.h>
MCUFRIEND_kbv tft;

#include <Fonts/FreeSans9pt7b.h>
#include <Fonts/FreeSans12pt7b.h>
#include <Fonts/FreeSerif12pt7b.h>

#include <FreeDefaultFonts.h>


#include <TouchScreen.h>
#define MINPRESSURE 200
#define MAXPRESSURE 1000

const int XP=8,XM=A2,YP=A3,YM=9; //240x320 ID=0x9341
const int TS_LEFT=902,TS_RT=142,TS_TOP=93,TS_BOT=885;    // done

TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);

Adafruit_GFX_Button on_btn, off_btn;

int pixel_x, pixel_y;     //Touch_getXY() updates global vars
bool Touch_getXY(void)
{
    TSPoint p = ts.getPoint();
    pinMode(YP, OUTPUT);      //restore shared pins
    pinMode(XM, OUTPUT);      //because TFT control pins
    bool pressed = (p.z > MINPRESSURE && p.z < MAXPRESSURE);
    if (pressed) {
        pixel_x = map(p.x, TS_LEFT, TS_RT, 0, tft.width()); //.kbv makes sense to me
        pixel_y = map(p.y, TS_TOP, TS_BOT, 0, tft.height());
    }
    return pressed;
}

#define BLACK   0x0000
#define BLUE    0x001F
#define WHITE   0xFFFF
#define NAVY    0x000F

int currentPins[3] = {10,11,12};              //Assign phase CT inputs to analog pins
double calib[3] = {11.8337,11.8234,12.0325};
double kilos[3];
unsigned long startMillis[3];
unsigned long endMillis[3];
double RMSCurrent[3];
int RMSPower[3];
int peakPower[3];
double RMSAverage;
double KWH;
double ShotWeight;
double CycleTime;
double MaterialThroughPerHr;
double KWHPerKg;
int CountAmps;
int  AmpsAverage25;
int RMSAverage25;
// end of added code 


void setup(void)
{
#if defined(__arm__) || defined(ESP32) //default to 12-bit ADC
    analogReadResolution(10); //Adafruit TouchScreen.h expects 10-bit
#endif
    Serial.begin(9600);
    uint16_t ID = tft.readID();
    Serial.print("TFT ID = 0x");
    Serial.println(ID, HEX);
    Serial.println("Calibrate for your Touch Panel");
    if (ID == 0xD3D3) ID = 0x9486; // write-only shield
    tft.begin(ID);
    tft.setRotation(0);            //PORTRAIT
    tft.fillScreen(BLUE);
    on_btn.initButton(&tft,  60, 290, 100, 40, WHITE, NAVY, WHITE, "KWH/KG", 2);
    off_btn.initButton(&tft, 180, 290, 100, 40, WHITE, NAVY, WHITE, "AMPS", 2);
    on_btn.drawButton(false);
    off_btn.drawButton(false);
    tft.fillRect(10, 10, 220, 240, NAVY);
    showmsgXY(20,40,2.8,NULL, "H");
    showmsgXY(20,65,2.8,NULL, "L");
    showmsgXY(20,89,2.8,NULL, "ENERGY MONITOR");
}

void readPhase ()      //Method to read information from CTs
{
  for(int i=0;i<=2;i++)
  {
    int current = 0;
    int maxCurrent = 0;
    int minCurrent = 1000;
    for (int j=0 ; j<=200 ; j++)  //Monitors and logs the current input for 200 cycles to determine max and min current
    {
      current =  analogRead(currentPins[i]);    //Reads current input and records maximum and minimum current
      if(current >= maxCurrent)
        maxCurrent = current;
      else if(current <= minCurrent)
        minCurrent = current;
    }
    if (maxCurrent <= 517)
    {
      maxCurrent = 516;
    }
    RMSCurrent[i] = ((maxCurrent - 516)*0.707)/calib[i];    //Calculates RMS current based on maximum value and scales according to calibration
    RMSPower[i] = 220*RMSCurrent[i];    //Calculates RMS Power Assuming Voltage 220VAC, change to 110VAC accordingly
    if (RMSPower[i] > peakPower[i])
    {
      peakPower[i] = RMSPower[i];
    }
    endMillis[i]= millis();
    unsigned long time = (endMillis[i] - startMillis[i]);
    kilos[i] = kilos[i] + (RMSPower[i] * (time/60/60/1000000));    //Calculate kilowatt hours used
    startMillis[i]= millis();

    RMSAverage = (RMSCurrent[0] + RMSCurrent[1] + RMSCurrent[2] /3); //calculate average of amps
    
    KWH = (415 * RMSAverage * 0.9 * 1.732 /1000); 

    ShotWeight = 0.100;

    CycleTime = 22;

    MaterialThroughPerHr = (3600 / CycleTime * ShotWeight);

    KWHPerKg = (KWH / MaterialThroughPerHr);

    AmpsAverage25 = (RMSAverage25 /25);
    
  }
}

void loop(void)
{
/*
void readPhase ()      //Method to read information from CTs
{
  for(int i=0;i<=2;i++)
  {
    int current = 0;
    int maxCurrent = 0;
    int minCurrent = 1000;
    for (int j=0 ; j<=200 ; j++)  //Monitors and logs the current input for 200 cycles to determine max and min current
    {
      current =  analogRead(currentPins[i]);    //Reads current input and records maximum and minimum current
      if(current >= maxCurrent)
        maxCurrent = current;
      else if(current <= minCurrent)
        minCurrent = current;
    }
    if (maxCurrent <= 517)
    {
      maxCurrent = 516;
    }
    RMSCurrent[i] = ((maxCurrent - 516)*0.707)/calib[i];    //Calculates RMS current based on maximum value and scales according to calibration
    RMSPower[i] = 220*RMSCurrent[i];    //Calculates RMS Power Assuming Voltage 220VAC, change to 110VAC accordingly
    if (RMSPower[i] > peakPower[i])
    {
      peakPower[i] = RMSPower[i];
    }
    endMillis[i]= millis();
    unsigned long time = (endMillis[i] - startMillis[i]);
    kilos[i] = kilos[i] + (RMSPower[i] * (time/60/60/1000000));    //Calculate kilowatt hours used
    startMillis[i]= millis();

    RMSAverage = (RMSCurrent[2] + RMSCurrent[1] + RMSCurrent[2] /3); //calculate average of amps
    
    KWH = (415 * RMSAverage * 0.9 * 1.732 /1000); 

    ShotWeight = 0.100;

    CycleTime = 22;

    MaterialThroughPerHr = (3600 / CycleTime * ShotWeight);

    KWHPerKg = (KWH / MaterialThroughPerHr);
    
  }
} */
  //////
    
    bool down = Touch_getXY();
    on_btn.press(down && on_btn.contains(pixel_x, pixel_y));
    off_btn.press(down && off_btn.contains(pixel_x, pixel_y));
    if (on_btn.justReleased())
        on_btn.drawButton();
    if (off_btn.justReleased())
        off_btn.drawButton();
    if (on_btn.justPressed()) {
        on_btn.drawButton(true);
        tft.fillRect(10, 10, 220, 240, NAVY);
       // showmsgXY(20, 40, 3, NULL, "KWH PER KG");
        showmsgXY(20, 65, 3, NULL, "");
        displayKWHPerKg (10, 110, 2, NULL, "");
        
    }
    if (off_btn.justPressed()) {
        off_btn.drawButton(true);
        tft.fillRect(0, 5, 240, 245, NAVY);
    //    showmsgXY(20,40,3,NULL, "PHASE1=0.0");
      //  showmsgXY(20,73,3,NULL, "PHASE2=0.0");
        showmsgXY(20,105,3,NULL, "");
        //showmsgXY(20,180,3,NULL, "TOTAL AMPS");
        //showmsgXY(20,205,3,NULL, "0.0");
         
        for (CountAmps; CountAmps <= 25; CountAmps++)
        {
        // for (Count2; Count2 <= 25; Count2++)
        tft.fillRect(0, 5, 240, 245, NAVY);
        tft.setTextColor(WHITE);
        readPhase();
        displayCurrent0 (10,40,2,NULL,"");
        displayCurrent1 (10,80,2,NULL,"");
        displayCurrent2 (10,115,2,NULL,"");
        displayCurrentAverage (10,160,3,NULL,"");
        delay(1000);
        tft.fillRect(0, 5, 240, 245, NAVY);
        readPhase();
        displayCurrent0 (10,40,2,NULL,"");
        displayCurrent1 (10,80,2,NULL,"");
        displayCurrent2 (10,115,2,NULL,"");
        displayCurrentAverage (10,160,3,NULL,"");
        tft.setTextColor(NAVY);
        delay(1000);
        }
        AverageOverAll (10,190,3,NULL,"");
    }
}
    

#endif

void showmsgXY(int x, int y, int sz, const GFXfont *f, const char *msg)
{
    int16_t x1, y1;
    uint16_t wid, ht;
    //tft.drawFastHLine(0, y, tft.width(), WHITE);
    tft.setFont(f);
    tft.setCursor(x, y);
    tft.setTextColor(WHITE);
    tft.setTextSize(sz);
    tft.print(msg);
    delay(1000);
}

void displayCurrent0(int x, int y, int sz, const GFXfont *f, const char *msg)     //Displays all current data
{
 // tft.setCursor(x,y);
 //for (CountAmps; CountAmps <= 25; CountAmps++)
 
  tft.setCursor(x,y);
  tft.print(RMSCurrent[0]);
  tft.print("A PH1");
 
 
}

void displayCurrent1(int x, int y, int sz, const GFXfont *f, const char *msg)     //Displays all current data
{
  tft.setCursor(x,y);
  tft.print(RMSCurrent[1]);
  tft.print("A PH2");
}
void displayCurrent2(int x, int y, int sz, const GFXfont *f, const char *msg)     //Displays all current data
{
  tft.setCursor(x,y);
  tft.print(RMSCurrent[2]);
  tft.print("A PH3");
}

void displayCurrentAverage(int x, int y, int sz, const GFXfont *f, const char *msg)     //Displays all current data
{
  tft.setCursor(x,y);
  tft.print(RMSAverage);
  tft.print("A Avg");
  RMSAverage25 = (RMSAverage25 + 1);
}

void AverageOverAll(int x, int y, int sz, const GFXfont *f, const char *msg)     //Displays all current data
{
  tft.setCursor(x,y);
  tft.print(AmpsAverage25);
  tft.print("A Avg");
}

void displayKWHPerKg(int x, int y, int sz, const GFXfont *f, const char *msg)     
{
  tft.setCursor(x,y);
  tft.print(KWHPerKg);
  tft.print("KWHPerKG");
}

The OpenEnergyMonitor approach has some really nice ideas for accurately measuring AC parameters. Note the special care taken in the code with sampling and averaging.

The Smoothing Example shows you how to make a moving-average.

Assuming a digital display you may just want to "sample" and save readings/calculations once or twice per second, maybe saving 10 o 20 values, and update the moving average once or twice per second. If you update the display too quickly it becomes impossible to read and if you include too many data-points you'll need lots of memory and the calculations will take longer.

For your RMS (or peak) calculations you'll need to sample the 50/60Hz voltage much faster.

As far as I can see you are calculating power by just multiplying current by 220v rms. There are two potential issues with this. First it assumes that current and voltage are in phase which they might not be (in fact usually aren't quite). So some of the solar power controllers also sample the voltage and calculate the phase difference. The other problem is that the voltage can vary quite widely - measuring the voltage takes care of this too.

Very interesting project. Did you look at How others compute 3-phase power..

Also, I see you are using a voltage of 220. My business used lots of 3-phase power and the voltage was usually 208 volts per phase.

Thank you all for your replies, looks like some very helpful information. I am away this weekend so I will study through it all next week. Much appreciated

I usually think that buying an energy meter is a good bet , they can be pretty cheap when you consider the current and voltage transformers , the programming and packaging needed to do the calculations .

If you have a balanced load , just measure power on one phase and multiply it up.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.