Librerie e Timer1

Ma lo sketch che ti ho passato a te funziona oppure no?

Si, ma solo se mando le stringa come nell’IDE Arduino, cioè viene mandata tutta in una volta. Se invece digito il comando da tastiera, e ogni tasto che premo viene mandato immediatamente, allora non funziona, sembra che non aspetti il CR. La routine precedente invece funziona in entrambi i modi, aspetta il CR a tempo indefinito.

Frattanto dopo aver scoperto che il Serial.println presente nella routine di interrupt inchiodava Arduino ho provato ad azzerare i registri del Timer1 ma il tutto continua a non funzionare, il dannato led non si accende.

zoomx: il dannato led non si accende

Se non va neanche così... è il led! :grin:

const byte led = 13;

void setup() {                
   pinMode(led, OUTPUT);     
}

void loop() {
  digitalWrite(led, HIGH);   
  delay(1000);               
  digitalWrite(led, LOW);    
  delay(1000);               
}

Il led funziona.

Nel programma ho messo un'istruzione che esplicitamente accende e spegne il led e funziona.

Edit: forse ho scoperto perché l'IDE segnalava un problema con le parentesi: conta una delle parentesi commentate. Questa qui

/*
            else {
            Serial.println(AcqDone);
            */

Però correttamente il compilatore non la considera.

... guarda che quel commento, messo così E' SBAGLIATO !!!

Tu commenti un "else {" il che significa che hai commentato una apertura di graffa, ma ... da qualche parte, nel codice, c'è una chiusura di graffa che rimane appesa e che può creare grossi problemi !

Se commenti la prima DEVI commentare anche la seconda ;)

Guglielmo

gpb, questo è un vero rompicapo. Se commento la parentesi chiusa dopo l'else il compilatore ri inca@@a :fearful: Ma se la lasco, compila, ma l'IDE segnala un errore nel conteggio delle parentesi. Adesso mi stampo lo sketch e le depenno a mano. :grin: :grin:

@Paolo : Bé ... probabilmente perché comunque resta della roba messa li così ... per fare una cosa fatta bene dovresti mettere l'inizio commento (/) dove è adesso, ovvero prima dell'else e il fine commento (/) dopo la parentesi graffa di chiusura dell'else così da eliminare TUTTO quello che è compreso nell'else. ;)

Poi ... se nell'IDE ti posizioni su una graffa, lui ti evidenzia la corrispondente di chiusura, quindi ... fai presto a fare il match :)

Guglielmo

L’IDE faceva il match con una delle parentesi dentro il commento, non so perché ma è evidentemente un errore. Eliminando la parte commentata, che non serviva, l’IDE non ha più avuto problemi e ha accoppiato correttamente le parentesi. La formattazione automatica non si inceppa più.
Può essere che il commento sia stato fatto parzialmente perché poi ho riciclato la parentesi, ormai non ricordo più. Il codice è parzialmente copiato, l’originale non ricordo dove l’ho preso ma si trova facilmente nei forum, probabilmente anche in questo.

Sto meditando se arrendermi e postare tutto il codice in un altro argomento. Non mi funziona la scrittura su SD ma gli esempi funzionano. A proposito, negli esempi sembra che si faccia un po’ di confusione sulla linea CS, diversa a seconda se si usi la SD della Ethernet shield (4) oppure lo shield apposito (10). Apro il file ma quando sto per scrivere mi va in reset.
Inoltre non funziona più la scrittura sul DS1307 del tempo corretto, lo leggo ma non riesco più a correggerlo, sebbene non abbia toccato una sola riga.
In altre parole singolarmente gli algoritmi funzionano ma quando metto tutto assieme no. Il programma è sotto i 22.000 byte, di RAM sembra ne rimanga un po’ meno di 700 per cui non si dovrebbe trattare di un problema di memoria. Non dovrebbe essere un problema di lunghezza dei fili di collegamento, sono sui 20cm e con le routine singole non ho avuto problemi.

Adesso stacco e magari preparo per domani un post pulito con la versione precedente che funziona, almeno così mi sembrava.

Il problema è qui

