4
\$\begingroup\$

I am using Arduino Uno and the Ethernet Shield with a 2GB SD Card. I have the following working code intended to be used to write data on the SD Card (note: the SD Library is properly initialized and I just hide the reated code in order to keep things smaller):

class Logger {
  private:
    File myFile;
    char *myFilename;

  public:
    Logger (char *myFilename = "text.txt") : myFilename(myFilename) {
      myFile = SD.open(myFilename, FILE_WRITE);
    }

    void writeAction () {
      if (myFile) {
        myFile.print("Sample text.");
        myFile.close();
      } else {
        Serial.print("Error opening the file ");
      }
    }
};

void setup() {
  Logger logger;

  ...

  logger.writeAction();
}

However, if I change a bit the above code as made in the following one, it will not work as expected: the "Sample text." is not written / saved on the SD card.

class Logger {
  private:
    File myFile;
    char *myFilename;

  public:
    Logger (char *myFilename = "text.txt") : myFilename(myFilename) {
      // Note: Here is the change. File opening statements are moved
      // "inside" the writeAction function.
    }

    void writeAction () {
      myFile = SD.open(myFilename, FILE_WRITE);

      if (myFile) {
        myFile.print("Sample text.");
        myFile.close();
      } else {
        Serial.print("Error opening the file ");
      }
    }
};

void setup() {
  Logger logger;

  ...

  logger.writeAction();
}

Why does it happen? How can I make the myFile to be properly opened and closed "inside" the writeAction function?

\$\endgroup\$
7
  • \$\begingroup\$ I don't remember my C++ file handling functions off the top of my head, but are you sure your settings are correct to clobber and overwrite text.txt if it already exits? \$\endgroup\$
    – The Photon
    Feb 26, 2013 at 0:14
  • 1
    \$\begingroup\$ I can't follow why your constructor is written that way but I wouldn't expect the global myFilename to be set. Try giving it a different name in the constructor and copy it to the global. \$\endgroup\$
    – PeterJ
    Feb 26, 2013 at 0:51
  • \$\begingroup\$ smells like a race condition, what happens if you put delay(1000); after myFile = SD.open(myFilename, FILE_WRITE); in your second code? \$\endgroup\$
    – vicatcu
    Feb 26, 2013 at 4:57
  • \$\begingroup\$ I don't know C++ at all well (Ada compilers tell you exactly what's wrong far more of the time!) but I am suspicious that myFile is left uninitialised by the second constructor : can you initialise it to a known "no file" value? \$\endgroup\$
    – user16324
    Feb 26, 2013 at 9:57
  • 2
    \$\begingroup\$ How is this a electrical engineering problem? This looks like a pure high level software problem. \$\endgroup\$ Mar 4, 2013 at 22:05

1 Answer 1

1
\$\begingroup\$

A common (and difficult to isolate) problem that sometimes happen in embedded systems development is that something that worked previously stops working after some seemingly innocuous change to the software. There are a great many reasons why this can happen, but the two most common are:

  • Stack Overflow (I've written about this extensively)
  • Undocumented (or Unrealized) Timing Dependencies / Assumptions

Stack overflow is usually the result of trying to squeeze too much into your chip, and not giving the code enough room to breathe. It can also be the result of nasty software mistakes involving pointers that leads to trampling memory that you didn't expect to be altering.

Timing Dependencies are much trickier. These often rear their head when you're using "somebody else's code" and the usage pattern that code relies on is not well documented. If you really understand the peripherals you're working with you can figure this out by digging into that person's code, or by using an instrument to do direct measurements on signals. Buried in some datasheet somewhere it probably says don't do X within Y milliseconds of doing Z. In cases like this there is an implied contract (open-loop) between the controller and the peripheral to give things time to do whatever before interacting with it again.

An API should make type of dependency clear, but beggar's can't be choosers, as they say. This is a challenge when it comes to Arduino, and code works fine as long as you don't stray to far from the examples. But if it doesn't work in your context, you have to be prepared to really understand what's going on and adjust things accordingly.

Ideally, the interface to a peripheral should be written in such a way as to be "delay insensitive" and use some kind of polling mechanism to gate progress. Code behavior like what you are seeing here is what I like to call "faith-based" development. Relying on feedback from a device is always better than crossing your fingers and moving on.

\$\endgroup\$
2
  • \$\begingroup\$ OK, maybe I understood what you mean and I am thinking my case is related to a "Undocumented (or Unrealized) Timing Dependency / Assumption". So, since I would like to "really understand what's going on and adjust things accordingly", where I can read the source code of the Arduino SD library? \$\endgroup\$
    – Backo
    Mar 4, 2013 at 22:22
  • \$\begingroup\$ In your arduino installation directory under /libraries/SD \$\endgroup\$
    – vicatcu
    Mar 5, 2013 at 3:55

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