problem with interrupt and writing to sd-card

Hi,
I have a sketch for logging rpm-meter data to a sd-card.
I have a reed switch connected to interrupt 1 counting pulses each second. in the main loop I am writing the counted value to the sd-card and reset the counter.
For some reason, the sd-card logging stops after some time between two to three hours regularly.
Can the problem be, that the interrupt interrupts the sd-card writing so that the sketch hangs?

Here’s the code

#include <FlexiTimer2.h>
#include "Fat16.h"
#include "SdCard.h"
#include "WString.h"


SdCard card;
Fat16 file;

boolean blinker;
boolean blinklogic;
int counter;
int counter1;
int tmpval;
int time;
//Digital Pin 3 (= Interrupt 1)
// 10k auf GRND
//Digital Pin 3 auf 10K
// Reed an 10K
//andere Seite Reed an 5V
boolean debug;

void setup(){
 debug=false;
  pinMode(11, INPUT);
 pinMode(13, OUTPUT);
 Serial.begin(9600);
 
 blinker = false;
 blinklogic = true;
 counter=0;
 counter1=0;
 tmpval = 0;
 time = millis();
 attachInterrupt(1,count,RISING);

 FlexiTimer2::set(1000,report);
 FlexiTimer2::start();
 
   if (!card.init()) error1("card.init");
  
   // initialize a FAT16 volume
   if (!Fat16::init(card)) error1("Fat16::init");
  
   // create a new file
   char name[] = "WIND0.00";
   for (uint8_t i = 0; i < 100; i++) {
     name[6] = i/10 + '0';
     name[7] = i%10 + '0';
     if (file.create(name)) break;
   }
   if (!file.isOpen()) error1("file.create");
   if(debug){
     Serial.print("Logging to: ");
     Serial.println(name);
   }
   //Header im File schreiben
   file.println("Umrundungen Windturbine");
  
    if (file.writeByteError || !file.sync()) {
      error1("write header");
    }
}

void error1(char *str){
  Serial.print("error ");
  Serial.println(str);
}

void report(){
  if(debug){
    Serial.print("Drehzahl: ");
    Serial.print(counter/2);
    Serial.print(", ");
    Serial.println(counter1/2);
  }
  counter = 0;
   
 
}

void count(){
 //increase counter
 counter++;
 counter1++;
 if((counter1/2)!=tmpval){
   /*Serial.print("tmpval: ");
   Serial.print(tmpval);
   Serial.print(", ");*/
   Serial.println(counter1/2);

 }
 tmpval = counter1/2;
  
}

void loop(){

  file.print((millis()/1000));
  file.print(";");
  file.println(counter1/2);
  delay(30);
 
  if (file.writeByteError) {
    error1("write data");
    blinklogic=false;
    //Serial.println("blinklogic false");
    }
  if (!file.sync()){
    error1("sync");
    blinklogic=false;
    if(debug){
      Serial.println("blinklogic false");
    }  
  }
  if(blinklogic){
    if(debug){  
      Serial.println("blinklogic high");
    }
    if(blinker==false){
       digitalWrite(13, HIGH);
      if(debug){  
         Serial.println("blinker high");
      }
       blinker=true;
     }else
     if(blinker==true){
       digitalWrite(13,LOW);
       if(debug){
         Serial.println("blinker low");
       }
       blinker=false;
     }
   else if(!blinklogic)digitalWrite(13,LOW);
  }
   Serial.println(counter1/2);
  delay(970);
  
  
}

Does anybody have an idea?

Cheers,
Marcel

Since the interrupt can come at anytime while you perform your file operations, you will need to use cli() (clear interrupt) and sei() (set interrupt) right before you open your file and right after you close your file. This way, your file operations will always be complete successfully and should solve your issue.

You’ll need to include #include <avr/interrupt.h> for cli() and sei().

Hi AlphaZeta,

I tried your solution. However after I disable the Interrupt with cli() it seems, that I cannot write to the file on the SD card. I suspect that for writing to the SD card interrupts need to be enabled.
It seems that the loop never makes it to the bottom, where I set the interrupt again with sei().

Any ideas?
Here’s the updated code:

