Problemi d'instabilità

Buonasera a tutti, Ho elaborato uno sketch per comandare un LCD di 7236 byte (22%) e con 1578 byte di variabili globali (77%).

Quando compilo su UNO e nano mi compare la scritta “Poca memoria disponibile, potrebbero presentarsi problemi di stabilità”.

Considerato che ho problemi di spazio e vorrei utilizzare un nano, esistono semplici accorgimenti per aumentare la capacità di memoria? Ovviamente senza modificare il programma che allego.

#include <Wire.h>
#include <LiquidCrystal.h>

#define BUTTON A0
#define BACKLIGHT 10

String v_testo;
int X = 0;
int X_old = 0;
int ripeti = 0;
int strLength = 0;
boolean blacklightStatus = false;
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

void setup() {
  Serial.println("** Trasmettitore per portatile - Slave **");
  Serial.println("** Release 2.0 **");
  Serial.println("** Collegamento I2C **");
  Serial.println("** Sketch: Slave_LCD_2 **");
  Serial.println("** Enrico Bianchetto - 25.09.18 **");
  
  Serial.begin(9600);
  Wire.begin(7);

  Wire.onReceive(ricevi);
  pinMode( BACKLIGHT, OUTPUT );
  digitalWrite( BACKLIGHT, LOW );
  lcd.begin(16, 1);
  digitalWrite( BACKLIGHT, HIGH );
}

void loop() {
  if (X > 0) {
    X_old = X;
    lcd.setCursor(16, 0);
    testo();
    Serial.println(v_testo);
    strLength = v_testo.length();
    Serial.println(strLength);
    
    while (X == X_old) {
      lcd.println(v_testo); // mi stampa su LCD la stringa
      for (int pCounter = 1; pCounter < strLength; pCounter++) {
        // scroll one position left:
        lcd.scrollDisplayLeft();
        //       wait a bit:
        delay(400);
        ricevi();
        if (X != X_old) {
          pCounter = strLength + 1;
          lcd.clear();
        }
      }
    }
  }
}

void ricevi() {

  int input = 0;
  while (Wire.available()) {
    input = Wire.read();
    X = input;
  }
}

void testo() {

  switch (X)  {

    case 4:
      v_testo = " Sistema acceso. ";
      break;

    case 5:
      v_testo = " Sistema spento. ";
      break;

    case 6:
      v_testo = " 2 minuti inizio tiri poi una volee di prova di 6 frecce 1 turno. ";
      break;

    case 7:
      v_testo = " 2 minuti inizio tiri poi gara di 6 frecce 1 turno. ";
      break;

    case 8:
      v_testo = " Gara di 6 frecce 1 turno. ";
      break;

    case 9:
      v_testo = " 2 minuti inizio tiri poi una volee di prova di 6 frecce 2 turni. ";
      break;

    case 10:
      v_testo = " 2 minuti inizio tiri poi gara di 6 frecce 2 turni. ";
      break;

    case 11:
      v_testo = " Gara di 6 frecce 2 turni. ";
      break;

    case 27:
      v_testo = " 2 minuti inizio tiri poi prima volee di prova di 3 frecce 1 turno. ";
      break;

    case 20:
      v_testo = " Seconda volee di prova di 3 frecce 1 turno. ";
      break;

    case 21:
      v_testo = " Gara di 3 frecce 1 turno. ";
      break;

    case 23:
      v_testo = " 2 minuti inizio tiri poi prima volee di prova di 3 frecce 2 turni. ";
      break;

    case 24:
      v_testo = " Seconda volee di prova di 3 frecce 2 turni. ";
      break;

    case 25:
      v_testo = " Gara di 3 frecce 2 turni. ";
      break;

    case 12:
      v_testo = " 2 minuti inizio tiri O.R. ";
      break;

    case 13:
      v_testo = " O.R. - Gara. ";
      break;

    case 22:
      v_testo = " 2 minuti inizio tiri poi gara di 3 frecce 1 turno- ";
      break;

    case 26:
      v_testo = " 2 minuti inizio tiri poi gara di 3 frecce 2 turni- ";
      break;

    case 14:
      v_testo = " Volee di recupero - 1 freccia. ";
      break;

    case 15:
      v_testo = " Volee di recupero - 2 frecce. ";
      break;

    case 16:
      v_testo = " Volee di recupero - 3 frecce";
      break;

    case 17:
      v_testo = " Volee di recupero - 4 frecce. ";
      break;

    case 18:
      v_testo = " Volee di recupero - 5 frecce. ";
      break;

    case 19:
      v_testo = " Volee di recupero - 6 frecce. ";
      break;

    case 31:
      v_testo = " Prova tempi - 120 sec. ";
      break;

    case 30:
      v_testo = " Prova tempi - 240 sec. ";
      break;
  }
}

