Calculating kWh not working

Thread Starter

Bogdan.m

Joined Apr 20, 2019
57
So i am trying to make a solar charger with arduino and i am taking it one step at a time, i am making the monitoring first, everything is ok and shows up nicely on the screen when the input V and A are not changing, the moment they change, let's say the A turns to 0, the Wh also goes to 0, the total amount of power used is not adding up, only for the current period of time. This is how my lcd looks like, and the code i am using.

Code:
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
#include <Adafruit_ADS1015.h>
Adafruit_ADS1115 ads(0x4B);
Adafruit_ADS1115 adsa(0x4A);
float V1 = 0.0;
float V2 = 0.0;
float V3 = 0.0;
float V4 = 0.0;
float V5 = 0.0;
float V6 = 0.0;

void setup(void)
{
  lcd.init();
  lcd.backlight();
  Serial.begin(9600);
  ads.begin();
  adsa.begin();
}
unsigned long startMillis;
unsigned long currentMillis;
unsigned long elapsedMillis;

void loop(void)
{
  int16_t adc1;
  adc1 = ads.readADC_SingleEnded(1);
  V1 = (adc1 * 0.0001875) * 6;
  int16_t adc2;
  adc2 = ads.readADC_SingleEnded(2);
  V2 = (adc2 * 0.0001875) * 6;
  int16_t adc3;
  adc3 = ads.readADC_SingleEnded(3);
  V3 = (adc3 * 0.0001875) * 6;
  int16_t adc4;
  adc4 = adsa.readADC_SingleEnded(1);
  V4 = (adc4 * 0.0001875) * 10;
  int16_t adc5;
  adc5 = adsa.readADC_SingleEnded(2);
  V5 = (adc5 * 0.0001875) * 10;
  int16_t adc6;
  adc6 = adsa.readADC_SingleEnded(3);
  V6 = (adc6 * 0.0001875) * 10;

  currentMillis = millis();
  elapsedMillis = (currentMillis - startMillis);
  unsigned long SS = (elapsedMillis / 1000) % 60;
  unsigned long MM = (elapsedMillis / (60000)) % 60;
  unsigned long HH = (elapsedMillis / (3600000));

  lcd.setCursor(0, 0);
  lcd.print(V1, 2);
  lcd.setCursor(5, 0);
  lcd.print("V");
  lcd.setCursor(7, 0);
  lcd.print(V2, 2);
  lcd.setCursor(12, 0);
  lcd.print("V");
  lcd.setCursor(14, 0);
  lcd.print(V3, 2);
  lcd.setCursor(19, 0);
  lcd.print("V");

  lcd.setCursor(0, 1);
  lcd.print(V4, 2);
  lcd.setCursor(5, 1);
  lcd.print("A");
  lcd.setCursor(7, 1);
  lcd.print(V5, 2);
  lcd.setCursor(12, 1);
  lcd.print("A");
  lcd.setCursor(14, 1);
  lcd.print(V6, 2);
  lcd.setCursor(19, 1);
  lcd.print("A");

  lcd.setCursor(0, 2);
  lcd.print(V1 * V4, 1);
  lcd.setCursor(5, 2);
  lcd.print("W");
  lcd.setCursor(7, 2);
  lcd.print(V2 * V5, 1);
  lcd.setCursor(12, 2);
  lcd.print("W");
  lcd.setCursor(14, 2);
  lcd.print(V3 * V6, 1);
  lcd.setCursor(19, 2);
  lcd.print("W");

  lcd.setCursor(0, 3);
  lcd.print((((V1 * V4) + (V2 * V5)) * elapsedMillis) / 3600000, 0);
  lcd.setCursor(7, 3);
  lcd.print(((V3 * V6) * elapsedMillis) / 3600000, 0);
  lcd.setCursor(14, 3);
  lcd.print(MM);
  lcd.setCursor(17, 3);
  lcd.print(":");
  lcd.setCursor(19, 3);
  lcd.print(" ");
  lcd.setCursor(18, 3);
  lcd.print(SS);
}
lcd look.jpg
 
