| Electronics > Beginners |
| Arduino Code - volts/amps/power monitor |
| << < (6/10) > >> |
| newbrain:
--- Quote from: paulca on March 11, 2018, 08:07:30 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. --- End quote --- And lets not talk about # and ## operators... |O --- Quote from: paulca on March 11, 2018, 08:07:30 am ---[....8< a lot of good advice >8...] For wrapping variables you can always use the modulus operator. wrappedCounter = wrappedCounter++ % MAX_VALUE; --- End quote --- :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) --- End quote --- 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; --- End quote --- 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. |
| paulca:
--- Quote from: newbrain on March 11, 2018, 09:36:32 am ---It is simply wrong code, stay away. --- End quote --- 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. |
| newbrain:
--- Quote from: paulca on March 11, 2018, 11:16:49 am --- --- Quote from: newbrain on March 11, 2018, 09:36:32 am ---It is simply wrong code, stay away. --- End quote --- So you got me on that, but it works. --- End quote --- 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! --- Quote from: paulca on March 11, 2018, 11:16:49 am ---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. --- End quote --- 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: ---#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; } --- End code --- Result on Raspberry Pi 3, gcc 6.3.0 (same on X86 Ubuntu 16.04, gcc 5.4.0): --- Code: ---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... --- End code --- 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. |
| paulca:
--- Quote from: newbrain on March 11, 2018, 11:36:21 pm ---It would seem your tutor was teaching C/C++ with training wheels...maybe they should have stressed how to correctly use them. --- End quote --- 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. |
| metrologist:
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: ---/* 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); } --- End code --- |
| Navigation |
| Message Index |
| Next page |
| Previous page |