Electronics > Beginners
Arduino Code - volts/amps/power monitor
<< < (3/10) > >>
Rick Law:

--- Quote from: metrologist on March 08, 2018, 11:20:57 pm ---I had not realized that "float count=1;" could result in count being an integer.
...

--- End quote ---

I don't think it results in count being integer.  count is float as declared.  But your ="1" is integer.  So compiler would convert 1 to float (1.0) to initialize.  However, depending on compiler, the compiler may not convert the same way the run-time library does.

Single number converts reasonably well but when your initialization value is from an expression, result may be a rather unexpected value.
 

--- Quote from: metrologist on March 08, 2018, 11:20:57 pm ---...
I think my error was where I divided millis()/6000.0 as that blows my time interval. I should have used time1/6000.0. The error was not accumulating.

Yes, I am polling sensors every 0.1 and adding the values until 2 seconds of data, then I find the average. But, I am not actually polling sensors yet as I wanted to ensure my averaging was going to work correctly, so I just use fixed data that works out math-wise with a simple counter.

I think I corrected all the concerns.
...

--- End quote ---

At 0.1 second interval for 2 seconds, you have only 20 samples.  If you want to ensure minimal precision lost in accumulation, rather than accumulating an evaluated Volt/mA/whatever, why not accumulate the ADC value in integer and do the conversion at display or averageCalculation time.

With 20 samples of up to 10 bits each, unsigned_int16 will give you 2**6 samples (64 samples) without overflow.  When you get your 20 sample, that is when you assign your adc_int_total to adc_float_total then do all float arithmetic with only adc_float_total.

If you decide to use much more than 20 samples, use int_32.  You can have 2**22 samples of 10 bits each before your adc_total overflows.



newbrain:

--- Quote from: frozenfrogz on March 09, 2018, 12:15:09 am ---
--- Quote from: newbrain on March 09, 2018, 12:05:34 am ---In any operation involving a floating point type and an integer type, the integer will be converted to the real type

--- End quote ---

»If doing math with floats, you need to add a decimal point, otherwise it will be treated as an int. See the Floating point constants page for details.«

Taken from the Arduino reference.

Edit: The Arduino reference - as always - is unspecific AF. So who knows, what’s really happening when mixing int and float... :scared:

--- End quote ---
The contents of that page are horrible: both unclear and misleading.
Frankly, between the Arduino website and an ISO standard, I know where to put my trust  :horse:
The gcc compiler used in Arduino is standard conforming enough not to play tricks on this simple stuff.
Note: I wrongly referenced a C standard, rather than then a C++ one, but the behaviour for built-in types is the same in this context, as are the contents of <float.h>.


--- Quote ---Floating point numbers are not exact, and may yield strange results when compared. For example 6.0 / 3.0 may not equal 2.0. You should instead check that the absolute value of the difference between the numbers is less than some small number.
[...8<...]
If doing math with floats, you need to add a decimal point, otherwise it will be treated as an int. See the Floating point constants page for details.

--- End quote ---
The first paragraph above gives correct advice (compare with a small delta), but if you are able to find a floating point implementation that gets 6.0/2.0 wrong, I'll pay you a beer (or other beverage of your choice).
It is true that the standard does not promise much about precision, but any decent FP implementation will give the expected result with integers that are exactly representable in the float (one would really need to go out of their way to have an imprecise result...).

That second paragraph is so lacking in context to be meaningless.
First of all, it switched from talking about the semantic and behaviour of floating point values (e.g. as stored in a variable) to describing a specific syntax accident in float literals (i.e. how you inform the compiler that a literal is FP).
That said, it's also very misleading, as an integer number literal will be happily converted to float in binary operation if the other  operand is a float, and operator overloading is not an option for built-in types.

So "who knows..." is only true if one relies on dodgy documentation - this is not to say that the matter is not confusing for a newcomer!

Finally  :blah:, my personal preference is very often fixed point, as bitseeker suggests, when doing simple arithmetic.
Possibly, I'll add one or two extra digits to help rounding.
metrologist:
OK, thanks all. I tried to take some of the points into account and clean up the code some.


--- 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.0;
float volts = 0.0;
long v = 0;
long a = 0;
float maxAmps = 0;
float maxVolts = 0;
float ah = 0.0;
float watts = 0.0;
float whr = 0.0;
long time0 = 0;
long time1 = 0;
float count = 0.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 by controlling sample rate
    time0 = millis();
    count++;
    a = a+analogRead(A0)-509; //sensor reads +/- with 0 near midpoint of adc
    v = v+analogRead(A1);

    if (millis() - time1 >= 2000){
      amps = (float)a*26.7/(count*1023.0);
      volts = (float)v*5.0/(count*1023.0);
      watts = amps * volts;
      ah = ah + ((amps) * (millis() - time1)) / 3600000.0;
      whr = whr + ((watts) * (millis() - time1)) / 3600000.0;
      time1 = millis();
      maxAmps = max(maxAmps, amps);
      maxVolts = max(maxVolts, volts);
      serial();
      lcd();   
      count = 0.0;
      a = 0;
      v = 0;
    }
  }

  if (millis()>86400000){
    maxAmps=0;
    maxVolts=0;
  }
}

void serial(void){
  Serial.print(count);
  Serial.print("\taverages\t");
  Serial.print(volts);
  Serial.print("\tV\t");
  Serial.print(amps);
  Serial.print("\tA\t");
  Serial.print(ah);
  Serial.print("\tAh\t");
  Serial.print(watts);
  Serial.print("\tW\t");
  Serial.print(whr);
  Serial.print("\tWhr\t");
  Serial.print(maxVolts);
  Serial.print("\tVmax\t");
  Serial.print(maxAmps);
  Serial.print("\tAmax\t");
  Serial.print(time1/60000.0);
  Serial.println("\tmin\t");
}

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(maxAmps*maxVolts);

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

  u8g.setFont(u8g_font_gdr14);
  u8g.setPrintPos(0, 64);
  u8g.print(whr);
  u8g.setFont(u8g_font_unifont);
  u8g.print("WHr ");
  u8g.print(time1/60000.0);
}
[\code]
--- End code ---
frozenfrogz:
Your vars time0/1 should be type «unsigned long» and not «long» since you are going to assign millis() to them (see above).

Also, this does not catch your overflow in a sane way - or I simply do not understand what you want to do here.


--- Quote ---if (millis()>86400000){
    maxAmps=0;
    maxVolts=0;
  }
--- End quote ---

I’d like to take a closer look at your calculations and propose what I would do, but that will have to wait a couple of hours :)
newbrain:
Looks better! :-+
Use unsigned long int or uint32_t for the counters and times, as that guarantees you the correct behaviour together with millis().
Some redundant cast and parenthesis, but no major problem at first glance.


--- Code: ---  if (millis()>86400000){
    maxAmps=0;
    maxVolts=0;
  }

--- End code ---
If I understand correctly, this is supposed to reset the maximum values every day.
It will work...a bit aggressively >:D: after one day the values will be kept to 0, as the test will always be verified.

To achieve your goal, implement a mechanism similar to the one used with time0 and time1.
Navigation
Message Index
Next page
Previous page
There was an error while thanking
Thanking...

Go to full version
Powered by SMFPacks Advanced Attachments Uploader Mod