Grazie + saluti

Enrico

esiste una funzione ' F( ' per mettere i testi nella memoria flash ed avere più RAM a disposizione

Serial.println(F("** Trasmettitore per portatile - Slave **"));
      .......

      v_testo = F ( " Sistema acceso. " ) ;

Oltre al consiglio di brunello, lo so che hai detto che non vuoi modificare il programma, ma aggiungo anche: qualche domandina...

  1. perché fai una funzione con switch case invece di un normalissimo array (anche questo ovviamente da mettere in flash, visto che il problema principale di memoria è proprio tutte quelle stringhe)?
  2. E poi perché agganci la funzione ricevi() con la Wire.onReceive() e poi lo chiami tu "a mano" nel loop()?
  3. E poi la funzione agganciata alla Wire dovrebbe avere un parametro int (vedi QUI) col quale puoi sapere quanti byte ha ricevuto
  4. E poi (finisco..) nella tua leggi() di fatto metti nella variabile X solo l'ULTIMO valore, mi pare strano ma sicuro che sia quello che realmente ti serve? Di fatto se lasci fare alla leggi() agganciata a Wire, nel parametro puoi sapere quanti byte ci sono nel buffer, leggerne solo uno (il primo) e quindi tornare, leggendo gli altri nei successivi cicli.

X Brunello: GRAZIE, con la funzione F sono sceso al 20% di spazio variabili!! eccezionale!!.

x docdoc: la mia formazione scolastica è di tutt'altro settore (.. e sono passati più di 50 anni dall'esame di maturità!), inoltre non ho mai avuto la possibilità di frequentare corsi di informatica e quel poco che so l'ho imparato provando e riprovando (e scopiazzando). Ho "soperto" Arduino ed il linguaggio C solo da poco più di 1 anno, prima mi limitavo a poche macro in Multiplan / Lotus prima ed Access poi.

D'accordo non è una scusante, ma gli array sono per me ancora una scoperta ed un interrogativo! Trovo molto più semplice l'utilizzo di "case".

Punto 2: giusto proverò ad eliminare il richiamo della variabile.

Punto 3: non ho messo alcun parametro in quanto ho scopiazzato un esempio dove no c'era. Funziona, per cui non penso modificherò il programma (salvo che tu non lo ritenga necessario).

Punto 4: direi di si, lo stato della X mi deve solo informare delle azioni che sono provocate dagli interruttori accesi prima che il valore sia inviato tramite HC12 ai tabelloni che devono sviluppare il programma.

Non so se sono stato sufficentemete chiaro nella risposta, sono comunque a disposizione per ulteriori chiarimenti.

Grazie (infine) per la risposta che mi ha fatto riflettere su alcuni errori concettuali che sto facendo e mi ha stimolato alla ricerca di una maggiore comprensione degli array.

Enrico

enrico24:
x docdoc: la mia formazione scolastica è di tutt'altro settore (.. e sono passati più di 50 anni dall'esame di maturità!)

Ah quindi il 24 del tuo nick non è l'età ma l'anno di nascita! :smiley:
(scusa, scherzo, figurati, il mio esame di maturità risale a 40 anni fa quindi siamo lì...).

ma gli array sono per me ancora una scoperta ed un interrogativo! Trovo molto più semplice l'utilizzo di "case".

Beh provo a darti ulteriori stimoli di miglioramento...