/* Month: 0 - 11 */
#define MONTH (__DATE__ [2] == 'n' ? (__DATE__ [1] == 'a' ? 0 : 5) \
: __DATE__ [2] == 'b' ? 1 \
: __DATE__ [2] == 'r' ? (__DATE__ [0] == 'M' ? 2 : 3) \
: __DATE__ [2] == 'y' ? 4 \
: __DATE__ [2] == 'l' ? 6 \
: __DATE__ [2] == 'g' ? 7 \
: __DATE__ [2] == 'p' ? 8 \
: __DATE__ [2] == 't' ? 9 \
: __DATE__ [2] == 'v' ? 10 : 11)

Se si mette tutto su una sola riga compila e l’IDE non da errori. Credo sia un bug.

// #include <Arduino.h>
#include <swRTC.h>
// #include <avr/io.h>
// #include <avr/interrupt.h>

#define YEAR ((((__DATE__ [7] - '0') * 10 + (__DATE__ [8] - '0')) * 10 + (__DATE__ [9] - '0')) * 10 + (__DATE__ [10] - '0'))

//* Month: 0 - 11 */
#define MONTH (__DATE__ [2] == 'n' ? (__DATE__ [1] == 'a' ? 0 : 5) : __DATE__ [2] == 'b' ? 1 : __DATE__ [2] == 'r' ? (__DATE__ [0] == 'M' ? 2 : 3) : __DATE__ [2] == 'y' ? 4 : __DATE__ [2] == 'l' ? 6 : __DATE__ [2] == 'g' ? 7 : __DATE__ [2] == 'p' ? 8 : __DATE__ [2] == 't' ? 9 : __DATE__ [2] == 'v' ? 10 : 11)
#define DAY ((__DATE__ [4] == ' ' ? 0 : __DATE__ [4] - '0') * 10 + (__DATE__ [5] - '0'))
//*******************

#define INLENGTH 5          //Needed for input with termination
#define INTERMINATOR 13     //Needed for input with termination

char inString[INLENGTH+1];  //Needed for input with termination
int inCount;                //Needed for input with termination

#define LEDPIN 13

int ore,minuti,secondi;
//String MyString;
char Stringa2[2];
swRTC rtc;
boolean AcqDone = false;  //It's time to acquisition!
boolean InAcq = false;    //If true=Acquisition is made during intterrupt
char comm;

void GetCharFromSerial() {
  Serial.flush(); //flush all previous received and transmitted data
  inCount = 0;
  do
  {
    while (!Serial.available());             // wait for input
    inString[inCount] = Serial.read();       // get it
    //++inCount;
    if (inString [inCount] == INTERMINATOR) break;
  } 
  while(++inCount < INLENGTH);
  inString[inCount] = 0;                     // null terminate the string
  //ch=inString;
  Serial.print("Ricevuto->");
  Serial.println(inString);
  comm=inString[0];
}

void GetTime() {
  Serial.println("Get Time");
  Serial.print(rtc.getHours(), DEC);
  Serial.print(":");
  Serial.print(rtc.getMinutes(), DEC);
  Serial.print(":");
  Serial.print(rtc.getSeconds(), DEC);
  Serial.print(" -- ");
  Serial.print(rtc.getDay(), DEC);
  Serial.print("/");
  Serial.print(rtc.getMonth(), DEC);
  Serial.print("/");
  Serial.println(rtc.getYear(), DEC);
}

void SetTime() {
  Serial.println("Set Time");
  //digitalWrite(13, LOW);
  Serial.print("Year");
  GetCharFromSerial();
  ore=atoi(inString); //Use the same variables for Year..
  Serial.print("Month");
  GetCharFromSerial();
  minuti=atoi(inString);
  Serial.print("Day");
  GetCharFromSerial();
  secondi=atoi(inString);
  rtc.stopRTC();
  rtc.setDate(ore,minuti,secondi);
  rtc.startRTC();
  Serial.print("Hour");
  GetCharFromSerial();
  ore=atoi(inString); //Use the same variables for Year..
  Serial.print("Minutes");
  GetCharFromSerial();
  minuti=atoi(inString);
  Serial.print("Seconds");
  GetCharFromSerial();
  secondi=atoi(inString);
  rtc.stopRTC();
  rtc.setTime(ore, minuti, secondi);
  rtc.startRTC();
  Serial.print("RTC changed->");
  GetTime();
}

