Advertisement
rossoreed

Power Monitor with Serial UART

Aug 26th, 2011
173
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 13.05 KB | None | 0 0
  1. //--------------------------------------------------------------------------------------
  2. // Mains AC Non-Invasive Energy Monitor (2 channel)
  3. // Last revision 27th Oct 2010
  4. // Licence: GNU GPL
  5. // By Trystan Lea (updated to support two channel energy monitoring by Glyn Hudson)
  6. // Serial UART library 'Easy Transfer' by Bill Porter.
  7. //--------------------------------------------------------------------------------------
  8. //--------------------------------------------------------------------------------------
  9. // VARIABLE DECLERATION
  10. //--------------------------------------------------------------------------------------
  11.   //--ENERGY MEASURMENT VARIABLES------------------------------
  12.  
  13.   //Power measurement/time variables
  14.   unsigned long t_now, t_diff, t_last, t_culm, ethsend;
  15.  
  16.   //Heater power culmative totals
  17.   unsigned long timeH = 0, timeL = 0, heaterH = 0, heaterL = 0;
  18.   float KWH_H = 0, KWH_L = 0;
  19.    
  20.   //kwhTotal is cumulative kwh today.
  21.   double Solarkwh =0.0;
  22.   double Mainskwh =0.0;
  23.  
  24.   //Variables for calculating heater power
  25.   double sd6 ;
  26.  
  27. //--------------------------------------------------------------------------------------
  28. // EMON
  29. //--------------------------------------------------------------------------------------
  30.  
  31. // include the library code:
  32. #include <LiquidCrystal.h>  // initialize the library with the numbers of the interface pins
  33. LiquidCrystal lcd(8, 9, 10, 11, 12, 13);
  34.  
  35. #include <EasyTransfer.h>
  36.   EasyTransfer ET; //for easy transfer
  37. struct SEND_DATA_STRUCTURE{
  38.   double sd1f;
  39.   double sd2f;
  40.   double sd3f;
  41.   double sd4f;
  42.   double sd5f;
  43.   double sd6f;
  44.   double sd7f;
  45. };
  46. SEND_DATA_STRUCTURE mydata;  //for easy transfer
  47.  
  48. int menuState = 0;
  49. int menu = 0;
  50. int menuintro = 0;
  51.  
  52. int wavelengths = 3000;   //number of wavelengths to sample
  53. int inPinV = 1;           //Analog input pin number that voltage signal is connected to
  54. int inPinI_1 = 3;         //Analog input pin number that current signal is connected to. Channel 1
  55. int inPinI_2 = 2;         //Analog input pin number that current signal is connected to. Channel 2
  56. int menupin = 2;          //Arduino pin 1 goes to pushbutton for menu
  57. int RelayOn = 4;          //Arduino pin 4 goes to the opto isolator (full power)
  58. int HRelayOn = 3;         //Arduino pin 3 goes to opto isolator (half power)
  59. int led_on = 6;           //Arduino pin 6 goes to panel LED
  60.  
  61.  
  62. //channel 1
  63. double VCAL = 1.28348;              //Voltage calibration scaler
  64. double ICAL_1 = 0.05815;            //Current calibration scaler
  65. double ICAL_2 = 0.05806;
  66.  
  67. double PHASECAL = 2.3;                        //Shifts voltage relative to current,
  68.                                               //subtracting any phase shifting caused by components
  69.    
  70. int emon_timeout = 2000;                       //how long to wait ms if something goes wrong.
  71.  
  72.  
  73. class Channel
  74. {
  75.   public:
  76.     void emon_calc(int,double);                //create pulse function
  77.     double realPower,                          //Output variables
  78.        apparentPower,
  79.        powerFactor,
  80.        Vrms,
  81.        Irms,
  82.        whInc,
  83.        wh;
  84.  
  85.   private: // Variable declaration for emon_calc procedure
  86.    int lastSampleV,sampleV;                          //sample_ holds the raw analog read value, lastSample_ holds the last sample_
  87.     int lastSampleI,sampleI;                      
  88.  
  89.     double lastFilteredV,filteredV;                   //Filtered_ is the raw analog value minus the DC offset
  90.     double lastFilteredI, filteredI;                  
  91.    
  92.     double phaseShiftedV;                             //Holds the calibrated phase shifted voltage.
  93.    
  94.     double sqV,sumV,sqI,sumI,instP,sumP;              //sq = squared, sum = Sum, inst = instantaneous
  95.    
  96.     int startV;                                       //Instantaneous voltage at start of sample window.
  97.    
  98.     boolean lastVCross, checkVCross;                  //Used to measure number of times threshold is crossed.
  99.     int crossCount;                                   // ''
  100.    
  101.     unsigned long lwhtime, whtime;                    //used to calculate energy used.
  102.  
  103. };
  104.  
  105. Channel ch1,ch2;    //create two incidence of class channel for the two channels
  106.  
  107. //--------------------------------------------------------------------------------------
  108. // SETUP
  109. //--------------------------------------------------------------------------------------
  110. void setup()
  111. {
  112.    lcd.begin(16, 2); // set up the LCD's number of columns and rows:
  113.  
  114.    Serial.begin(9600);
  115.    ET.begin(details(mydata), &Serial); //easy transfer
  116.    randomSeed(analogRead(0));  //easy transfer
  117.    
  118.    pinMode(RelayOn,OUTPUT); // this sets the pin named RelayOn to be an output
  119.    pinMode(HRelayOn,OUTPUT); // this sets the pin named RelayOn to be an output
  120.    pinMode(menupin, INPUT); // this sets the pin named menupin to be an input
  121.    pinMode (led_on, OUTPUT); //this sets the pin named led_on to be an output
  122.  
  123.    t_culm = (3600000 * 12);       //On first run, advances the 24hr timer by 12hrs (so can restart at midday!)  
  124.    //t_culm = (3600000 * 23.75);  //FOR TESTING ONLY
  125.    
  126.    digitalWrite(RelayOn,HIGH);   //Switches relay off
  127.    digitalWrite(HRelayOn,HIGH);  //Switches relay off
  128.    
  129.    //Introduces 15 second delay to allow caps to charge up
  130.     lcd.clear();
  131.     lcd.setCursor(0, 0);
  132.     lcd.print("  Please Wait  ");
  133.     lcd.setCursor(0, 1);
  134.     lcd.print("Initialising....");
  135.     delay (15000);
  136. }
  137.  
  138. unsigned long wtime;
  139.  
  140. //--------------------------------------------------------------------------------------
  141. // MAIN LOOP
  142. //--------------------------------------------------------------------------------------
  143. void loop()
  144. {
  145.   //-------------------------------------------------------------------------------------------
  146.   // 1) Calculate energy monitor values
  147.   //-------------------------------------------------------------------------------------------
  148.   ch1.emon_calc(inPinI_1,ICAL_1);  //Energy Monitor calc function for channel 1, pass Arduino analog in pin nummber and calibration coefficient
  149.   ch2.emon_calc(inPinI_2,ICAL_2);  //Energy Monitor calc function, for channel 2, pass Arduino analog in pin nummber and calibration coefficient
  150.  
  151. if (ch1.realPower < 0)  {   //Stops the solar channel reading negative (invertor anomaly)
  152.    (ch1.realPower = 0);  }
  153.  
  154.   //--ENERGY MEASURMENT CALCULATION----------------  
  155.  
  156.  //Power measurement
  157.   t_now = millis();
  158.     if ( t_now > t_last ) {   //check to see if millis has overflowed back to zero (after approx 49 days)
  159.       t_diff = t_now - t_last; } //if not, the time each emon cycle takes
  160.         else {  //if millis has reset...
  161.           t_diff = 4127; }   //t_diff = (the amount of time each average emon cycle takes), again dealing with the millis reset.
  162.   t_culm += t_diff;
  163.   t_last = t_now;
  164.  
  165.   if ( t_culm > (3600000 * 24)){ //check to see if 24hrs has elapsed
  166.   Solarkwh = 0; // RESET
  167.   Mainskwh = 0; // RESET
  168.   t_culm = 0;   // RESET
  169.   heaterH = 0;  // RESET
  170.   timeH = 0;    // RESET
  171.   heaterL = 0;  // RESET
  172.   timeL = 0;    // RESET
  173.   ethsend = 0;  // RESET
  174.   }
  175.  
  176.  //Calculate kW's diverted to heater per 24hrs
  177.    if (digitalRead(RelayOn) == LOW) timeH += t_diff;
  178.    if (digitalRead(HRelayOn) == LOW) timeL += t_diff;
  179.    heaterH = (1.09 * timeH);    // units Ws;  (long math)
  180.    heaterL = (0.545 * timeL);      
  181.    KWH_H = heaterH / 3.6e6;     // units KWH    3.6e6 = (3600.0 * 1000.0)
  182.    KWH_L = heaterL / 3.6e6;
  183.  
  184.  //checks to see if can run at full power
  185.       if (ch1.realPower > (ch2.realPower +1090)) {
  186.       digitalWrite(HRelayOn,HIGH); //turn the half power output off
  187.       delay (200);
  188.       digitalWrite(RelayOn,LOW); //turn the full output on
  189.        }
  190.   else{
  191.       digitalWrite(RelayOn,HIGH); //turn the output off
  192.        //checks to see if can run at half power
  193.        if (ch1.realPower > (ch2.realPower +545)) {
  194.         delay (200);
  195.         digitalWrite(HRelayOn,LOW); }//turn the output on
  196.           else  {
  197.            digitalWrite(HRelayOn,HIGH); //turn the output off
  198.      }
  199.    }
  200.  
  201.  //LED control
  202.    if (digitalRead(RelayOn) == LOW) {
  203.      analogWrite(led_on, 200);  }
  204.    else if (digitalRead(HRelayOn) == LOW) {
  205.      analogWrite(led_on, 20);   } //low brightness
  206.    else {
  207.          digitalWrite (led_on,HIGH);
  208.          delay (100);
  209.          digitalWrite (led_on,LOW);
  210.         }
  211.        
  212.   //Calculate number of kwh generated.
  213.   Solarkwh += ((ch1.realPower/1000.0) * 1.0/3600.0 * (t_diff/1000.0));
  214.   Mainskwh += ((ch2.realPower/1000.0) * 1.0/3600.0 * (t_diff/1000.0));  
  215.  
  216.     //------------Data Output-------------
  217.      
  218.     //Read menupin to see what menu to display
  219.     menuState = digitalRead(menupin);
  220.     if ( menuState == LOW) {
  221.     menu = menu+1;
  222.     menuintro = 0;  } //because the button has been pressed, sets menuintro to 0, so intro screen is displayed
  223.     if (menu > 4){
  224.     menu = 0;
  225.     }  
  226.    
  227.     //MENU 0  
  228.     if (menu == 0) {
  229.     // clear the lcd screen
  230.     lcd.clear();
  231.     //if-else statement to alter LCD print format depending upon power reading
  232.     lcd.setCursor(0, 0);
  233.         if (ch1.realPower >= 1000)  {
  234.       lcd.print("Solar "); lcd.print(ch1.realPower / 1000.0, 3); lcd.print(" kW");  }
  235.     else  {
  236.       lcd.print("Solar "); lcd.print(ch1.realPower, 0); lcd.print(" Watts");  }
  237.    
  238.     lcd.setCursor(0, 1);
  239.         if (ch2.realPower >= 5000)  {
  240.       lcd.print("M/Power over 5kW"); }
  241.     else
  242.       if (ch2.realPower >= 1000)  {
  243.       lcd.print("Mains "); lcd.print(ch2.realPower / 1000.0, 3); lcd.print(" kW");  }
  244.     else  {
  245.       lcd.print("Mains "); lcd.print(ch2.realPower, 0); lcd.print(" Watts");  }
  246.     }
  247.  
  248.     //MENU 1 - Displays kWh's per 24hrs
  249.     if (menu == 1) {
  250.       if (menuintro == 0) {  //if menu button has been pressed, displays intro screen
  251.       lcd.clear();
  252.       lcd.setCursor(0, 0);
  253.       lcd.print ("Power Stats for");
  254.       lcd.setCursor(0, 1);
  255.       lcd.print("today");
  256.       delay (2500); }
  257.     lcd.clear();
  258.     lcd.setCursor(0, 0);
  259.     lcd.print("Solar "); lcd.print(Solarkwh); lcd.print(" kWh");
  260.     lcd.setCursor(0, 1);
  261.     lcd.print("Mains "); lcd.print(Mainskwh); lcd.print(" kWh");
  262.     menuintro = 1;  }  //sets menu intro to 1 so that intro screen is not repeated
  263.    
  264.     //MENU 2 - Displays kWh's diverted to heater per 24hrs
  265.     if (menu == 2) {
  266.     if (menuintro == 0) {
  267.     lcd.clear();
  268.     lcd.setCursor(0, 0);
  269.     lcd.print ("kWh diverted to");
  270.     lcd.setCursor(0, 1);
  271.     lcd.print("imm heater today");
  272.     delay (2500);  }
  273.       lcd.clear();
  274.       lcd.setCursor(0, 0);
  275.       lcd.print(KWH_H); lcd.print("H + "); lcd.print(KWH_L); lcd.print("L");
  276.       lcd.setCursor(0, 1);
  277.       lcd.print("Total= "); lcd.print(KWH_H + KWH_L); lcd.print(" kWh");  
  278.       menuintro = 1; }
  279.    
  280.     //MENU 3 - Displays Voltage & Power Factor calibration
  281.     if (menu == 3) {
  282.       if (menuintro == 0) {
  283.       lcd.clear();
  284.       lcd.setCursor(0, 0);
  285.       lcd.print ("Voltage and");
  286.       lcd.setCursor(0, 1);
  287.       lcd.print("power factor");
  288.       delay (2500);  }
  289.     lcd.clear();
  290.     lcd.setCursor(0, 0);
  291.     lcd.print("PowerF "); lcd.print(ch1.powerFactor, 2);
  292.     lcd.setCursor(0, 1);
  293.     lcd.print("Voltage "); lcd.print(ch1.Vrms, 0); lcd.print(" V");
  294.     menuintro = 1;  }
  295.    
  296.     //MENU 4 - DEMO MODE
  297.     if (menu == 4) {
  298.     lcd.clear();
  299.     lcd.setCursor(0, 0);
  300.     lcd.print ("Press button for");
  301.     lcd.setCursor(0, 1);
  302.     lcd.print("2 secs for demo");
  303.     delay (2000);
  304.     if (digitalRead(menupin)== LOW) {  //check to see if button is still pressed
  305.     lcd.clear();
  306.     lcd.setCursor(0, 0);
  307.     lcd.print ("DEMO MODE");
  308.     delay (2000);
  309.     lcd.clear();
  310.     lcd.setCursor(0, 0);
  311.     lcd.print("Solar 1.326 kW");
  312.     lcd.setCursor(0, 1);
  313.     lcd.print("Mains 298 Watts");
  314.     digitalWrite (led_on,HIGH);
  315.     delay (4000);
  316.     lcd.clear();
  317.     lcd.setCursor(0, 0);
  318.     lcd.print("Solar 18.36 kWh");
  319.     lcd.setCursor(0, 1);
  320.     lcd.print("Mains 4.29 kWh");
  321.     digitalWrite (led_on,HIGH);
  322.     delay (4000);
  323.     lcd.clear();
  324.     lcd.setCursor(0, 0);
  325.     lcd.print("2.44H + 1.80L");
  326.     lcd.setCursor(0, 1);
  327.     lcd.print("Total= 4.24 kWh");
  328.     delay (4000);
  329.     lcd.clear();
  330.     lcd.setCursor(0, 0);
  331.     lcd.print("PowerF 0.98");
  332.     lcd.setCursor(0, 1);
  333.     lcd.print("Voltage 247 V");
  334.     digitalWrite (led_on,HIGH);
  335.     delay (4000);
  336.     menu = 0; }
  337.     else {
  338.       menu = 0; }
  339.     }
  340.        
  341.     //----------------Serial Data to Ethernet unit------------------//
  342.    
  343.     if (digitalRead(RelayOn) == LOW) {
  344.       sd6 = 1090 ;  }
  345.        else if (digitalRead(HRelayOn) == LOW) {
  346.          sd6 = 545 ;  }
  347.           else {
  348.             sd6 = 0  ;}
  349.    
  350.     //timer to control ethernet send rate
  351.     if (t_culm > (ethsend + 30000)) {  //will execute a serial UART send 'after' 30 seconds
  352.       ethsend = t_culm;
  353.       //ethernet  send code goes here
  354.         mydata.sd1f = (ch1.realPower, 0);
  355.         mydata.sd2f = (ch2.realPower, 0);
  356.         mydata.sd3f = (Solarkwh);
  357.         mydata.sd4f = (Mainskwh);
  358.         mydata.sd5f = (KWH_H + KWH_L);
  359.         mydata.sd6f = sd6;
  360.         mydata.sd7f = (ch1.Vrms, 0);
  361.      //send the data
  362.         ET.sendData();
  363.     }
  364.  }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement