sleep, watchdog e scrittura su SD... caratteri sbagliati

Buongiorno a tutti,
vi scrivo per sottoporre il seguente problema riguardante un semplice logger che mi serve come esercizio…
Il sistema è: scheda arduino + SD shield + sensore termometro + input analogico + pila 9V
Avendo problemi di durata della batteria (150 minuti con una registrazione su SD ogni 7 secondi circa) ho letto sul forum il modo di implementare lo Sleep per Arduino ed il relativo risveglio attraverdo il watchdog.
Riprogrammando l’Arduino con un codice che “addormentasse” il sistema per circa 7 secondi tra una scrittura e l’altra, ho ripetuto il test, facendo ripartire il “logger” e monitorando la durata della batteria, ottenendo una durata di 380 minuti (molto bene).

Il problema è che guardando le registrazioni sulla scheda SD, in molte righe, compaiono caratteri sbagliati (alcuni non sono affatto caratteri dell’alfabeto “classico”), mancano pezzi di stringhe, o caratteri sostituiti (L anzichè T) ed altri cambiamenti.

Durante il primo test di durata (quello senza sleep) ogni riga è stata registrata perfettamente, fino a pochi secondi prima dello spegnimento per batteria esausta.

Mi sorge cosi il dubbio che il risveglio dopo lo sleep, faccia un po’ male alla scrittura su SD.

Avete qualche esperienza in merito?

Vi allego il codice che ho usato (risultato di un copia e incolla + qualche riga scritta da me…)

Grazie,
Tredipunta.

//****************************************************************
*

  • Watchdog Sleep Example
  • SD log
    /
    //
    ***************************************************************
    #include <SD.h>
    #include “Wire.h”
    #include <avr/sleep.h>
    #include <avr/wdt.h>
    #ifndef cbi
    #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
    #endif
    #ifndef sbi
    #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= BV(bit))
    #endif
    #define DS1307_I2C_ADDRESS 0x68 // This is the I2C address
    #define TCN75A_ADDRESS 0x48 //depends on how the pins A0, A1, A2 are wired
    // Global Variables
    int command = 0; // This is the command char, in ascii form, sent from the serial port
    int i;
    String second, minute, hour, dayOfWeek, dayOfMonth, month, year;
    File myFile;
    String NomeFile; //= “MMDDhhmm.dat”;
    char NomeFile_C[12];
    volatile boolean f_wdt=1;
    String Giorno;
    String Ora;
    float cels;
    int LightSensorValue;
    float ResSensor;
    // Convert binary coded decimal to normal decimal numbers
    String bcdToDec(byte val)
    {
    String dummy;
    byte numero;
    numero = (val/1610) + (val%16);
    if (numero < 10)
    {
    dummy = String(“0”);
    dummy += String(numero, DEC);
    }else
    {
    dummy = String(numero, DEC);
    }
    return (dummy);
    }
    // Gets the date and time from the ds1307 and prints result
    void getDateDs1307(String
    giorno, String* ora)
    {
    // Reset the register pointer
    Wire.beginTransmission(DS1307_I2C_ADDRESS);
    Wire.send(0x00);
    Wire.endTransmission();
    Wire.requestFrom(DS1307_I2C_ADDRESS, 7);
    // A few of these need masks because certain bits are control bits
    second = bcdToDec(Wire.receive() & 0x7F);
    minute = bcdToDec(Wire.receive());
    hour = bcdToDec(Wire.receive() & 0x3F); // Need to change this if 12 hour am/pm
    dayOfWeek = bcdToDec(Wire.receive());
    dayOfMonth = bcdToDec(Wire.receive());
    month = bcdToDec(Wire.receive());
    year = bcdToDec(Wire.receive());
    *giorno = String(year) + String(month) + String(dayOfMonth);
    //*ritorno += String("
    ");
    *ora = String(hour) + String(minute) + String(second);
    }
    void setup()
    {
    Wire.begin();
    Serial.begin(9600);
    Serial.print(“Initializing SD card…”);
    // On the Ethernet Shield, CS is pin 4. It’s set as an output by default.
    // Note that even if it’s not used as the CS pin, the hardware SS pin
    // (10 on most Arduino boards, 53 on the Mega) must be left as an output
    // or the SD library functions will not work.
    pinMode(10, OUTPUT);

if (!SD.begin(10)) {
Serial.println(“initialization failed!”);
return;
}
Serial.println(“initialization done.”);
getDateDs1307(&NomeFile, &Ora);
NomeFile +=".dat";
NomeFile.toCharArray(NomeFile_C, 12);
Serial.println(“File Name:” + NomeFile);

// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
myFile = SD.open(NomeFile_C, FILE_WRITE);
// if the file opened okay, write to it:
if (myFile) {
Serial.println(“Intestazione”);
myFile.println(“Intestazione”);
// close the file:
myFile.close();
Serial.println(“done.”);
} else {
// if the file didn’t open, print an error:
Serial.println(“error opening test.txt”);
}
// configurazione della risoluzione per il termometro

Serial.println(“Setting Temperature sensor”);
byte config;
config = B01100000;
//START the transmission
Wire.beginTransmission(TCN75A_ADDRESS);
Wire.send(0x01);
Wire.send(config);
//STOP the transmission
Wire.endTransmission();
Serial.println(“Done.”);
// Fine cfg termometro

// configurazione WatchDog

// CPU Sleep Modes
// SM2 SM1 SM0 Sleep Mode
// 0 0 0 Idle
// 0 0 1 ADC Noise Reduction
// 0 1 0 Power-down
// 0 1 1 Power-save
// 1 0 0 Reserved
// 1 0 1 Reserved
// 1 1 0 Standby(1)
cbi( SMCR,SE ); // sleep enable, power down mode
cbi( SMCR,SM0 ); // power down mode
sbi( SMCR,SM1 ); // power down mode
cbi( SMCR,SM2 ); // power down mode
setup_watchdog(9);
// -----------------------
}
void GetTemperature(float* ritorno)
{
float cels;

//START the transmission
Wire.beginTransmission(TCN75A_ADDRESS);
//address the ambient temperature register
Wire.send(0x00);
//the ambient temperature register is 16-bit so request 2 bytes
Wire.requestFrom(TCN75A_ADDRESS, 2);
//the two registers being read are the msb and lsb.
byte msb;
byte lsb;
int count = 0;
while(Wire.available())
{
if (count == 0)
{
//get the msb
msb = Wire.receive();
count++;
}
else
{
//get the lsb
lsb = Wire.receive();
count = 0;
}
}
//STOP the transmission
Wire.endTransmission();
//there are only 4 bits used in the lsb, so bitshift the msb and lsb accordingly.
//then OR the msb and lsb together into one Extraordinarily Significant Bit™.
//esb is an int (and not a byte) because it is 16-bit and can hold the 12-bits we need.
int esb = (msb << 4) | ((lsb >> 4) & 0xF);
//multiply by 2^-4 (0.0625). why? because the datasheet says so.
//and at least now we know where the decimal is coming from.
cels = esb * 0.0625;
//convert to fahrenheit
//float fahr = 1.8 * cels + 32.0;
//convert to kelvin for dork factor
//float kelv = cels + 273.15;

*ritorno = cels;
}
void loop()
{
if (f_wdt==1)
{ // wait for timed out watchdog / flag is set when a watchdog timeout occurs
f_wdt=0; // reset flag
getDateDs1307(&Giorno, &Ora);
GetTemperature(&cels);

// Lettura Luce
LightSensorValue = analogRead(0);
ResSensor = analogRead(1);
ResSensor = ResSensor 5.0/1024.0;
ResSensor = ResSensor
3.0*1.01489;

// Scrittura su file
File dataFile = SD.open(NomeFile_C, O_CREAT | O_WRITE | O_APPEND);
// if the file is available, write to it:
if (dataFile) {
dataFile.print(Giorno + “_” + Ora + “\t”);
dataFile.print(float(millis()/1000.0), 3);
dataFile.print("\tT\t");
dataFile.print(cels, 4);
dataFile.print("\tLight\t");
dataFile.print(LightSensorValue, DEC);
dataFile.print("\tRes\t");
dataFile.println(ResSensor);

dataFile.flush();
dataFile.close();
// print to the serial port too:
Serial.print(Giorno + “_” + Ora + “\t”);
Serial.print(float(millis()/1000.0), 3);
Serial.print("\tT\t");
Serial.print(cels, 4);
Serial.print("\tLight\t");
Serial.print(LightSensorValue, DEC);
Serial.print("\tRes\t");
Serial.println(ResSensor);
}
// if the file isn’t open, pop up an error:
else {
Serial.println(“error opening datalog.txt”);
}
Serial.println();
system_sleep();
}
}
//****************************************************************
// set system into the sleep state
// system wakes up when wtchdog is timed out
void system_sleep() {
cbi(ADCSRA,ADEN); // switch Analog to Digitalconverter OFF
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
sleep_enable();
sleep_mode(); // System sleeps here
sleep_disable(); // System continues execution here when watchdog timed out
sbi(ADCSRA,ADEN); // switch Analog to Digitalconverter ON
}
//****************************************************************
// 0=16ms, 1=32ms,2=64ms,3=128ms,4=250ms,5=500ms
// 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec
void setup_watchdog(int ii) {
byte bb;
int ww;
if (ii > 9 ) ii=9;
bb=ii & 7;
if (ii > 7) bb|= (1<<5);
bb|= (1<<WDCE);
ww=bb;
//Serial.println(ww);
MCUSR &= ~(1<<WDRF);
// start timed sequence
WDTCSR |= (1<<WDCE) | (1<<WDE);
// set new watchdog timeout value
WDTCSR = bb;
WDTCSR |= _BV(WDIE);
}
//****************************************************************
// Watchdog Interrupt Service / is executed when watchdog timed out
ISR(WDT_vect) {
f_wdt=1; // set global flag
}

Vi allego l'immagine del grafico sulla durata della batteria nei due casi: http://imageshack.us/photo/my-images/46/batteriae.jpg/

Tredipunta.

Prova a mettere un delay dopo il risveglio. L'uscita da uno sleep necessita sempre di un po' di tempo per riportare tutte le periferiche del micro alla piena efficienza.