Author Topic: Arduino Code - volts/amps/power monitor  (Read 7107 times)

0 Members and 1 Guest are viewing this topic.

Online newbrain

  • Super Contributor
  • ***
  • Posts: 1719
  • Country: se
Re: Arduino Code - volts/amps/power monitor
« Reply #25 on: March 11, 2018, 09:36:32 am »
Nested, conditional, complex and parameterised macros can make debugging a nightmare and require that you end up running the pre-processor to resolve them to be able to find the bugs.
And lets not talk about # and ## operators... |O


[....8< a lot of good advice >8...]

For wrapping variables you can always use the modulus operator.

wrappedCounter = wrappedCounter++ % MAX_VALUE;
:scared: :scared: :scared: :scared: :scared:
I summon the holy Clause 2 in chapter 6.5 of our sacred book(s)!
Let's chant together Annex J.2 and let its informative nature be our light and guidance!
Vade retro Satana Undefined Behaviour!
 :scared: :scared: :scared: :scared: :scared:

C2011 language has been revised, and is more precise, but much less intuitive, let's stick to C99's 6.5 (2):
Quote
Between the previous and next sequence point an object shall have its stored value
modified at most once by the evaluation of an expression.72) Furthermore, the prior value
shall be read only to determine the value to be stored.73)
Note 73):
Quote
This paragraph renders undefined statement expressions such as
    i = ++i + 1;
    a[i++] = i;
while allowing
    i = i + 1;
    a[i ] = i;

Also, the list of sequence points (in Annex C) has been (slightly) changed between C99 and C2011.

In our case, the only sequence points are the semicolon before and after our assignment expression.
wrappedCounter is clearly modified twice: once by the assignment operator, and once by the post-increment operator (same as note 73).

The ugly thing with undefined behaviour, with respect to unspecified and implementation defined ones is that there's no way to tell what the result will be.
The compiler might decide to open a portal towards hell or make you grow a purple beard (or shave you, if you already have one), and it would still be perfectly standard compliant.
The results are often more tamed (if less interesting) but they cannot be relied upon.

It is simply wrong code, stay away.

Good IDE and compilers can inform you of the wrongness of your ways, but it is not possible to catch all the cases.


Note:
Unspecified behaviour is where the standard makes no recommendation and leaves the implementation free to do thing the way best suited to the situation, with no need to document it (e.g. inlining or not a function call).
Implementation defined behaviour is where a precise choice has to be made and documented by the compiler, e.g. if plain char is signed or unsigned.

Edit: readded the bold warning. Simply writing the UB code in VS made it disappear. Or at least, that's my version of facts.
« Last Edit: March 11, 2018, 09:44:14 am by newbrain »
Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline paulca

  • Super Contributor
  • ***
  • Posts: 4046
  • Country: gb
Re: Arduino Code - volts/amps/power monitor
« Reply #26 on: March 11, 2018, 11:16:49 am »
It is simply wrong code, stay away.

So you got me on that, but it works.

I have found it not working in several languages however.

If you remove the contractions it will work however.

wrappedCounter = (wrappedCounter + 1) % MAX_VALUE;

My University tutor in C/C++ went even further and banned us all from using ++ in the first place.  That and operator overloading.
"What could possibly go wrong?"
Current Open Projects:  STM32F411RE+ESP32+TFT for home IoT (NoT) projects.  Child's advent xmas countdown toy.  Digital audio routing board.
 

Online newbrain

  • Super Contributor
  • ***
  • Posts: 1719
  • Country: se
Re: Arduino Code - volts/amps/power monitor
« Reply #27 on: March 11, 2018, 11:36:21 pm »
It is simply wrong code, stay away.

So you got me on that, but it works.
I told you! I told you!
You invoke undefined behaviour, and the next thing you know the forum goes belly up for hours...
Never underestimate the dark side of C.

On a more serious note:
It is wrong code, stay away.
The problem with that kind of code is that one cannot in any way rely on any particular result.
Of course the compiler (once you ignore the warnings) will do "something" (it could very well crash, actually), but that something is not documented, and there's no guarantee it will always be the same, from version to version, from platform to platform from compilation to compilation, and it can freely vary depending on the surrounding code!

If you remove the contractions it will work however.

wrappedCounter = (wrappedCounter + 1) % MAX_VALUE;

My University tutor in C/C++ went even further and banned us all from using ++ in the first place.  That and operator overloading.
Postfix ++ is not a contraction of +1, first because it can only be applied to modifiable lvalues, second - and the whole point here - because it modifies its operand.
Prefix ++, OTOH is shorthand for (lvalue+=1)

Hence, with the +1, that is a perfectly good C statement.

It would seem your tutor was teaching C/C++ with training wheels...maybe they should have stressed how to correctly use them.

And, now, the "working" code :horse::
 :box: I suspect your definition of 'working' and mine are radically different. :box:
Code: [Select]
#include <stdio.h>
#define MAX_VALUE 12
unsigned int wrappedCounter;

int main()
{
  for (int i = 0; i < 20; i++)
  {
    wrappedCounter = wrappedCounter++ % MAX_VALUE;
    printf("i%4d  counter %4u\n", i, wrappedCounter );
  }
  return 0;
}

Result on Raspberry Pi 3, gcc 6.3.0 (same on X86 Ubuntu 16.04, gcc 5.4.0):
Code: [Select]
newbrain@tritium:~ $ ./a.out
i   0  counter    0
i   1  counter    0
i   2  counter    0
i   3  counter    0
i   4  counter    0
i   5  counter    0
i   6  counter    0
i   7  counter    0
...ok you get the gist...
Surprising isn'it?

The Microsoft compiler gives an incrementing counter from 1 to 12.
I seem remember that older gcc (3.??) had a similar behaviour (it did not return all zeros).
I don't know whether you would consider this correct: I personally cannot meaningfully parse that statement.

Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline paulca

  • Super Contributor
  • ***
  • Posts: 4046
  • Country: gb
Re: Arduino Code - volts/amps/power monitor
« Reply #28 on: March 12, 2018, 07:38:00 am »
It would seem your tutor was teaching C/C++ with training wheels...maybe they should have stressed how to correctly use them.

One interesting reason was the question...

"And what if "i" which you "i++" turns out not to be an int and turns out to be macro?"

Of course the answer depends on the macro, but the point he was making is it could result in one of C++s less than friendly errors.

Anyway, he had just seen so many mistakes using it.  He didn't even like single character variable names for loop indexes. He actually stipulated that we should use this style for for loops:

for( int Index=0; Index < MAX; Index=Index+1; ) {//...}

My own little mistake here suggests I've been away from C/C++ too long and little adventures in arduino don't really count.
"What could possibly go wrong?"
Current Open Projects:  STM32F411RE+ESP32+TFT for home IoT (NoT) projects.  Child's advent xmas countdown toy.  Digital audio routing board.
 

Online metrologistTopic starter

  • Super Contributor
  • ***
  • Posts: 2200
  • Country: 00
Re: Arduino Code - volts/amps/power monitor
« Reply #29 on: March 12, 2018, 05:01:38 pm »
I was just trying to add SD card logging and work in some points mentioned here - the ones I could understand anyway ... :-DD

I ran out of room - removing the other font from the LCD screen prints got back 5k of space. Later I want to explore another light sensor (may not be needed) and control at least a one axis motor to keep the panel pointed at the sun.

Code: [Select]
/*
  SD card datalogger
 
 This example shows how to log data from three analog sensors
 to an SD card using the SD library.
 
 The circuit:
 * analog sensors on analog ins 0, 1, and 2
 * SD card attached to SPI bus as follows:
 ** MOSI - pin 11
 ** MISO - pin 12
 ** CLK - pin 13
 ** CS - pin 4
 
 created  24 Nov 2010
 modified 9 Apr 2012
 by Tom Igoe
 
 This example code is in the public domain.
 
 */

#include <SD.h>

// On the Ethernet Shield, CS is pin 4. Note that even if it's not
// used as the CS pin, the hardware CS pin (10 on most Arduino boards,
// 53 on the Mega) must be left as an output or the SD library
// functions will not work.
const int chipSelect = 4;



#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.0;
float maxVolts = 0.0;
float minAmps = 100.0;
float minVolts = 100.0;
float ah = 0.0;
float watts = 0.0;
float whr = 0.0;
unsigned long time0 = 0;
unsigned long time1 = 0;
unsigned long time2 = 0;
float count = 0.0;


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

  Serial.print("Initializing SD card...");
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:
  pinMode(4, OUTPUT);

  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("card initialized.");

  // 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);

  delay(300);
}

void loop() {
  if (millis() - time0 >= 10) adc(); // fudge averaging by controlling sample rate
  if (millis() - time1 >= 1000) tally(); // compute result on interval
}

void adc(){
  time0 = millis();
  count++;
  a = a+analogRead(A0)-509;
  v = v+analogRead(A1);
}
void tally(){
  amps = (float)a*29.69/(count*1023.0); //26.7
  volts = (float)v*142.94/(count*1023.0); //5.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);
  minAmps = min(minAmps, amps);
  minVolts = min(minVolts, volts);
  serial();
  sdc();
  lcd();
  count = 0.0;
  a = 0;
  v = 0;

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

void serial(){
  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(minVolts);
  Serial.print("\tVmin\t");
  Serial.print(maxAmps);
  Serial.print("\tAmax\t");
  Serial.print(minAmps);
  Serial.print("\tAmin\t");
  Serial.print(time1/60000.0);
  Serial.println("\tmin\t");
}

void sdc(){
  File dataFile = SD.open("datalog.txt", FILE_WRITE);
  // if the file is available, write to it:
  if (dataFile) {
    dataFile.print(count);
    dataFile.print("\taverages\t");
    dataFile.print(volts);
    dataFile.print("\tV\t");
    dataFile.print(amps);
    dataFile.print("\tA\t");
    dataFile.print(ah);
    dataFile.print("\tAh\t");
    dataFile.print(watts);
    dataFile.print("\tW\t");
    dataFile.print(whr);
    dataFile.print("\tWhr\t");
    dataFile.print(maxVolts);
    dataFile.print("\tVmax\t");
    dataFile.print(minVolts);
    dataFile.print("\tVmin\t");
    dataFile.print(maxAmps);
    dataFile.print("\tAmax\t");
    dataFile.print(minAmps);
    dataFile.print("\tAmin\t");
    dataFile.print(time1/60000.0);
    dataFile.println("\tmin\t");
    dataFile.close();
  }
}

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

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

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

  u8g.setPrintPos(0, 31);
  u8g.print(ah);
  u8g.print("AHr");

  u8g.setPrintPos(0, 48);
  u8g.print(watts);
  u8g.print("W ");
  u8g.print(maxAmps*maxVolts);

  u8g.setPrintPos(0, 64);
  u8g.print(whr);
  u8g.print("WHr ");
  u8g.print(time1/60000.0);
}
 

Offline paulca

  • Super Contributor
  • ***
  • Posts: 4046
  • Country: gb
Re: Arduino Code - volts/amps/power monitor
« Reply #30 on: March 12, 2018, 07:52:00 pm »
Much better.  A few points.

This might not do what you think it does:
Code: [Select]
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }

It will just return from the setup() method and the application will proceed onto the loop() anyway.  I'm not sure how exactly you would call "halt()" in Arduino, but you could do:

while(1){ delay(10000); }

Or better yet...

while(1){ digitalWrite(LED_BUILTIN, HIGH); delay(100); digitalWrite(LED_BUILTIN, LOW); delay(250);}

To signal "error" until the board is reset.

Alternatively, you can retry the card.  Just wrap the test:

Code: [Select]
  while (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    digitalWrite(LED_BUILTIN, HIGH); delay(100); digitalWrite(LED_BUILTIN, LOW); delay(250);
  }

Then it will wait until you insert the card.

Code: [Select]
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(minVolts);
  Serial.print("\tVmin\t");
  Serial.print(maxAmps);
  Serial.print("\tAmax\t");
  Serial.print(minAmps);
  Serial.print("\tAmin\t");
  Serial.print(time1/60000.0);
  Serial.println("\tmin\t");
Becomes:

Code: [Select]
char buffer[1024];
memset( buffer, 0, 1024 );
snprintf( buffer, 1024, "%d \t averages %.2fV %.2fA %.2fAh %2.fW %.2fWhr %.2fVmax %.2fVmin %.2fAmax %.2fAmin [%d] minutes",
              count, volts, amps, ah, watts, whr, maxVolts, minVolts, maxAmps, minAmps, time1/60000);
Serial.println(buffer);

Or similar.  I didn't test compile that, but basically....

%d  - Integer value
%.2f - Decimal value with 2 decimal places.

For each "token" you place the value after the string in a list.

The only concern with regards an MCU here is you could run out of memory, allocating 1k of memory for the string to built up is nothing on a PC but I believe the ATMega 328p only has 8k total.  Of course if you work out the maximum length of the string to only be 256, then you can use a shorter buffer.

You can do the same for printing to the SD card, but I would recommend you use CSV format, then you can import the file into Excel or similar and graph it.

Note that as you use the same, more or less, print statement in two places, you could make it a function.  You will need to create the memory "buffer" in the common parent and pass the pointer to the string creation function and the two functions (serial and sdc) that use it.  Or just  put the