#include <FlexiTimer2.h>
#include "Fat16.h"
#include "SdCard.h"
#include "WString.h"
#include <avr/interrupt.h>

SdCard card;
Fat16 file;

boolean blinker;
boolean blinklogic;
int counter;
int counter1;
int tmpval;
int time;
long timerun;
//Digital Pin 3 (= Interrupt 1)
// 10k auf GRND
//Digital Pin 3 auf 10K
// Reed an 10K
//andere Seite Reed an 5V
boolean debug;

void setup(){
 timerun=0;
 debug=false;
 pinMode(11, INPUT);
 pinMode(13, OUTPUT);
 Serial.begin(9600);
 
 blinker = false;
 blinklogic = true;
 counter=0;
 counter1=0;
 tmpval = 0;
 time = millis();
 attachInterrupt(1,count,RISING);

 FlexiTimer2::set(1000,report);
 FlexiTimer2::start();
 
   if (!card.init()) error1("card.init");
  
   // initialize a FAT16 volume
   if (!Fat16::init(card)) error1("Fat16::init");
  
   // create a new file
   char name[] = "WIND0.00";
   for (uint8_t i = 0; i < 100; i++) {
     name[6] = i/10 + '0';
     name[7] = i%10 + '0';
     if (file.create(name)) break;
   }
   if (!file.isOpen()) error1("file.create");
   if(debug){
     Serial.print("Logging to: ");
     Serial.println(name);
   }
   //Header im File schreiben
   file.println("Umrundungen Windturbine");
  
    if (file.writeByteError || !file.sync()) {
      error1("write header");
    }
}

void error1(char *str){
  Serial.print("error ");
  Serial.println(str);
}

void report(){
  if(debug){
    Serial.print("Drehzahl: ");
    Serial.print(counter/2);
    Serial.print(", ");
    Serial.println(counter1/2);
  }
  counter = 0;
   
 
}

void count(){
 //increase counter
 counter++;
 counter1++;
 if((counter1/2)!=tmpval){
   /*Serial.print("tmpval: ");
   Serial.print(tmpval);
   Serial.print(", ");*/
   Serial.println(counter1/2);

 }
 tmpval = counter1/2;
  
}

void loop(){

  if(millis()-timerun>1000){
    //uint8_t SaveSREG = SREG;   // save interrupt flag
    cli(); //clear Interrupt
    if(debug)Serial.println("Interrupt off");
    file.print((millis()/1000));
    if(debug)Serial.println("Interrupt off1");    
    file.print(";");
    if(debug)Serial.println("Interrupt off2");
    file.println(counter1/2);
    if(debug)Serial.println("Interrupt off3");
    delay(30);
    if(debug)Serial.println("Daten geschrieben4.");
    if (file.writeByteError) {
      error1("write data");
      blinklogic=false;
      //Serial.println("blinklogic false");
      }
    if (!file.sync()){
      error1("sync");
      blinklogic=false;
      if(debug){
        Serial.println("blinklogic false");
      }  
    }
   sei(); // again Interrupt again
    //SREG = SaveSREG;   // restore the interrupt flag
    Serial.println("Interrupt ein");

    if(blinklogic){
      if(debug){
        Serial.println("blinklogic high");
      }
      if(blinker==false){
         digitalWrite(13, HIGH);
        if(debug){
           Serial.println("blinker high");
        }
         blinker=true;
       }else if(blinker==true){
         digitalWrite(13, LOW);
         if(debug){
           Serial.println("blinker low");
         }
        blinker=false;
       }
     else if(!blinklogic)digitalWrite(13, LOW);
    }
     Serial.println(counter1/2);
     timerun = millis();
     
  }
}

If I remember correctly, SPI probably wasn't the problem. Serial.print routines may be the culprit here. Did your code reach this line?

if(debug)Serial.println("Interrupt off");

My guess is probably not. Try again by commenting out all the Serial.print stuff.

Can the problem be, that the interrupt interrupts the sd-card writing so that the sketch hangs?

Why would this suddenly be a problem when it apparently works for 3 hours without issues?

