#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);
}
#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;
float v = 0.0;
float a = 0.0;
float maxAmps = 0.0;
float minAmps = 10.0;
float maxVolts = 0.0;
float minVolts = 50.0;
float lastAmps = 0.0;
float lastVolts = 0.0;
float anoise = 0.0;
float vnoise = 0.0;
float ah = 0.0;
float watts = 0.0;
float whr = 0.0;
float time0 = 1.0;
float time1 = 1.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
time0 = millis();
count=count+1.0;
//a = (analogRead(A0)- 509) * 26.7 / 1023;
//v = analogRead(A1) * 4.6 / 1023;
a = 60.0; //temporary test value
v = 10.0; //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 >= 2000){
amps = amps/count;
volts = volts/count;
watts = amps * volts;
ah = ah + ((amps) * (millis() - time1)) / 3600000.0;
whr = whr + ((watts) * (millis() - time1)) / 3600000.0;
time1 = millis();
serial();
lcd();
count = 0.0;
amps = 0.0;
volts = 0.0;
lastAmps = amps;
lastVolts = volts;
maxAmps = 0.0;
minAmps = 10.0;
maxVolts = 0.0;
minVolts = 50.0;
}
}
}
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((long)count);
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/6000.0);
}
I had not realized that "float count=1;" could result in count being an integer.
In any operation involving a floating point type and an integer type, the integer will be converted to the real type
I had not realized that "float count=1;" could result in count being an integer.
...
...
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.
...
In any operation involving a floating point type and an integer type, the integer will be converted to the real type
»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...
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.
#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]
if (millis()>86400000){
maxAmps=0;
maxVolts=0;
}
if (millis()>86400000){
maxAmps=0;
maxVolts=0;
}
If I understand correctly, this is supposed to reset the maximum values every day. if (millis()-time2 > 86400000){
time2=millis();
maxAmps=0;
maxVolts=0;
}
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
if( currentValue-oldValue > delta )
{
oldValue = currentValue;
...rest of the code...
}
if( currentValue > oldValue+delta )
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!).if( currentValue-delta > oldValue )
(finding bad values and intervals left as an exercise for the reader)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);
...
...
#define ADC_AMPS(adc,counts) ( (float)((float) adc)*26.7/( ((float)counts)*1023.0) )
0x000 represents analog ground, and 0x3FF represents the selected reference voltage minus one LSB.
And now that we have perhaps sorted out the major things, let's go with the nitpicking
@Rick Law:
I'm not a fan of extra casts, as I prefer to make sure that the expression is correctly written in the first place.
I treat them as a strong spice in a recipe: it's has its place, in moderation .Quote#define ADC_AMPS(adc,counts) ( (float)((float) adc)*26.7/( ((float)counts)*1023.0) )The guideline to put parenthesis around a function-like macro parameter is definitely sound and correct.
Unfortunately, the code above does not do that!
Can you spot the case where the (float) cast is not applied correctly to the #define parameter?
It is, I admit, an uncommon, situation, but, it happens, and, is difficult, to spot.
Hint in the period above.
It does not make much difference here (conversion to float is guaranteed by all the other operands), but could in other contexts.
...
...
#define z x,y
with a comma operator, and that's passed as parameter, the (float) cast will only be applied to the first operand.static inline float ADC_AMPS(float adc, float amps) { return (adc*26.7f)/(counts*1024.0f); }
Clear, concise, type safe, and it is as fast as the macro in decent compiler implementations.I probably was not very clear (in trying to be cheeky), and also thought that you meant that ((float)param) was enough; after all, in the form shown, most integer expressions will work correctly and result in being cast to a float, as at least one operand will.
I apologize for the misunderstanding, I hope you did not take offence.
...
...
I think there is a good learning point from all this discussion on macros:
Macros are a powerful instrument, but making them safe is difficult and sometimes impossible. Readability is bound to suffer.
But rejoice, there's hope:
For most function-like macros, C99 (and later) provides a more powerful instrument in the form of inline functions (6.7.4, clause 6):Code: [Select]static inline float ADC_AMPS(float adc, float amps) { return (adc*26.7f)/(counts*1024.0f); }
...
...