char buffer[512];  // or 256 if you can fit the string into it.

As a global.  Don't forget to memset it to 0 each time though ;)
"What could possibly go wrong?"
Current Open Projects:  STM32F411RE+ESP32+TFT for home IoT (NoT) projects.  Child's advent xmas countdown toy.  Digital audio routing board.
 

Offline frozenfrogz

  • Frequent Contributor
  • **
  • Posts: 936
  • Country: de
  • Having fun with Arduino and Raspberry Pi
Re: Arduino Code - volts/amps/power monitor
« Reply #31 on: March 12, 2018, 08:14:32 pm »
Code: [Select]
char buffer[1024];
memset( buffer, 0, 1024 );
snprintf( buffer, 1024, "%d \t averages %.2fV %.2fA %.2fAh %2.fW %.2fWhr %.2fVmax %.2fVmin %.2fAmax %.2fAmin [%d] minutes",
              count, volts, amps, ah, watts, whr, maxVolts, minVolts, maxAmps, minAmps, time1/60000);
Serial.println(buffer);
Or similar.  I didn't test compile that, but basically....

%d  - Integer value
%.2f - Decimal value with 2 decimal places.

You wish!

Suggesting to use printf or sprintf to a buffer was also my initial thought. Needing hundreds of Serial.print() lines just to get some barely formatted text totally bugs me every time. Arduino sucks big time in that regard!
The printf() method is not implemented in the Serial class, so Serial.printf() will not compile.

In addition, sprintf() on Arduino does not understand doubles (%f %e %g). I read somewhere that this is on purpose, because of memory limitations of the smaller Atmegas.

To make some use of the above, you need to use dtostrf from the stdlib:
Code: [Select]
#include<stdlib.h>
dtostrf(FLOAT,WIDTH,PRECISION,BUFFER);

If there is a better way, I would love to hear it :)
Still trying to get my head around all of these Arduino »specialties«.

Might be another argument against using floats.
He’s like a trained ape. Without the training.
 

Offline paulca

  • Super Contributor
  • ***
  • Posts: 4046
  • Country: gb
Re: Arduino Code - volts/amps/power monitor
« Reply #32 on: March 12, 2018, 08:39:59 pm »
In addition, sprintf() on Arduino does not understand doubles (%f %e %g). I read somewhere that this is on purpose, because of memory limitations of the smaller Atmegas.

Aww balls.  I know I've used it before, but not sure if I have used it with %f.   Aww well, it's the thought that counts. :)  You could always do:

"%d.%d", value, value*100%100

or something similarly cheesy.

I went hunting  for where I was formatting decimal output and sure enough; dtostrf:

Code: [Select]
  sensorValue = analogRead(A1);
  vOut = (sensorValue * AREF) / 102.4;
  memset(buffer,0, 16); 
  dtostrf(vOut,5,1,buffer);
  oled.print("Is: "); oled.print(buffer); oled.print("A"); oled.clearToEOL(); oled.println();

« Last Edit: March 12, 2018, 08:45:15 pm by paulca »
"What could possibly go wrong?"
Current Open Projects:  STM32F411RE+ESP32+TFT for home IoT (NoT) projects.  Child's advent xmas countdown toy.  Digital audio routing board.
 

Online metrologistTopic starter

  • Super Contributor
  • ***
  • Posts: 2200
  • Country: 00
Re: Arduino Code - volts/amps/power monitor
« Reply #33 on: March 12, 2018, 08:53:14 pm »
I started off reading fixed point math and that will have to wait.

One idea I had was to build a string and then send that to the serial and SD ports when needed.

String data = "";
data += count;
data += "\tavegs\t";
data += volts;
data += "\tV\t";
...
(that ^ wipes out my remaining program space)


I also implemented the below, but not because I understand it...


#define ADC_AMPS(a,count)  (((float)((float)a)*26.7)/(((float)count)*1024.0));
#define ADC_VOLTS(v,count) (((float)((float)v)*50.0)/(((float)count)*1024.0));
...
amps = ADC_AMPS(a,count);
volts = ADC_VOLTS(v,count);

Now, about 1023. I read more and suspect a routine could be applied. One suggesting is to simply add half a bit to the adc reading, or perhaps with k in the following a variable with value based on some factor.

V = (analogRead(pin) + k) * AREF / 1024;
with k of
0 for floor
0.5 for mean
1 for ceiling
« Last Edit: March 12, 2018, 08:56:05 pm by metrologist »
 

Offline paulca

  • Super Contributor
  • ***
  • Posts: 4046
  • Country: gb