The SD libraries use a lot of RAM and this would be my primary concern. Typically two buffers of 512 bytes each, 50% of the AtMega328 RAM capacity, is needed when writing to the card. This is in addition to your sketch and additional libraries RAM requirements.

Fort starters I would eliminate the Wstring library (using dynamic memory allocation is asking for problems) from the sketch and also move all string literals to flash (e.g. Serial.print("...move string to flash...").

I'll try that, but I have a similar sketch running without problems, with all the Serial.println commands. One question: how do I move Serial.print statements to flash?

That did not help.
The problem seems to be somewhere else.
The arduino stops writing to the sd card always after a little more than two hours...
The strange thing is that the light of the led on pin 13 that keeps blinking while writing to the sd-card is becoming dimmer and dimmer over time... At first I thought, that the problem might be the battery, but that is not the case, as the same behavior occurs when connected to a USB cable...
I tried different arduinos, but found the same behavior... Does anyone have an idea, what might be the problem?

I see you are printing inside your interrupt function. This is generally not good since print is a slow function that will block other interrupts. You should try to rewrite your code so that the interrupt routine is as fast as possible (move printing to the main loop).

void count(){
 //increase counter
 counter++;
 counter1++;
 if((counter1/2)!=tmpval){
   /*Serial.print("tmpval: ");
   Serial.print(tmpval);
   Serial.print(", ");*/
   Serial.println(counter1/2);
 }
 tmpval = counter1/2;
}

Also you need to prefix global variables that are changed within an interrupt handler with the "volatile" keyword. If not, changes to these variables may not be picked up by code outside of the interrupt function.

There is also a concurrency issue with accessing variables changed by interrupt handlers. Acess to variables beyond a single byte need to be guarded with interrupt enable/disable statements. This is missing from your code.

.... the "volatile" keyword. If not, changes to these variables may not be picked up by code outside of the interrupt

Even then, this is a weak protection for bytes only, not for multi-byte datatypes...

Even then, this is a weak protection for bytes only

This has nothing to do with protection at all.

Volatile is a compiler directive that turns off access optimization (register caching) for the marked variables (single or multi-byte). This is to make certain that each C-source reference to the variables will be read from RAM prior to use.

Sorry, none of the tips helped.
I modified the code according to the above recommendations.

#include <FlexiTimer2.h>
#include "Fat16.h"
#include "SdCard.h"
#include "MemoryFree.h"
#include <avr/interrupt.h>


SdCard card;
Fat16 file;

boolean blinker;
boolean blinklogic;
volatile int counter;
volatile int counter1;
volatile int tmpval;
int time;
//Digital Pin 3 (= Interrupt 1)
// 10k auf GRND
//Digital Pin 3 auf 10K
// Reed an 10K
//andere Seite Reed an 5V
boolean debug;

void setup(){
 debug=true;
  pinMode(11, INPUT);
 pinMode(13, OUTPUT);
 Serial.begin(9600);
 
 blinker = false;
 blinklogic = true;
  counter=0;
  counter1=0;
  tmpval = 0;
 time = millis();
 attachInterrupt(1,count,RISING);

 FlexiTimer2::set(1000,report);
 FlexiTimer2::start();
 
   if (!card.init()) error1("card.init");
  
   // initialize a FAT16 volume
   if (!Fat16::init(card)) error1("Fat16::init");
  
   // create a new file
   char name[] = "WIND0.00";
   for (uint8_t i = 0; i < 100; i++) {
     name[6] = i/10 + '0';
     name[7] = i%10 + '0';
     if (file.create(name)) break;
   }
   if (!file.isOpen()) error1("file.create");
   if(debug){
     Serial.print("Logging to: ");
     Serial.println(name);
   }
   //Header im File schreiben
   file.println("Umrundungen Windturbine");
  
    if (file.writeByteError || !file.sync()) {
      error1("write header");
    }
}

void error1(char *str){
  Serial.print("error ");
  Serial.println(str);
}

void report(){
  if(debug){
    /*Serial.print("Drehzahl: ");
    Serial.print(counter/2);
    Serial.print(", ");
    Serial.println(counter1/2);*/
  }
  counter = 0;
   
 
}

void count(){
 //increase counter
 cli();
 counter++;
 counter1++;
 if((counter1/2)!=tmpval){
   /*Serial.print("tmpval: ");
   Serial.print(tmpval);
   Serial.print(", ");*/
   Serial.println(counter1/2);

 }
 tmpval = counter1/2;
 sei();
}

void loop(){

  file.print((millis()/1000));
  file.print(';');
  file.print(counter1/2);
  file.print(';');
  file.println(freeMemory());  
  delay(20);
 
  if (file.writeByteError) {
    error1("write data");
    blinklogic=false;
    Serial.println("blinklogic false");
    }
  if (!file.sync()){
    error1("sync");
    blinklogic=false;
    if(debug){
      Serial.println("blinklogic false");
    }  
  }
  if(blinklogic){
    if(debug){  
      Serial.println("blinklogic high");
    }
    if(blinker==false){
       digitalWrite(13, HIGH);
      if(debug){  
         Serial.println("blinker high");
      }
       blinker=true;
     }else
     if(blinker==true){
       digitalWrite(13,LOW);
       if(debug){
         Serial.println("blinker low");
       }
       blinker=false;
     }
   else if(!blinklogic)digitalWrite(13,LOW);
  }
   Serial.println(counter1/2);
  delay(970);
  
  
}

What I noticed is that the LED (see in loop())on Pin 13 flashes weaker and weaker while writing to the sd-card over time. After approx. 130 minutes, the flash is almost not visible anymore. That is about the time, when arduino stops writing to the sd-card.

The problem is not the power source, as the same behavior occurs when sourcing power from a battery or from USB.

There is also no memory problem, as the free memory is always >1000bytes

I am desparate. Does anybody have a clue what the problem might be?

Is there anybody, who at least can confirm, that writing to a sd-card has worked for longer than 2 hours continuously?

Hi Fritz,

Yes, I used one as a temperature logger and it ran for at least a few days at a time and I didn't run into any issues.

Did the issue ever get resolved? I'm trying to build an arduino/datalogger/accel/rpm reading device, and I'm worried about doing SD card work with interrupts going on at the same time.

I still have the same problem. I am writing the rpm info on my windrotor, however, after about two hours writing stops…

i'm thinking of a similar project, but the frequency of writing will be a lot lower.
Did you try if a much-lower writing frequency can effectively lengthen the 2.xhr limit in writing to SDcard (and pin13dim) ?

-Frankie.

I have good experiences with the sdfat library Google Code Archive - Long-term storage for Google Code Project Hosting. for writing a logfile with one ~70 byte line of data to a csv file with a frequency of one per second (86400 record/day). Worked > 5 days in a row and then I needed the board ...

Code snippet: // slightly stripped and commented
As you see I open and close the SD card over and over again.

void DoLogSDCard()
{
    int len = strlen(buffer);  // extern var char [128]

    char name[24];
    now = RTC.now();  // a ds1307 
    sprintf(name, "%04d%02d%02d.log", now.year(), now.month(), now.day()); // every day a new file

    // initialize the SD card
    if (!card.init()) 
    {
      mySerial.print(card.errorCode(), HEX); // uses New SoftSerial
      return;
    }
    // initialize a FAT volume
    if (!volume.init(card)) 
    {
      mySerial.print(card.errorCode(), HEX);
      return;
    }
    // open the root directory
    if (!root.openRoot(volume))
    {
       mySerial.print(card.errorCode(), HEX);
      return;
    }

    file.writeError = false;
    if (!file.open(root, name, O_CREAT | O_APPEND | O_WRITE))
    {
      mySerial.println("error opening file");
    } 
    else
    {
      file.println(buffer);
      file.timestamp(T_WRITE, now.year(), now.month(), now.day(), 
            now.hour(), now.minute(), now.second());
      file.close();
    }

    root.close();
}

For handling my interrupt counter I work this way
Arduino Playground - HomePage This is based upon examples of using a critical section in a one reader - code loop() - and one writer - IRQ routine. See http://www.amazon.ca/Principles-Concurrent-Distributed-Programming-Ben-Ari/dp/032131283X for a very good book about parallel processing.