Basically, you need to integrate over time and that time interval can be whatever you decide on. Like per day. You can implement the trapezoidal rule and numerically integrate. https://en.wikipedia.org/wiki/Trapezoidal_rule

You essentally have to compute the definate integral of P dt from t=0 to t=(1 day). dt or delta t has to be small (e.g. 10 ms) for the greatest accuracy. So, you keep adding up the area of trapezoids every small unit of time delta t.

You mught end up with Watt * 10ms * t; there are so many 10 ms chunks of time in say a day. So, you need to convert the sum into kWh.

for solar, current kWh, kWh/day and kWh/month are useful numbers
Current kWh resets at 00:00 h.
 

Thread Starter

Bogdan.m

Joined Apr 20, 2019
57
The problem is, even if i add up W * 10ms * t, and in a time being the watt is = 0 , the equation will be 0, so i need a line of code that add up the total W every second, addition will not change if the W = 0 like multiplying does. My timer is showing HHH:MM, so it can reset after 999 hours, however i am multiplying V * A * elapsed time, so i don't know if it makes a difference what is showing on the lcd.
 
it's more like:
pseudocode

main:

KWH = 0: elapsed time =0

On every 10ms interval do:

While elapsed time <=24h

elapsed time = elapsed time + 10ms

KW_Sum_of_10mS interval) = KW_Sum_of_10mS interval+ v(t)*i(t) * 10mS

Hours = concert elapsed time to hours

Display KWH = KW_Sum_of_10mS interval*Hours

Loop 10ms interval

Loop 24 hours

Goto main

This basically assumes that within each 10mS interval v(t) * i(t) does not change. The trapezoidal rule would fix that somewhat.

Your basically counting the number of elapsed 10ms intervals to get the elapsed time in hours for a 0-24 hour period and calculate hours.

And your summing v(t) * i(t) over the interval 0 to t; where t is the current time.

using the trapezoidal rule, you will add or subtract a right triangle shaped region of the power curve with a height of p(t-10ms) + p(t) and a width of 10ms so as the interval gets smaller, the result gets more accurate providing there is change in power in the interval.

it's better to add trapezoids than rectangles.

See: https://www.google.com/search?client=firefox-b-d&q=area+of+a+trpezoid

a nd b are p(t-10ms) and p(t) and h is the time interval. d will be equal to h.

I may have missed something.
 

Thread Starter

Bogdan.m

Joined Apr 20, 2019
57
I forgot to mention that i have no training what so ever in programming, and my code is gathered here and there, and sometimes it feels like you guys are speaking in foreign tongues to me. I am thinking that it would be easier to just write the current W + the W 10ms ago(if that is possible), and then repeat, and since the W 10ms ago will always have the total power because you keep adding to it, even if i add 0 it would just stay at the total consumption level.
 

sagor

Joined Mar 10, 2019
903
One could also do lower units like Watt-Seconds. That is, take a Volt*Amp reading every second and store that wattage value, adding it to a variable. After an hour, take the total and divide by 3600 to get watt-hour. Or, take the raw 1 second reading , divide by 3600 (small fractional value) and add it up for an hour in one variable. This may be less accurate because of such small values.
The final issue is, is one second reading frequent enough or can it produce reasonable accumulated totals? The sun does not change that fast in terms of solar power, even with a cloud rolling by.
Add up all the watt-hour readings over 24 hours (another variable), divide by 1000 to get your total kwH for the day.

Bottom line is you have to take watt readings with a unit of time, and you have to use some accurate fixed time interval to calculate the watt-(time units)
 
kWh is over some time. With solar, per day makes sense,so does average per day and so does average per month.

You utility might keep a 2 yr monthly average and a monthly bar graph so you can compare the prior year.

With solar, you might want a monthly bar graph with power coming from the utiity and power coming from the array stacked n top of one another.