Re: Arduino Code - volts/amps/power monitor
« Reply #34 on: March 12, 2018, 09:00:59 pm »
String data = "";
data += count;
data += "\tavegs\t";
data += volts;
data += "\tV\t";

This works in Java, but it will only work in C++ if you use STL string class and it is Satan's niece.  Not because it's evil, but when it goes wrong it gives you errors like these:
https://codegolf.stackexchange.com/questions/1956/generate-the-longest-error-message-in-c

String streams are another option.
http://www.dreamincode.net/forums/topic/95826-stringstream-tutorial/

Not sure of AVR compat though.
"What could possibly go wrong?"
Current Open Projects:  STM32F411RE+ESP32+TFT for home IoT (NoT) projects.  Child's advent xmas countdown toy.  Digital audio routing board.
 

Online Rick Law

  • Super Contributor
  • ***
  • Posts: 3441
  • Country: us
Re: Arduino Code - volts/amps/power monitor
« Reply #35 on: March 14, 2018, 02:57:52 am »
I started off reading fixed point math and that will have to wait.

One idea I had was to build a string and then send that to the serial and SD ports when needed.

String data = "";
data += count;
...
(that ^ wipes out my remaining program space)
...
...
I also implemented the below, but not because I understand it...

re: "I also implemented the below, but not because I understand it..."
Actually, I would suggest not to add anything that you don't understand.  It is your program so you need to understand the content and the reason(s) of why certain code is there.
I suggested #define there really for in case you do it again elsewhere such as in debug-print of info.  It would be more harm then good to have things you don't really understand.  Wait till you understand it and make the decision on if it is worthwhile.

re: "(that ^ wipes out my remaining program space)"
Three things that may help you get space back
(1) Check your IDE/C++ compiler and see if it have a flag to optimize space utilization.  If I recall right, gcc (used in Arduino) has -Os to "optimize for size".

(2) Initialization of variables takes a good chuck of space.  If you don't really need that, don't initialize it.

(3) Your Serial.print is killing you.  You have a load of them!  Paulca's suggestion (to solve another issue) in using snprintf (reply #30 in this thread) may be of help to solve that other issue and change the situation here, but I doubt you would have the space for it since you are running out now.
...
char buffer[1024];
memset( buffer, 0, 1024 );
snprintf( buffer, 1024, "%d \t averages %.2fV %.2fA %.2fAh %2.fW %.2fWhr ...
...
Your serial print appears to be always printing a value then a literal such as this snip I took from your code:
  void serial(void){
    Serial.print(amps);
    Serial.print("A  ");
    Serial.print(ah);
    Serial.print("Ah  ");
You have a lot of such pairs, so you will save a lot of space by creating a subroutine that print a pair like this:
   void serialPrintPair(float value,char *label) {
      Serial.print(value);
      Serial.print(label);
   }
Now for each such pair of calls, you reduced it to a single call.  That will save you a chunk.  Adding a function do add some bytes-used.  So it takes a few pairs to first pay for the added overhead of having a new function.  After paying the overhead, the rest is pure gravy.

(4) Note if there is a generated byte count displayed after compilation.  Arduino IDE does that (if indeed you are using that IDE).   If you want more information about what use what, there may be a way with your IDE to generates listing files.  When I was coding for ATTINY 13 with gcc (directly without IDE), the assembler listing file was critical in help me fit things into 1K space.  I don't know what is your IDE.  Do some research and see if/how you can grab the listing file.  If all you want out of it is to manage space, you don't have to understand the details of the code generated - you just note the space generated for that line of source.
 

Online metrologistTopic starter

  • Super Contributor
  • ***
  • Posts: 2200
  • Country: 00
Re: Arduino Code - volts/amps/power monitor
« Reply #36 on: March 14, 2018, 09:05:22 pm »
I'd rather understand the define than avoid it as it looks like it will have a lot of utility.

I also tried creating a float array and string array rather than individual variables. That was so I could loop an index for both serial and SD card outputs. That made the code more difficult to understand and it actually made the compiled code a little larger. I selected a reduced font set and I'm back down to ~23kB.
 

Online Rick Law

  • Super Contributor
  • ***
  • Posts: 3441
  • Country: us
Re: Arduino Code - volts/amps/power monitor
« Reply #37 on: March 15, 2018, 04:37:51 am »
I'd rather understand the define than avoid it as it looks like it will have a lot of utility.
...

The way #define is used here is simple to understand so let's see if I can present it as so for you.

#define is a way to ask the pre-compiler part of the compile process to do text replacement/modification.  The pre-compiler hands over a modified source file to the compiler to compile.

// ====================================
// Simple case:
// ====================================
In its simplest form:
#define myDigitalRead digitalReadV2
Every time when it sees the word myDigitalRead in your source (other than comments), it is replaced with the word digitalReadV2

Imagine you have these functions in your code:
    digitalReadV1(value1, value2...) // functioning old version
    digitalReadV2(value1, value2...) // working on this new version
and elsewhere in the codes, you have many calls it to all over the place:
   result = myDigitalRead(something, something...)
All occurrences will turn into:
   result = digitalReadV2(something, something...)

Now, if you want to call the old function again, all you have to do is change the #define statement to:
#define myDigitalRead digitalReadV1
Now all occurrences will turn into:
   result = digitalReadV1(something, something...)


// -----------------------------------------------------------
// I have two type of I2C with different addresses and size
// and the code supports them
#define LCDxSize 20
#define LCDySize 4
#define LCDAddress 0xfb  // I2C address of current LCD installed
// with all my codes using the #defined value above, I can change LCD without
// hunting all over my code when I switch LCD

// ====================================
// Now more complicated #define with parameter:
// ====================================


#define myDoublePrint(label,value)      Serial.print(label);Serial.print(value);
// now, you can just use myDoublePrint and the pre-compiler will convert that
// into two Serial.print for you.
// This would be a good way to compare memory use by make double calls verses
// changing the #define and make it call a function that do the two printing.

//
// ----------- Example: -----------
// make the choice by commenting out one of the #define below
#define myDoublePrint(label,value)      { Serial.print(label);Serial.print(value); }
#define myDoublePrint(label,value)      myNewFunction((label),(value))
   void myNewFunction(char *label,float value) {
      Serial.print(label);Serial.print(value);
   }
//-------------------------------------------------------

Hope that helps you understand simple #define.  If it does, you may even use
#if def(something)
   some codes here in between
#endif
And then there are more complicated ways of using #define to achieve more...  When you feel comfortable with using #define, lots of fun to research what else can be done

By the way, which IDE are you using?
« Last Edit: March 15, 2018, 04:40:56 am by Rick Law »
 

Online metrologistTopic starter

  • Super Contributor
  • ***
  • Posts: 2200
  • Country: 00
Re: Arduino Code - volts/amps/power monitor
« Reply #38 on: March 15, 2018, 02:49:00 pm »
Thanks, Rick. I had thought of #define as a constant and typically saw it used to define a led pin#, but I see how it's being used now.

I am using the Arduino IDE 1.06 and often Notepad++.
 

Online Rick Law

  • Super Contributor
  • ***
  • Posts: 3441
  • Country: us
Re: Arduino Code - volts/amps/power monitor
« Reply #39 on: March 15, 2018, 08:17:44 pm »
Thanks, Rick. I had thought of #define as a constant and typically saw it used to define a led pin#, but I see how it's being used now.

I am using the Arduino IDE 1.06 and often Notepad++.

You are welcome.  Since you are using Arduino, like most library and IDE, #define is also use to control what source codes are applicable.  For example, things like which register and which bits are used for, say, controlling PWM are typically #defined somewhere.

It will be helpful if you look into #if, #elif, and #endif along with #if def() and #if ndef().  When you have some understanding of those pre-processor commands, if will help you understand Arduino headers files.  You can make out more of what they do.
 

Online metrologistTopic starter

  • Super Contributor
  • ***
  • Posts: 2200
  • Country: 00
Re: Arduino Code - volts/amps/power monitor
« Reply #40 on: March 21, 2018, 03:32:49 pm »
So, I was having trouble with the SD card writes stopping after 7 or so hours. I slowed down the SD card writes with more averaging of the logged data.

Code: [Select]
/*
  SD card datalogger
 * SD card attached to SPI bus as follows:
 ** MOSI - pin 11
 ** MISO - pin 12
 ** CLK - pin 13
 ** CS - pin 4
 
 created  24 Nov 2010
 modified 9 Apr 2012
 by Tom Igoe
 
 This example code is in the public domain.
 
 */

#include <SD.h>

// On the Ethernet Shield, CS is pin 4. Note that even if it's not
// used as the CS pin, the hardware CS pin (10 on most Arduino boards,
// 53 on the Mega) must be left as an output or the SD library
// functions will not work.
const int chipSelect = 4;



#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 count = 0.0;
float volts = 0.0;
float minVolts = 100.0;
float maxVolts = 0.0;
float amps = 0.0;
float minAmps = 100.0;
float maxAmps = 0.0;
float ah = 0.0;
float watts = 0.0;
float whr = 0.0;

long v = 0;
long a = 0;
unsigned long time0 = 0;
unsigned long time1 = 0;
unsigned long time2 = 0;
unsigned long time3 = 0;

#define ADC_AMPS(a,count)  (((float)((float)a)*26.7)/(((float)count)*1024.0));
#define ADC_VOLTS(v,count) (((float)((float)v)*52.6)/(((float)count)*1024.0));

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

  Serial.print("Initializing SD card...");
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:
  pinMode(4, OUTPUT);

  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("card initialized.");

  // 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);

  delay(300);
}

void loop() {
  if (millis() - time0 >= 100) adc(); // set adc sample rate
  if (millis() - time1 >= 1000) tally(); // compute average adc result on interval
}

void adc(){
  time0 = millis();
  count++;
  a = a+analogRead(A0)-509;
  v = v+analogRead(A1);
}

void tally(){
  amps = ADC_AMPS(a,count);
  volts = ADC_VOLTS(v,count);
  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);
  minAmps = min(minAmps, amps);
  minVolts = min(minVolts, volts);
  lcd();
  if (millis() - time3 >= 10000){
    time3 = millis();
    serial();
    sdc();
    count = 0.0;
    a = 0;
    v = 0;
  }

  if (millis()-time2 >= 86400000){
    time2=millis();
    maxAmps=0.0;
    maxVolts=0.0;
  }
}

void serial(){
  Serial.print(count);
  Serial.print(",averages,");
  Serial.print(minVolts);
  Serial.print(",Vmin,");
  Serial.print(volts);
  Serial.print(",V,");
  Serial.print(maxVolts);
  Serial.print(",Vmax,");
  Serial.print(minAmps);
  Serial.print(",Amin,");
  Serial.print(amps);
  Serial.print(",A,");
  Serial.print(maxAmps);
  Serial.print(",Amax,");
  Serial.print(ah);
  Serial.print(",Ah,");
  Serial.print(watts);
  Serial.print(",W,");
  Serial.print(whr);
  Serial.print(",Whr,");
  Serial.print(time1/60000.0);
  Serial.println(",min");
}

void sdc(){
  File dataFile = SD.open("datalog.txt", FILE_WRITE);
  // if the file is available, write to it:
  if (dataFile) {
    dataFile.print(count);
    dataFile.print(",averages,");
    dataFile.print(minVolts);
    dataFile.print(",Vmin,");
    dataFile.print(volts);
    dataFile.print(",V,");
    dataFile.print(maxVolts);
    dataFile.print(",Vmax,");
    dataFile.print(minAmps);
    dataFile.print(",Amin,");
    dataFile.print(amps);
    dataFile.print(",A,");
    dataFile.print(maxAmps);
    dataFile.print(",Amax,");
    dataFile.print(ah);
    dataFile.print(",Ah,");
    dataFile.print(watts);
    dataFile.print(",W,");
    dataFile.print(whr);
    dataFile.print(",Whr,");
    dataFile.print(time1/60000.0);
    dataFile.println(",min");
    dataFile.close();
  }
}

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

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

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

  u8g.setPrintPos(0, 31);
  u8g.print(ah);
  u8g.print("AHr");

  u8g.setPrintPos(0, 48);
  u8g.print(watts);
  u8g.print("W ");
  u8g.print(maxAmps*maxVolts);

  u8g.setPrintPos(0, 64);
  u8g.print(whr);
  u8g.print("WHr ");
  u8g.print(time1/60000.0);
}
 

Offline paulca

  • Super Contributor
  • ***
  • Posts: 4046
  • Country: gb
Re: Arduino Code - volts/amps/power monitor
« Reply #41 on: March 21, 2018, 05:07:00 pm »
What is the maximum file size?  Will probably be found in the SDC driver/library documentation.
"What could possibly go wrong?"
Current Open Projects:  STM32F411RE+ESP32+TFT for home IoT (NoT) projects.  Child's advent xmas countdown toy.  Digital audio routing board.
 

Online metrologistTopic starter

  • Super Contributor
  • ***
  • Posts: 2200
  • Country: 00
Re: Arduino Code - volts/amps/power monitor
« Reply #42 on: March 21, 2018, 06:33:43 pm »
I read the file size is limited by the FAT file system, or around 4GB.

My file had around 30k records (lines of data) and was about 2800kB.