void StartAcq() {
  Serial.println("Start Acquisition");
  if (InAcq==true) return;
  // initialize Timer1
  cli();         // disable global interrupts
  TCCR1A = 0;    // set entire TCCR1A register to 0
  TCCR1B = 0;

  // enable Timer1 overflow interrupt:
  TIMSK1 = (1 << TOIE1);

  // Set CS10 bit so timer runs at clock speed:
  TCCR1B |= (1 << CS10);
  TCCR2B &= ~(1 << CS11);
  TCCR1B |= (1 << CS12);
  //Serial.println(TCCR1B);
  // enable global interrupts:
  sei();
  InAcq = true;
}

void Download() {
  Serial.println("Download Data");
}

void StopAcq() {
  Serial.println("Stop Acquisition");
  cli();         // disable global interrupts
  TCCR1A = 0;    // set entire TCCR1A register to 0
  TCCR1B = 0;
  sei();
  InAcq=false;
}

void PrintMenu() {
  Serial.println("1 Start Acquisition");
  Serial.println("3 Stop Acquisition");
  Serial.println("8 Info");
  Serial.println("T Get Time");
  Serial.println("t Set Time");
  Serial.println("--------------------");
  Serial.println("Type the number and press enter");
}

void ParseMenu(char Stringa) {
  Serial.println("Parse Menu");
  switch (Stringa) {
  case '1':
    StartAcq();
    break;
  case '2':
    Download();
    break;
  case '3':
    StopAcq();
    break;
  case '8':
    Serial.print("Acquisition->");
    Serial.println(InAcq,DEC);
    break;
  case 'T':
    GetTime();
    break;
  case 't':
    SetTime();
    break;
  default:
    Serial.print("Command Unknown! ->");
    Serial.println(Stringa,HEX);
  }
}

void setup() {
  Serial.begin(9600);
  // initialize the digital pin as an output.
  // Pin 13 has an LED connected on most Arduino boards:
  pinMode(13, OUTPUT);
  //Initialize swRTC
  rtc.stopRTC();
  //rtc.setClockWithTimestamp(__TIMESTAMP__);
  rtc.setDate(DAY,MONTH+1,YEAR);
  //MyString=__TIMESTAMP__;
  Stringa2[0]=__TIMESTAMP__[11];
  Stringa2[1]=__TIMESTAMP__[12];
  Stringa2[2]=0;
  ore = atoi(Stringa2);

  Stringa2[0]=__TIMESTAMP__[14];
  Stringa2[1]=__TIMESTAMP__[15];
  minuti = atoi(Stringa2);

  Stringa2[0]=__TIMESTAMP__[17];
  Stringa2[1]=__TIMESTAMP__[18];
  secondi = atoi(Stringa2);
  ///*
  //	Serial.print(ore);
  //	Serial.print(":");
  //	Serial.print(minuti);
  //	Serial.print(":");
  //	Serial.println(secondi);
  //*/
  rtc.setTime(ore, minuti, secondi);
  rtc.startRTC();
  GetTime();

  // initialize Timer1
  cli();         // disable global interrupts
  TCCR1A = 0;    // set entire TCCR1A register to 0
  TCCR1B = 0;

  // enable Timer1 overflow interrupt:
  TIMSK1 = (1 << TOIE1);

  // Set CS10 bit so timer runs at clock speed:
  TCCR1B |= (1 << CS10);
  TCCR2B &= ~(1 << CS11);
  TCCR1B |= (1 << CS12);
  //Serial.println(TCCR1B);
  // enable global interrupts:
  sei();
  InAcq = true;
}

void loop()
{
  PrintMenu();
  GetCharFromSerial();
  ParseMenu(comm);
  //Serial.println(__TIMESTAMP__);
}

ISR(TIMER1_OVF_vect) {
  digitalWrite(LEDPIN, !digitalRead(LEDPIN));
  if ( rtc.getMinutes()%5==0 )  //Se il resto è zero i minuti finiscono per zero o 5
  { 
    if (AcqDone == false)
    {  //Siccome il controllo viene fatto ogni 30 secondi evita
      GetTime();        //che la routine scatti 2 volte ad es a 05 e 35 secondi
      //Serial.println(); //usando la boolean AcqDone
      AcqDone = true;
    }
    /*
    else {
     Serial.println(AcqDone);
     }
     */
  }
  else
  {
    AcqDone=false;
  }
}

NO, il problema è che nell’ultima funzione ( ISR(TIMER1_OVF_vect) ) hai fatto un casino con le parentesi XD XD XD

#include <Arduino.h>

#include <swRTC.h>

// avr-libc library includes
//http://arduinodiy.wordpress.com/2012/02/28/timer-interrupts/
#include <avr/io.h>
#include <avr/interrupt.h>

/*
Programma di test per provare la libreria swRTC e il modo di campionare ogni 5 minuti
a partire da 00. Il campionamento avviene via interrupt e quindi contemporaneamente si
gestisce un menu.
Aggiungiamo il settaggio della data e dell'ora.
Aggiungiamo lo start e stop dell'acquisizione
*/

//***************
//http://www.velocityreviews.com/forums/t316565-convert-__date__-to-unsigned-int.html
//read corrections!!!!!!
//http://lists.gnu.org/archive/html/avr-gcc-list/2009-01/msg00159.html
#define YEAR ((((__DATE__ [7] - '0') * 10 + (__DATE__ [8] - '0')) * 10 \
+ (__DATE__ [9] - '0')) * 10 + (__DATE__ [10] - '0'))

/* Month: 0 - 11 */
#define MONTH (__DATE__ [2] == 'n' ? (__DATE__ [1] == 'a' ? 0 : 5) \
: __DATE__ [2] == 'b' ? 1 \
: __DATE__ [2] == 'r' ? (__DATE__ [0] == 'M' ? 2 : 3) \
: __DATE__ [2] == 'y' ? 4 \
: __DATE__ [2] == 'l' ? 6 \
: __DATE__ [2] == 'g' ? 7 \
: __DATE__ [2] == 'p' ? 8 \
: __DATE__ [2] == 't' ? 9 \
: __DATE__ [2] == 'v' ? 10 : 11)

#define DAY ((__DATE__ [4] == ' ' ? 0 : __DATE__ [4] - '0') * 10 \
+ (__DATE__ [5] - '0'))
//*******************


#define INLENGTH 5          //Needed for input with termination
#define INTERMINATOR 13     //Needed for input with termination
char inString[INLENGTH+1];  //Needed for input with termination
int inCount;                //Needed for input with termination

#define LEDPIN 13

int ore,minuti,secondi;
//String MyString;
char Stringa2[2];
swRTC rtc;
boolean AcqDone = false;  //It's time to acquisition!
boolean InAcq = false;    //If true=Acquisition is made during intterrupt
char comm;

void GetCharFromSerial()
{
    Serial.flush(); //flush all previous received and transmitted data
    inCount = 0;
          do
          {
            while (!Serial.available());             // wait for input
            inString[inCount] = Serial.read();       // get it
            //++inCount;
            if (inString [inCount] == INTERMINATOR) break;
          } while(++inCount < INLENGTH);
          inString[inCount] = 0;                     // null terminate the string
          //ch=inString;
    Serial.print("Ricevuto->");
    Serial.println(inString);
    comm=inString[0];
}

void GetTime()
{
    Serial.println("Get Time");
    Serial.print(rtc.getHours(), DEC);
    Serial.print(":");
    Serial.print(rtc.getMinutes(), DEC);
    Serial.print(":");
    Serial.print(rtc.getSeconds(), DEC);
    Serial.print(" -- ");
    Serial.print(rtc.getDay(), DEC);
    Serial.print("/");
    Serial.print(rtc.getMonth(), DEC);
    Serial.print("/");
    Serial.println(rtc.getYear(), DEC);

}

void SetTime()
{
    Serial.println("Set Time");
    //digitalWrite(13, LOW);
    Serial.print("Year");
    GetCharFromSerial();
    ore=atoi(inString); //Use the same variables for Year..
    Serial.print("Month");
    GetCharFromSerial();
    minuti=atoi(inString);
    Serial.print("Day");
    GetCharFromSerial();
    secondi=atoi(inString);

    rtc.stopRTC();
    rtc.setDate(ore,minuti,secondi);
    rtc.startRTC();

    Serial.print("Hour");
    GetCharFromSerial();
    ore=atoi(inString); //Use the same variables for Year..
    Serial.print("Minutes");
    GetCharFromSerial();
    minuti=atoi(inString);
    Serial.print("Seconds");
    GetCharFromSerial();
    secondi=atoi(inString);

    rtc.stopRTC();
    rtc.setTime(ore, minuti, secondi);
    rtc.startRTC();

    Serial.print("RTC changed->");
    GetTime();
}

void StartAcq()
{
    Serial.println("Start Acquisition");
    if (InAcq==true) return;
    // initialize Timer1
    cli();         // disable global interrupts
    TCCR1A = 0;    // set entire TCCR1A register to 0
    TCCR1B = 0;

    // enable Timer1 overflow interrupt:
    TIMSK1 = (1 << TOIE1);

    // Set CS10 bit so timer runs at clock speed:
    TCCR1B |= (1 << CS10);
    TCCR2B &= ~(1 << CS11);
    TCCR1B |= (1 << CS12);
    //Serial.println(TCCR1B);
    // enable global interrupts:
    sei();
    InAcq=true;

}

void Download()
{
    Serial.println("Download Data");
}

void StopAcq()
{
    Serial.println("Stop Acquisition");
    cli();         // disable global interrupts
    TCCR1A = 0;    // set entire TCCR1A register to 0
    TCCR1B = 0;
    sei();
    InAcq=false;

}

void PrintMenu()
{
    Serial.println("1 Start Acquisition");
    Serial.println("3 Stop Acquisition");
    Serial.println("8 Info");
    Serial.println("T Get Time");
    Serial.println("t Set Time");
    Serial.println("--------------------");
    Serial.println("Type the number and press enter");
}

void ParseMenu(char Stringa)
{
    Serial.println("Parse Menu");
    switch (Stringa) {
    case '1':
      StartAcq();
      break;
    case '2':
      Download();
      break;
    case '3':
      StopAcq();
      break;
    case '8':
      Serial.print("Acquisition->");
      Serial.println(InAcq,DEC);
      break;
    case 'T':
      GetTime();
      break;
    case 't':
      SetTime();
      break;
    default:
      Serial.print("Command Unknown! ->");
      Serial.println(Stringa,HEX);
    }
}

void setup()
{
	Serial.begin(9600);

	// initialize the digital pin as an output.
	// Pin 13 has an LED connected on most Arduino boards:
	pinMode(13, OUTPUT);

	//Initialize swRTC
	rtc.stopRTC();
	//rtc.setClockWithTimestamp(__TIMESTAMP__);
	rtc.setDate(DAY,MONTH+1,YEAR);
	//MyString=__TIMESTAMP__;
	Stringa2[0]=__TIMESTAMP__[11];
	Stringa2[1]=__TIMESTAMP__[12];
	Stringa2[2]=0;
	ore = atoi(Stringa2);

	Stringa2[0]=__TIMESTAMP__[14];
	Stringa2[1]=__TIMESTAMP__[15];
	minuti = atoi(Stringa2);

	Stringa2[0]=__TIMESTAMP__[17];
	Stringa2[1]=__TIMESTAMP__[18];
	secondi = atoi(Stringa2);
/*
	Serial.print(ore);
	Serial.print(":");
	Serial.print(minuti);
	Serial.print(":");
	Serial.println(secondi);
*/
	rtc.setTime(ore, minuti, secondi);

	rtc.startRTC();
	GetTime();

	// initialize Timer1
    cli();         // disable global interrupts
    TCCR1A = 0;    // set entire TCCR1A register to 0
    TCCR1B = 0;

    // enable Timer1 overflow interrupt:
    TIMSK1 = (1 << TOIE1);

    // Set CS10 bit so timer runs at clock speed:
    TCCR1B |= (1 << CS10);
    TCCR2B &= ~(1 << CS11);
    TCCR1B |= (1 << CS12);
    //Serial.println(TCCR1B);
    // enable global interrupts:
    sei();
    InAcq=true;

}

void loop()
{
    PrintMenu();
    GetCharFromSerial();

    ParseMenu(comm);
    //Serial.println(__TIMESTAMP__);


}

ISR(TIMER1_OVF_vect)
{
  digitalWrite(LEDPIN, !digitalRead(LEDPIN));
  
  if (rtc.getMinutes()%5 == 0) {
  
    if (AcqDone == false) {  //Siccome il controllo viene fatto ogni 30 secondi evita
      GetTime();        //che la routine scatti 2 volte ad es a 05 e 35 secondi
      //Serial.println(); //usando la boolean AcqDone
      AcqDone=true;
    }
    else {
      /*
      Serial.println(AcqDone);
      */
    }
  } 
  else
  {
    AcqDone=false;
  }
}

Toh … questo lo compila correttamente :wink:

Guglielmo

zoomx: Sembra proprio che se i caratteri vengono mandati uno alla volta, digitandoli da tastiera, non funzioni anche se i caratteri arrivano all'Arduino come posso vedere dal LED. Inoltre ho l'eco locale per cui vedo che il return c'è, in Hyperterminal infatti ritorna all'inizio della riga perché non c'è il LineFeed.

Funziona solo se metto la stringa in una casella e questa viene sparata tutta in una volta, sia nell'IDE sia nel programma terminale che uso.

Ma è normale che faccia così. Il codice che hai pubblicato contiene ad un certo punto un blocco di codice controllato da un while:

while (inByte != terminatingChar && Serial.available() > 0)

Questo dice: cicla mentre inByte è diverso da "invio" E c'è qualcosa nel buffer seriale. Se il tuo terminale spedisce il carattere non appena è premuto, l'Arduino lo riceve ed il buffer seriale contiene 1 carattere. Questo viene prelevato da quel ciclo while e perciò svuotato. Essendo vuoto, il programma esce dal ciclo while e va ad analizzare i dati che ha ricevuto. Se invece spedisci tutto insieme, ovviamente funziona.

inizio a sospettare che la routine freeram del playground non funzioni

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

e che in realtà io abbia problemi di RAM. O ci siano ANCHE problemi di RAM i cui sintomi sono i reset durante l'esecuzione. A stampa dice che ci sono oltre 800 byte.

@gpb01 Io ho fatto un casino, non PaoloP! Comunque ho controllato la tua versione con la mia, dopo aver rimosso il commento e coincidono. La tua ha in più un else {} che io ho eliminato ma che forse rimetterò, riempendolo, una volta che funzionerà.

@leo72 A me serviva una routine che prendesse anche dei caratteri digitati da un terminale, per cui l'alternativa non va bene. Sono tornato alla versione iniziale che funziona.

Non è possibile che non funzioni per quanto è banale.
Con int v; alloca una variabile nello stack
Con il comando return calcola la differenza tra l’indirizzo della variabile appena allocata, quindi il limite dello stack, e l’indirizzo dell’heap, oppure il brkval se sono state allocate variabili col malloc.

Ti ricordo la distribuzione della memoria negli ATmega

Ok, freeram deve funzionare, allora.

Non mi spiego, quindi, i reset.

Faccio come in Windows, allora. Torno alla ultima versione funzionante (del datalogger) e aggiungo le cose poco a poco. Avrò fatto il passo più lungo della gamba.

Grazie :)

Dopo ulteriori esperimenti credo proprio che sia stato un problema di memoria RAM. Ho aggiunto la sola libreria SdFat e tolto la parte dell'interrupt e non riuscivo a scrivere su SD oppure avevo dei reset. Ho eliminato la libreria del DS1307 e adesso la scrittura su SD funziona.

Prova usando altre librerie per scrivere sulla SD, non esiste solo quella ufficiale. Anzi, questa è abbastanza esosa in termini di risorse. Esiste anche una piccola libreria, PetitFatFs, che offre un basso impatto di risorse e funzioni base di accesso alla SD: http://elm-chan.org/fsw/ff/00index_p.html

Ho usato la SdFat ma adesso proverò il tuo suggerimento. Grazie! :)

Edit: non ci sono ancora riuscito perché tutti gli esempi che trovo riguardano la lettura e non la scrittura.

Sono tornato un po' indietro, ho eliminato il codice dell'interrupt ma già quello di scrittura su SD mi ha dato problemi. Ho aperto un nuovo argomento qui: http://forum.arduino.cc/index.php?topic=166840.0 dove sembra che abbia risolto provando varie librerie fino a trovare quella giusta. Resta sempre il dubbio che ci siano problemi di memoria.

zoomx: Resta sempre il dubbio che ci siano problemi di memoria.

Con la funzione su citata, stampa su seriale la RAM libera da diversi punti della routine di accesso alla SD e guarda se durante le operazioni di lettura/scrittura la RAM vari in modo considerevole.

Ho messo il codice completo, dove ci sono anche le stampe su seriale della RAM libera, qui

Ma non c’è nessuna variazione. Ho messo una stampa quando apro il file, quando scrivo la breve frase di prova e quando chiudo.
Per adesso ho risolto cambiando libreria ma non sono riuscito a capire il perché. Ho messo tutti nell’altro topic perché qui mi sembra ormai OT.