2
\$\begingroup\$

How I can calculate accurate energy if I have power, current and voltage values?

This is the code of energy calculation, the result's it's wrong so how I can fix that?

I want to measure apparent energy, I don't have a problem in V, I, P values.

if(millis() >= energyLastSample + 1)                                                
          {
            energySampleCount = energySampleCount + 1;  
            energyLastSample = millis(); 
          }
          if(energySampleCount >= 1000)
          {                                                       
            apparent_energy_l1 = apparent_power_l1/3600.0;                                            
            finalEnergyValue_l1 = finalEnergyValue_l1 + apparent_energy_l1;
            energySampleCount = 0 ;                                                           
         }
}
\$\endgroup\$

2 Answers 2

7
\$\begingroup\$

Energy is the integral of power, and you can compute an integral by the discrete approximation ΣPδt. In code:

float get_power_reading();  // assumed to be defined elsewhere
float energy = 0;  // calculated energy

void update_energy()
{
    static uint32_t last_sample_time;
    uint32_t now = millis();
    uint32_t delta_t = now - last_sample_time;
    if (delta_t >= minimal_sample_period) {
        float power = get_power_reading();
        energy += power * delta_t;
        last_sample_time = now;
    }
}

A few remarks:

  • The shorter the minimal sample period, the more accurate the resulting integral, but that could slow your program if get_power_reading() is a slow process. If that function is fast, you can forget about minimal_sample_period and update the energy at every loop iteration.

  • If the update period is very short, use micros() instead of millis().

  • Both millis() and micros() roll over periodically, but the computation of delta_t is rollover-safe.

  • If energy gets very large, it may suffer an overflow (if defined as integer/fixed point) or loss of precision (if it is floating point).

  • The computed energy is in milliseconds times whatever unit is used by get_power_reading(). For example, if the power reading is in watts, then the energy is in millijoules (1 W × 1 ms = 1 mJ). That would be microjoules if you are using micros() instead of millis(). You may want to convert that to the unit of your choice.

  • millis() is called only once per call of update_energy(). This is important to keep the calculations correct.

\$\endgroup\$
1
  • \$\begingroup\$ Very well written, salute. \$\endgroup\$
    – php_nub_qq
    yesterday
1
\$\begingroup\$

For the sampling of data, I would use a "timer interrupt" for "exact" regularity.

  • Choose timing as "problem" needs.

  • Sample V and I in the interrupt task.

Then calculate outside interrupt ... P, E, and others variables if needed ... as fast as possible within "time" ...

Here a sample of parts program/libraries used with Arduino UNO...

  • digitalWrite(13, HIGH) ; digitalWrite(13, LOW) ;
    are for tracing on "logic analyser" the timings of sample ...

Program was use for tracing power & energy on just one 20ms cycle period (21 samples), repeated as soon as possible after all calculations (FFT included).

#include <TimerOne.h>
#include <LowPower.h>


void setup()
{
  Serial.begin(115200); // Open serial connection to report values to host
  pinMode(13, OUTPUT);

  Timer1.initialize(1000); // set a timer of length 1000 microseconds     
  Timer1.attachInterrupt( timerIsr ); // attach the service routine here
  
}

/// --------------------------
/// Custom ISR Timer Routine
/// --------------------------
void timerIsr()
{   
if (count < nbp) 
   { 
    //  Start ADC on negative edge of D13   8 channels ??? in 1 ms ???     OK !!!
       // Serial.print(micros() / 10000); Serial.print("/");
       // Serial.print(micros() % 10000); Serial.print("/"); // 4 us d'erreur max
   // delays are used for some corrections when calculating all powers S, P, Q
       
digitalWrite(13, HIGH) ; delayMicroseconds(1); digitalWrite(13, LOW) ;  // 0 us           
        sensorValue0[count] = analogRead(A0) ;
digitalWrite(13, HIGH) ; delayMicroseconds(1); digitalWrite(13, LOW) ;  // 124 us         
        sensorValue1[count] = analogRead(A1) ;
digitalWrite(13, HIGH) ; delayMicroseconds(1); digitalWrite(13, LOW) ;  // 243.5 us          
        sensorValue2[count] = analogRead(A2) ;
digitalWrite(13, HIGH) ; delayMicroseconds(1); digitalWrite(13, LOW) ;  // 358.5 us          
        sensorValue3[count] = analogRead(A3) ;
digitalWrite(13, HIGH) ; delayMicroseconds(1); digitalWrite(13, LOW) ;  // 479 us         
        sensorValue4[count] = analogRead(A4) ;
digitalWrite(13, HIGH) ; delayMicroseconds(1); digitalWrite(13, LOW) ;  // 599 us          
        sensorValue5[count] = analogRead(A5) ;
digitalWrite(13, HIGH) ; delayMicroseconds(1); digitalWrite(13, LOW) ;  // 715 us         
        sensorValue6[count] = analogRead(A6) ; // mid point
digitalWrite(13, HIGH) ; delayMicroseconds(1); digitalWrite(13, LOW) ;  // 838.5 us       
        sensorValue7[count] = analogRead(A7) ; // mid point 
digitalWrite(13, HIGH) ; delayMicroseconds(1); digitalWrite(13, LOW) ;  // 963.5us end
                
    //  ADC channels A4, A5, A6, A7 are also ok ... on pro/mini boards ...
        
        // channel  ... for tension measured at the "same time" ... 20.005 ms ok
        // then ... calculus of active/reactive/apparent power possible
        // by formula active power = v(0)*c(0)+v(90)/c(90) ???

        //    Serial.print(micros()); Serial.print(" / "); 
                // for control exact timing       
   }
        else {    
          Timer1.stop();
          digitalWrite(13, LOW) ;
          num_per++ ;          
          }     
        count++ ;
}


```
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.