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);
}
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().
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();
}
}
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).
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.
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?
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'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) ?
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();
}