Numerical integration is kinda a computer thing, but I had the trapezoidal rule in calculus.

Lot's of V*I's added together a fixed time apart is a start. V in volts, I in amps.

Count the number of intervals since 00:00 hrs and reset at 24:00 hrs. Call that number I. I is the current interval.

So Current kWH is the sum of (Power in Watts), the V*I sum.

That translates to:
(10ms*I); The number of intervals since reset,
(10ms*I)*1s/1000ms*60m/s*1h/60min; this converts to elapsed hours.

You have this V*I sum that you have to divide by 1000 to get Watts.

So when your all done, you have: (10ms*I)*1s/1000ms*60m/s*1h/60min*[Sum(V(i)*I(i)]

The current kWh then keeps getting bigger until reset for the 24h period.

Summing power trapezoids rather than power rectangles is more accurate.
 
This kind of nonsense is called numerical methods. Numerical Integration, differentiation are usually one of the first concepts.

Integration (definate integral) is usually described as the area under a curve.
Double integrals can find volume of a solid.

Differentiation is an equation describing the slope at any point of the curve.

They are kinda opposite functions, but once you differentiate a function, you can't get the original back without knowing more information like the value at a point.

Remember y=mx+b where m is the slope; Differentiated, 1593317150857.png OR 1593317013349.png
 
Last edited:

Thread Starter

Bogdan.m

Joined Apr 20, 2019
57
The idea behind what i am doing is just to have some sort of idea of what i put in vs what i put out, i don't care about monthly average, or anything else, this is why i use only a small lcd, and a counter of HHH:MM and later on i also want a reset button so i can reset it whenever i want, gonna happen probably once a week or so, since i have 999 hours to monitor. so i need help with just one line of code that does this for me.
 
Area of trapezoid: https://www.google.com/search?client=firefox-b-d&q=area+of+trapezod

Pseudo-code

(a+b)/2 * h; h is delta time.

a=P(i-1); b=P(i)

You need the sum of (P(i-1)+P(i))/2 * h; P(i) is Power. i is the number of times through the loop.

What that basically means is you need a recurring timer, say 10 ms.

The first time through the loop after a reset, you do not sum because P(i-1) is not defined or maybe it is or you get that value before you stat the timer.

You just them sum and get the units right and within your display capability.

I general, the ISR (Interrupt Service Routine) should do very little. it might just gather the info you need and exit. The other part of the program does the computations.
the sum of Watt-Seconds might work.

This would use the trapezoidal rule.
 
Last edited:
Better Pseudo-code

Energy_sum = 0
Power-1 = Power()

On timer, recurring Timer_interval_ms goto Compute_Energy_ISR

Exit

Compute_Energy_ISR:

P=Power()

Energy_sum=Energy_sum+(Power-1+P)/2*Timer_interval_ms; Energy_sum must be global; P could be global

Power-1=P; (Power-1) must be a global variable.

Return

Notes:
Energy_sum is in some wacko-units. If Power returned Watts, then Energy_Sum /1000 /60 /60 is kWh, I think.

if you need elapsed time, you can use the # of intervals. You can drop the Time_interval_ms in the ISR. Energy_sum isn;t quite Energy_sum, but you multiply by elapsed time to get kWh.

Your going to have to watch numbers overflowing..

You could have a separate route to start the ISR and make the ISR stop or make the sum stop when you reach 999 hours.

Watt-seconds is an easier number to understand as was pointed out by @sagor, but it's comutation you don't want to do in the ISR.
For instance, your display update might be every second or every 100 calls to the 10mS ISR. The ISR just sets a variable that says "do display update".

the program would generally end with something that says:

Wait for interrupt.

it's a different methodology for programming.

Usually the first thing an ISR does is disable interrupts.

I don't think you have to worry about semaphores yet. These are used when there can be competition for a resource.

You check to see if a resource is busy with
is x=0, then x=x+1;
The x=x+1 is implemented in one machine language instruction, so it can't get interrupted.
Then you check if x>1 and if it is you dec x. the resource is busy, otherwise if x=1 and you have the resource.
 
Last edited:
Your current program needs to be structured like:

main:


DC=0; Display count - Counts # of 10 ms intervals;

Gosub RESET_energy

display = true (It will display on next iteration)

On every 10ms goto ISR

On falling edge of reset_KWH pin gosub RESET_Energy (the energy reset button)

Loop:

If display == true gosub display

Wait for interrupt

Goto loop

Display():
Write to LCD, P, I V etc.
Some check on elapsed time, so program doesn't crash.
display=false
RETURN

ISR:

ET=ET+1 ! Elapsed time - Number of 10 ms intervals

Disable interrupts
get, V, I and P and sum energy units into global variables.
If reset_energy == true gosub RESET_energy : Goto End_ISR.
If DC>99 then DC=0; Display = true

Sum energy using trapezoidal rule.

DC=DC+1


End_ISR:
Enable Interrupts
RETURN



reset_energy:

reset-energy=false
get V, I and P
Energy_sum =0
Power-1=P
DC=0
ET=0

RETURN

END

Hopefully getting V and I are fast operations. Variables that keep increment , you have to worry about.ting like ET
Keeping track of the number of intervals is the elapsed time.

But the number of ms in 1000 hrs is
3600000000 which is a BIG number, so that's another thing you have to worry about.

You can ignore it and use the system time functions. You might even change the energy units dynamically. W-s, W-min, W-hrs, kWh.

power-1 is a variable, not an equation. Previous_Power might make more sense.

I don't know if you have to make the reads take place outside of the ISR. That's where things can get messy. V and I should be read at the same time and the tme between reads should be known.
 
Last edited:

Thread Starter

Bogdan.m

Joined Apr 20, 2019
57
Problem solved, thank you very much, i added your idea from the previous post and it works like a charm. Thanks
 
@Bogdan.m I'm Impressed. I wasn't sure if you had the math background.

In a project back in the 80's I did compute energy in a 7-loop PID controller implementation.

I gave you some got-cha's as well.

There was an equation I was given for an implementation which I would like to try to re-derive. It's basically the VCR tape problem. You know the initial diameter, current diameter and the thickness of the tape. Move the tape about 4" every 2 minutes. A counter kept track of the number of turns.

Anyway, the set-up ended up to be an initial shaft size of 1/4", thickness was 0.001", a synchronous motor of some low RPM and a turns-counting dial which could be reset to zero for a 1/4" shaft size.

You would input how often, length, initial turns count and bingo.

What it did was move a piece of transparent Teflon that was deposited on by the reaction of a UV lamp to keep the window clean.

You had a mechanical counter that had to match the computer counter. Accuracy of how much material was moved was not that important.

it's loosely based on the fundamental s=r*theta equation.
 

Thread Starter

Bogdan.m

Joined Apr 20, 2019
57
for the charging i use this code, you think it's ok ?
Code:
if (V3 < 28 )
  {
    if (pulseWidth != 255) pulseWidth++;
  }
  if (V3 > 28 )
  {
    if (pulseWidth = 0);
  }
  analogWrite(9, pulseWidth);
 

ci139

Joined Jul 11, 2016
1,898
just the reminder \[1kWh=1kW·1h=10^3W·60^2s=3.6·10^6J\\ {\ }\\ \left({SI}\right)E\left({kWh}\right)=UIt\frac1{36}10^{-5}\left({kWh}\right)\]
... e.g. ... if you consume in average 1W of power -- you'll be using 0.001kWh per hour ... half of that per half an hour ... etc. ...
 

djsfantasi

Joined Apr 11, 2010
9,156
I can’t be sure without seeing a little more code, but the if test for V3 > 28 does nothing.

in this snippet, you’re always performing an analogWrite().

And pulsewidth never is initialized in this snippet. Without further code, once V3 hits 28, it stays there.
 
Top