2

I am building a rover using an Arduino Mega and an Arduino Uno. The Mega has a sensor shield that has an SD card slot. The intent is for the rover to be autonomous, and I currently send logging info to the serial port (to log actions like boot, diagnostics, etc.)

Is there a simple way to redirect this output to BOTH the serial and the SD card? I would be happy with just a single file with lines added for each entry.

My complication here is to prevent using Serial.println as well as SD.println as two separate commands. Is there any other way around this?

3
  • 5
    Create something like a log function that does call both Serial.println and SD.println – Gerben Dec 30 '15 at 15:52
  • Do you want to eliminate the two separate println() calls to keep the code concise or is there another issue that you're concerned with? – dlu Dec 30 '15 at 23:07
  • Do you have internet connection? I'd rather log to a server than to a SD-card, for faster access of the log and for less wearing out the SD-card. – ott-- Dec 31 '15 at 18:29
2

The simplest thing you can do is to create a simple function that performs both tasks for you. However, there are certain caveats that you should look out for. The most basic function would be:

void log(const char *msg) {
    Serial.println(msg);
    myFile.println(msg);
}

However that is not the best way of doing it since that requires all your messages to be in RAM. So you should really use the __FlashStringHelper type as well as an overload function so you can use the F() macro to save RAM:

void log(const char *msg) {
    Serial.println(msg);
    myFile.println(msg);
}

void log(const __FlashStringHelper *msg) {
    Serial.println(msg);
    myFile.println(msg);
}

And that will pick the right version of log depending on what you pass. But what if you want to pass some variables, like numbers? Overloading can help again here. Another pair of functions now that take two parameters instead of one:

void log(const char *msg) {
    Serial.println(msg);
    myFile.println(msg);
}

void log(const __FlashStringHelper *msg) {
    Serial.println(msg);
    myFile.println(msg);
}

void log(const char *msg, int v) {
    Serial.print(msg);
    Serial.print(" ");
    Serial.println(v);
    myFile.print(msg);
    myFile.print(" ");
    myFile.println(v);
}

void log(const __FlashStringHelper *msg, int v) {
    Serial.print(msg);
    Serial.print(" ");
    Serial.println(v);
    myFile.print(msg);
    myFile.print(" ");
    myFile.println(v);
}

So now you have 4 functions called log, and they can be used in these different ways:

log("This is a message"); // Could be a char * variable
log(F("This is a message"));
log("The temperature is:", temp);
log(F("The temperature is:", temp);

And of course you can keep expanding and expanding the parameters in different ways for different combinations of parameters. Just be warned that for some combinations of parameters the compiler won't be able to work out which is the right function to call (for instance if you have one that takes a long and one that takes a byte in the same place and you just pass a numeric literal), so you have to think carefully about how you arrange your parameters.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.