Electronics > Beginners
Arduino Code - volts/amps/power monitor
<< < (4/10) > >>
frozenfrogz:
Just a short heads up for you, metrologist, in regard to newbrains answer: I suspect you do not fully understand how the millis() function works. It basically counts the uptime of your Arduino, where 1 tick is 1.024ms if I remember correctly. So it is just somewhat capable of pedantic time keeping (but that is just a side note) ;)
You should not try to implement millis() rollover handling but better go for rollover safe code :)
metrologist:
 :-DD I fixed this bit.


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

--- End code ---

I thought millis() was actual ms, similar to micros(), and each wavers around 4us accuracy/resolution (according to arduino time).
newbrain:

--- Quote from: frozenfrogz on March 09, 2018, 07:36:09 pm ---Just a short heads up for you, metrologist, in regard to newbrains answer: I suspect you do not fully understand how the millis() function works. It basically counts the uptime of your Arduino, where 1 tick is 1,024ms if I remember correctly. So it is just somewhat capable of pedantic time keeping (but that is just a side note) ;)
You should not try to implement millis() rollover handling but better go for rollover safe code :)

--- End quote ---
Thank for clarifying this!
It is in fact a trap for young and not-so-young players.

Since I saw that - once the correct unsigned types are used - metrologist's time comparison code is roll-over safe, I did not go in much detail.

Maybe, though, an explanation of what we mean by roll-over safe is in order.

To be roll-over safe means that even in the case a variable overflows, the involved comparison still works as expected.

Two main factors contribute to the behaviour of a comparison (such as we have here for time0 and the like) at roll-over:

* The involved variables, constants and function return values types
* The way the comparison is written.
For the first point, only unsigned integral types have defined behaviour at arithmetic overflow. Regular integer overflow has undefined behaviour.
(see 6.2.5 Clause 9 in the C11 draft).

For the second point, we must write our comparison (for a monotonically non-decreasing value):

--- Code: ---if( currentValue-oldValue > delta )
{
    oldValue = currentValue;
    ...rest of the code...
}

--- End code ---

Let's take our case as a practical example: the millis() library function returns an unsigned long int counting the ms from the system start.
We have 32 bits unsigned long int in the AVR+gcc environment (it's compiler+platform dependent) what will happen when we pass 0xFFFFFFFFu ms (about 53 days)?
Simply the function will return 0x00000000u, no worries (someone did not get this right: google "time_t 2038 problem"!).

Let's assume that our oldValue is 0xFFFFFFF0u: we have an overflow, as we are trying to subtract a large number from a small one (and obtain a positive numer)!
But this overflow is benign: thanks to the guarantees of the C standard (always be praised) the behaviour is well defined and we get the real "distance" between the two number (0x00000010u), the comparison with our delta will always work.

Would it be correct if we had written a mathematically equivalent form?
Let's try:

--- Code: ---if( currentValue > oldValue+delta )

--- End code ---
We see that if oldValue is e.g. 0xFFFFFF00u, delta 0x00000040u and currentValue has rolled over (0x00000000u) the comparison does not yield the expected result (should have been true, but it's false!).
Similarly, if we'd had:

--- Code: ---if( currentValue-delta > oldValue )

--- End code ---
(finding bad values and intervals left as an exercise for the reader)

I hope this helps metrologist grasping a deeper understanding of how C works!
ez24:
 :popcorn: interesting
Rick Law:

--- Quote from: metrologist on March 09, 2018, 06:43:21 pm ---OK, thanks all. I tried to take some of the points into account and clean up the code some.
...
...
    if (millis() - time1 >= 2000){
      amps = (float)a*26.7/(count*1023.0);
      volts = (float)v*5.0/(count*1023.0);
...
...

--- End quote ---

A further suggestion to aid debugging when needed: use a #define here, so when debugging is needed, it will likely saves a lot of debugging the debug code.

For "amps = (float)a*26.7/(count*1023.0);"

Before the loop(), I would first add a #define
#define ADC_AMPS(adc,counts)  ( (float)adc*26.7/(counts*1023.0) )

Now the line "amps = (float)a*26.7/(count*1023.0);" becomes:
   amps = ADC_AMPS(a,count);

With that, you can re-invoke the same calculation be it in debugging, or in additional features to show/print the amps, volts, whatever.

***

Now if you are comfortable with that, let me make it a little more complicated.  Sometime in debugging, you may just use a literal such as:
  testValue = ADC_AMPS(a, 1);
That would be fine, but if you do:
  testValue = ADC_AMPS(a, count+2); // add 2 for whatever reason
Now the counts in the #define is replaced with count+2 so
"a*26.7/(counts*1023.0)" becomes "a*26.7/(count+2*1023.0)"
You can see the problem there.

To fix that, and also ensure the math is done exactly the same, I put parenthesis around each argument.  I would also force the conversion right there in this case.  The extra cast may be redundant, but I found it helps me when I need to do code change.  At the very least, it reminds me to consider the variable type.

So on the right side, instead of just counts, I would use ((float) counts) and same for adc I would use ((float) adc)

#define ADC_AMPS(adc,counts)  ( (float)adc*26.7/(counts*1023.0) )
becomes:
#define ADC_AMPS(adc,counts)  ( (float)((float) adc)*26.7/( ((float)counts)*1023.0) )

I found this approach helps me a lot.  I hope this is as useful to you as it is to me.
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