Intanto diciamo che la classe "String" (con la "S" maiuscola) con cui definisci la v_testo, seppure sia comoda da usare, è una delle cause di malfunzionamento e blocco di molti programmi su Arduino. Questo perché nonostante si possa gestire come le stringhe di altri linguaggi più di alto livello, quando tu cambi il valore di una String di fatto Arduino alloca una nuova zona di memoria per il nuovo valore, ma quella "vecchia" non ha modo di recuperarla in modo erfficiente in quanto altri linguaggi (C#, Java, eccetera) hanno il "garbage collector" ossia un processo che riconsoce le zone di memoria non più in uso e le "recupera". Arduino è una piccola MCU e non ha nulla di ciò. Quindi la classe String va evitata come la peste.
Al suo posto ci sono le stringhe C, definite come una sequenza di caratteri (char o char*), per le quali si deve specificare la dimensione (numero massimo di caratteri previsti più uno), ad esempio:

char v_testo[32] = "";

definisce una variabile stringa (una"stringa C" con la "s" minuscola) da 32 byte ossia potrà contenere al massimo 31 caratteri in quanto uno è sempre utilizzato per il "terminatore di stringa", ossia un byte a zero (per dire, la stringa "ciao" sono 5 char: 'c', 'i', 'a', 'o', 0x00. L'assegnazione serve per impostare a stringa nulla il valore di partenza

Poi ricorda che per convenzione gli identificativi tutti in maiuscolo identificano delle macro, come quelle che hai definito tu (es. "#define BUTTON A0"), ed i nomi delle variabili è meglio renderle iun minimo esplicative. Per queste ragioni, la tua variabile "X" è un poco "scorretta" in questo senso. Se nella "ricevi()" già leggi il valore in una variabile "input" perché non levi la "X" e usi la "input"?
Ossia:

int input = 0;
void ricevi() {
  while (Wire.available()) {
    input = Wire.read();
  }
}

Quindi in tutti i punti in cui usi "X" ci metti "input" che è ben più chiaro da leggere.

Detto questo, passiamo alla testo().
La tua funzione è "void testo()", ma se ci pensi prende in ingresso un valore (il contenuto della variabile intera "X", ora la "input") e restituisce una stringa: per renderla più chiara nel suo scopo e più coerente dovrebbe essere una "char* testo(int input)".
Le stringhe C però non si possono usare come le String, quindi se vuoi mettere nella nuova variabile char "v_testo" il valore restituito dalla funzione devi usare la funzione strcpy() ("STRing CoPy"):
strcpy(v_testo,testo(input));
Infine quando fai una switch() mettici sempre anche la "default", se per caso dovessi ricevere un valore inatteso.
Quindi la testo() diventa:

char* testo(int input) {
  switch (X)  {
    case 4:
      return " Sistema acceso. ";
    case 5:
      return " Sistema spento. ";
    case 6:
      return " 2 minuti inizio tiri poi una volee di prova di 6 frecce 1 turno. ";
	...
    default:
      return "*ERRORE*";
  }
}

Per il discorso array ti farò un post separato, se vuoi ora devo scappare, ciao!

x docdoc: … in quegli anni 10 anni erano (in termini scolastici) una vita!! Il ragionamento era solo e sempre lineare, l'insiemistica non era ancora arrivata nelle scuole ed i computer (intesi come oggi) non esistevano!

Comunque, OK per gli array, da quanto letto possono essere paragonati a delle matrici monodimensionali; ulteriori informazioni sono comunque e sempre ben accette. L'ignoraza è, a mio parese, sempre senza fine!

Veniamo a noi, d'accordo sulla sostituzione della "X"; avevo cercato in internet un qualcosa di simile allo "otherwise" per lo switch case ma non lo avevo trovato = inserito anche il default.

Poi … il buio.

Capito il concetto, ad ogno variazione di "input" (la "X" ritengo sia una svista) mi ritorna (return) una frase ma ....
Dove metto lo switch? Immagino sia una funzione a se stante, come viene richiamata? o deve essere subito prima di " lcd.println(strcpy(v_testo,testo(input))); " ?

Tra l'altro nelle svariate prove mi è ricomparso l'avviso di poca memoria!

ciao

Enrico

PS: da una richiesta Hardware siamo scivolati sul Software non è che qualcuno si "arrabbi"? E' possibile spostare tutto di sezione?

enrico24:
Dove metto lo switch? Immagino sia una funzione a se stante, come viene richiamata?

Te l'ho scritto, la "nuova" funzione "char* testo(int input)" ha lei dentro lo switch case.

o deve essere subito prima di " lcd.println(strcpy(v_testo,testo(input))); " ?

No, la strcpy copia il testo restituito dalla funzione "testo()" all'interno della variavbile v_testo.

Diciamo che "strcpy(v_testo,testo(input))" equivale ad una assegnazione del genere (non corretta qui con le stringhe C, quindi non devi farlo, serve solo per farti capire): "v_testo = testo(input);"
Quindi ad esempio per mostrare su LCD farai DUE istruzioni:

strcpy(v_testo,testo(input)); // Copia in v_testo il testo "calcolato" dalla funzione
lcd.println(v_testo); // Visualizza il testo

Tra l'altro nelle svariate prove mi è ricomparso l'avviso di poca memoria!

O ci rimandi il sorgente che hai usato (e che ti dà l'avviso) o non possiamo capire perché...

Buona domenica a tutti, innanzitutto mi scuso per la “latitanza”, ma ero impegnato su altri fronti-

Bene, ho capito dove inserire il richiamo della funzione “char* testo(int input)” dopo alcuni tentativi.

Ora funziona tutto.

Solo 2 cose ancora:
.

  1. esiste la possibilità di controllare la lunghezza di “v_testo” senza fare una funzione a parte ( e come ho trovato in internet) tipo “strcpy()”? In modo da uniformare la velocità di scorrimento?

  2. compare sempre l’avviso di poca memoria; allego quindi il sorgente come richiesto.

#include <Wire.h>
#include <LiquidCrystal.h>

#define BUTTON A0
#define BACKLIGHT 10

char v_testo[70] = "";
int input = 5;
int input_old = 0;
int ripeti = 0;
int strLength = 0;
boolean blacklightStatus = false;
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

void setup() {
  Serial.println(F("** Trasmettitore per portatile - Slave **"));
  Serial.println(F("** Release 3.1 **"));
  Serial.println(F("** Collegamento I2C **"));
  Serial.println(F("** Sketch: Slave_LCD_3_1 **"));
  Serial.println(F("** Enrico Bianchetto - 27.09.18 **"));

  Serial.begin(9600);
  Wire.begin(7);

  Wire.onReceive(ricevi);
  pinMode( BACKLIGHT, OUTPUT );
  digitalWrite( BACKLIGHT, LOW );
  lcd.begin(16, 0);
  digitalWrite( BACKLIGHT, HIGH );
}

void loop() {
  char* testo(int input); 
  if (input > 0) {
    input_old = input;
    lcd.setCursor(16, 0);
    strcpy(v_testo, testo(input));

    while (input == input_old) {
      lcd.println(v_testo); // mi stampa su LCD la stringa
      //       wait a bit:
      delay(250);
    }
  }
}

void ricevi() {
  while (Wire.available()) {
    input = Wire.read();
  }
}

char* testo(int input) {

  switch (input)  {

    case 4:
      return " Sistema acceso. ";

    case 5:
      return  " Sistema spento. ";

    case 6:
      return " 2 minuti inizio tiri poi una volee di prova di 6 frecce 1 turno. ";

    case 7:
      return " 2 minuti inizio tiri poi gara di 6 frecce 1 turno. ";

    case 8:
      return " Gara di 6 frecce 1 turno. ";

    case 9:
      return " 2 minuti inizio tiri poi una volee di prova di 6 frecce 2 turni. ";

    case 10:
      return " 2 minuti inizio tiri poi gara di 6 frecce 2 turni. ";

    case 11:
      return " Gara di 6 frecce 2 turni. ";

    case 27:
      return " 2 minuti inizio tiri poi prima volee di prova di 3 frecce 1 turno. ";

    case 20:
      return " Seconda volee di prova di 3 frecce 1 turno. ";

    case 21:
      return " Gara di 3 frecce 1 turno. ";

    case 23:
      return " 2 minuti inizio tiri poi prima volee di prova di 3 frecce 2 turni. ";

    case 24:
      return " Seconda volee di prova di 3 frecce 2 turni. ";

    case 25:
      return " Gara di 3 frecce 2 turni. ";

    case 12:
      return " 2 minuti inizio tiri O.R. ";

    case 13:
      return " O.R. - Gara. ";

    case 22:
      return " 2 minuti inizio tiri poi gara di 3 frecce 1 turno. ";

    case 26:
      return " 2 minuti inizio tiri poi gara di 3 frecce 2 turni. ";

    case 14:
      return " Volee di recupero - 1 freccia. ";

    case 15:
      return " Volee di recupero - 2 frecce. ";

    case 16:
      return " Volee di recupero - 3 frecce";

    case 17:
      return " Volee di recupero - 4 frecce. ";

    case 18:
      return " Volee di recupero - 5 frecce. ";

    case 19:
      return " Volee di recupero - 6 frecce. ";

    case 31:
      return " Prova tempi - 120 sec. ";

    case 30:
      return " Prova tempi - 240 sec. ";

    default:
      return  "*ERRORE*";
  }
}

Grazie anticipatamente + saluti

Enrico