Electronics > Beginners
Arduino Code - volts/amps/power monitor
(1/10) > >>
metrologist:
I've been working on this for my solar system. It will display the panel voltage, current, and power consumed.

I wanted some affirmation that the averaging routing and other calculations are correct. I've padded dummy data for the sensor reads. I'm noticing a steady 0.02 or 0.03 WHr discrepancy.


--- Code: ---#include "U8glib.h"
//U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE|U8G_I2C_OPT_DEV_0); // I2C / TWI
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0|U8G_I2C_OPT_NO_ACK|U8G_I2C_OPT_FAST); // Fast I2C / TWI
//U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NO_ACK); // Display which does not send AC

float amps = 0;
float volts = 0;
float v = 0;
float a = 0;
float maxAmps = 0;
float minAmps = 10;
float maxVolts = 0;
float minVolts = 50;
float lastAmps = 0;
float lastVolts = 0;
float anoise = 0;
float vnoise = 0;
float ah = 0;
float watts = 0;
float whr;
float time0 = 1;
float time1 = 1;
float count = 0;

void setup(void) {
  Serial.begin(9600);

  // flip screen, if required
  // u8g.setRot180();

  // assign default color value
  if ( u8g.getMode() == U8G_MODE_R3G3B2 ) {
    u8g.setColorIndex(255);     // white
  }
  else if ( u8g.getMode() == U8G_MODE_GRAY2BIT ) {
    u8g.setColorIndex(3);         // max intensity
  }
  else if ( u8g.getMode() == U8G_MODE_BW ) {
    u8g.setColorIndex(1);         // pixel on
  }
  else if ( u8g.getMode() == U8G_MODE_HICOLOR ) {
    u8g.setHiColorByRGB(255,255,255);
  }

  pinMode(A0, INPUT);
  pinMode(A1, INPUT);
  pinMode(8, OUTPUT);
}

void loop(void) {

  if (millis() - time0 >= 100){ // fudge averaging
    time0 = millis();
    count=count+1.0;
    //a = (analogRead(A0)- 509) * 26.7 / 1023;
    //v = analogRead(A1) * 4.6 / 1023;
    a = 60; //temporary test value
    v = 10; //temporary test value
    amps = amps + a;
    volts = volts + v;
    maxAmps = max(maxAmps, a);
    minAmps = min(minAmps, a);
    anoise = (maxAmps - minAmps);
    maxVolts = max(maxVolts, v);
    minVolts = min(minVolts, v);
    vnoise = (maxVolts - minVolts);
    if (millis() - time1 >= 5000){
      amps = amps/count;
      volts = volts/count;
      watts = amps * volts;
      ah = ah + ((amps) * (millis() - time1)) / 3600000;
      whr = whr + ((watts) * (millis() - time1)) / 3600000;
      time1 = millis();
      serial();
      lcd();   
      count = 0;
      amps = 0;
      volts = 0;
      lastAmps = amps;
      lastVolts = volts;
      maxAmps = 0;
      minAmps = 10;
      maxVolts = 0;
      minVolts = 50;

    }
  }
}

void serial(void){
  Serial.print(amps);
  Serial.print("A  ");
  Serial.print(ah);
  Serial.print("Ah  ");
  Serial.print(volts);
  Serial.print("V  ");
  Serial.print(watts);
  Serial.print("W  ");
  Serial.print(whr);
  Serial.print("Whr    Noise: ");
  Serial.print(vnoise);
  Serial.print("Vn  ");
  Serial.print(anoise);
  Serial.println("An");
  Serial.print(maxVolts);
  Serial.print("Vmax  ");
  Serial.print(minVolts);
  Serial.print("Vmin  ");
  Serial.print(maxAmps);
  Serial.print("Amax  ");
  Serial.print(minAmps);
  Serial.println("Amin  ");
  Serial.println(".");
}

void lcd(void){
  // picture loop
  u8g.firstPage(); 
  do {
    draw1();
  }
  while( u8g.nextPage() );
}

void draw0(void) {
}

void draw1(void) {
  //graphic commands to redraw the complete screen should be placed here 
  //  u8g.setFontPosTop();
  u8g.setFont(u8g_font_gdr14);
  u8g.setPrintPos(0, 14);
  u8g.print(volts);
  u8g.setFont(u8g_font_unifont);
  u8g.print("V");

  u8g.setFont(u8g_font_gdr14);
  u8g.setPrintPos(66, 14);
  u8g.print(amps);
  u8g.setFont(u8g_font_unifont);
  u8g.print("A");

  u8g.setFont(u8g_font_gdr14);
  u8g.setPrintPos(0, 31);
  u8g.print(ah);
  u8g.setFont(u8g_font_unifont);
  u8g.print(" AHr");
    u8g.print((int)count);

  u8g.setFont(u8g_font_gdr14);
  u8g.setPrintPos(0, 48);
  u8g.print(watts);
  u8g.setFont(u8g_font_unifont);
  u8g.print(" watts");

  u8g.setFont(u8g_font_gdr14);
  u8g.setPrintPos(0, 64);
  u8g.print(whr);
  u8g.setFont(u8g_font_unifont);
  u8g.print(" WHr");
  u8g.print(millis()/6000.0);

}

--- End code ---
rstofer:
You're doing a lot of mixed mode arithmetic (it seems to me!).  If you want to use floating point then make certain that all values in an expression are floating point.  Float your constants or add a decimal point.

We just had a thread where mixed mode arithmetic cause substantial errors.  I would expect the compiler to promote integer constants to floating point when used with other floating point values in an evaluation but I guess we can't guarantee it.
Wimberleytech:
Ditto what rstofer said...
A good example is your use of the variable count.  It is typed as a float and in one case assigned a float and in another an int.

A counter is typically typed as an int but you may have been worried about mixed arithmetic and so you typed it as a float but were not consistent.

You could have typed it as an int and then use a cast to promote it to a float when doing an operation with a float.  Doing so saves some compute cycles.
frozenfrogz:
The discrepancy you are describing is in respect to actual data compared to a power meter or from a fed data set vs. what you calculated yourself?

If I understand correctly, you are polling every 0.1 seconds and calculating the cumulative moving average after 5 seconds.
As rstofer pointed out, feeding integer values to floating point variables might or might not work as expected.
bitseeker:
Floating point arithmetic has many gotchas including inaccuracies (potentially huge ones) and performance penalties. If possible, you'll benefit from using and operating on either integer or fixed-point values instead. For example, if you need 1/1000 resolution, convert everything into millis (ms, mV, mA, etc.), accumulate and compute on them, and convert to "normal" values only on output.
Navigation
Message Index
Next page
There was an error while thanking
Thanking...

Go to full version
Powered by SMFPacks Advanced Attachments Uploader Mod