I think that if there is any glitch to the SD card, the bus shuts down. At least it will not write to the card if it's removed and reinserted. It needs to restart the SD Card bus in Setup(), so it won't recover. I hope slowing down the data logs to about 6 per minute will alleviate the symptom.
 

Online metrologistTopic starter

  • Super Contributor
  • ***
  • Posts: 2200
  • Country: 00
Re: Arduino Code - volts/amps/power monitor
« Reply #43 on: April 18, 2018, 11:58:08 pm »
Your serial print appears to be always printing a value then a literal such as this snip I took from your code:
  void serial(void){
    Serial.print(amps);
    Serial.print("A  ");
    Serial.print(ah);
    Serial.print("Ah  ");
You have a lot of such pairs, so you will save a lot of space by creating a subroutine that print a pair like this:
   void serialPrintPair(float value,char *label) {
      Serial.print(value);
      Serial.print(label);
   }

If I implemented that correctly, I saved ~80 bytes converting 22 serial print calls into 11 serialprintpair function calls. I will do the same for the SD card writes, and that is still presenting a problem for me with stopping logging after a few days, around 2-3MB. Maybe the consecutive writes are happening too fast and hanging the card? Or I should try a different card. I'm using Kingston 2GB micro SD in an adapter. Oh, and I just read about using SDFormatter instead of Windows so am trying that now...I've added an error counter if the file is not. I also moved my CS pin to 10, rather that what was specified in the header notes. I also downloaded the SDfat lib that I'll try if this is still failing.


Code: [Select]
void sdc(){
  time3 = millis();
  File dataFile = SD.open("datalog.txt", FILE_WRITE);
  // if the file is available, write to it:
  if (dataFile) {
    dataFile.print(count);
    dataFile.print(",averages,");
    dataFile.print(minVolts);
    dataFile.print(",Vmin,");
    dataFile.print(volts);
    dataFile.print(",V,");
    dataFile.print(maxVolts);
    dataFile.print(",Vmax,");
    dataFile.print(minAmps);
    dataFile.print(",Amin,");
    dataFile.print(amps);
    dataFile.print(",A,");
    dataFile.print(maxAmps);
    dataFile.print(",Amax,");
    dataFile.print(ah);
    dataFile.print(",Ah,");
    dataFile.print(watts);
    dataFile.print(",W,");
    dataFile.print(whr);
    dataFile.print(",Whr,");
    dataFile.print(time1/60000.0);
    dataFile.println(",min");
    dataFile.close();
    err = 0;
  }
  err=err+1;
}


void serial(){
  serialPrintPair(count,",averages,");
  serialPrintPair(minVolts,",Vmin,");
  serialPrintPair(volts,",V,");
  serialPrintPair(maxVolts,",Vmax,");
  serialPrintPair(minAmps,",Amin,");
  serialPrintPair(amps,",A,");
  serialPrintPair(maxAmps,",Amax,");
  serialPrintPair(ah,",Ah,");
  serialPrintPair(watts,",W,");
  serialPrintPair(whr,",Whr,");
  serialPrintPair(time1/60000.0,",min");
}

void serialPrintPair(float value,char *label) {
  Serial.print(value);
  Serial.print(label);
}
« Last Edit: April 19, 2018, 03:34:21 am by metrologist »
 

Offline paulca

  • Super Contributor
  • ***
  • Posts: 4046
  • Country: gb
Re: Arduino Code - volts/amps/power monitor
« Reply #44 on: April 19, 2018, 06:48:55 am »
I have had a few devices that stall on SD card errors, rather than just retrying.  SD card writers are supposed to skip over errored sectors, marking them as such, but my dash cam for one get's hung up on errored sectors and goes into a loop speaking to me, "Please check the SD card, Please check the SD card, Please check the SD card".  If I depower and repower it, it goes back to normal.  I changed the card and this finally stopped.
"What could possibly go wrong?"
Current Open Projects:  STM32F411RE+ESP32+TFT for home IoT (NoT) projects.  Child's advent xmas countdown toy.  Digital audio routing board.
 

Online metrologistTopic starter

  • Super Contributor
  • ***
  • Posts: 2200
  • Country: 00
Re: Arduino Code - volts/amps/power monitor
« Reply #45 on: April 20, 2018, 08:07:52 pm »
yes, indications are my actions are no help. I was getting errors right off. I reset a couple times and it was still logging after a day. I found an SD Mini card I may solder to a level shifter and go with it. I read resistive dividers on the signal lines is not acceptable for SD cards. I think that is what my LC Soft SD shield has, along with a 3.3V regulator.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf