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.
[....8< a lot of good advice >8...]
For wrapping variables you can always use the modulus operator.
wrappedCounter = wrappedCounter++ % MAX_VALUE;
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)
This paragraph renders undefined statement expressions such as
i = ++i + 1;
a[i++] = i;
while allowing
i = i + 1;
a[i ] = i;
It is simply wrong code, stay away.
It is simply wrong code, stay away.
So you got me on that, but it works.
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.
#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;
}
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?It would seem your tutor was teaching C/C++ with training wheels...maybe they should have stressed how to correctly use them.
/*
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);
}
// 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;
}
while (!SD.begin(chipSelect)) {
Serial.println("Card failed, or not present");
digitalWrite(LED_BUILTIN, HIGH); delay(100); digitalWrite(LED_BUILTIN, LOW); delay(250);
}
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: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);
Code: [Select]char buffer[1024];
Or similar. I didn't test compile that, but basically....
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);
%d - Integer value
%.2f - Decimal value with 2 decimal places.
#include<stdlib.h>
dtostrf(FLOAT,WIDTH,PRECISION,BUFFER);
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.
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();
String data = "";
data += count;
data += "\tavegs\t";
data += volts;
data += "\tV\t";
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...
...
char buffer[1024];
memset( buffer, 0, 1024 );
snprintf( buffer, 1024, "%d \t averages %.2fV %.2fA %.2fAh %2.fW %.2fWhr ...
...
I'd rather understand the define than avoid it as it looks like it will have a lot of utility.
...
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++.
/*
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);
}
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);
}
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);
}