Random Freeze di Arduino

Buonasera,
dopo vari test non sono riuscito a trovare una soluzione quindi chiedo aiuto a voi. L’algoritmo è molto semplice, un cronometro e delle letture di temperatura, ma arduino “freeza” in modo random

Hardware Utilizzato:

  • Arduino Nano;
  • Ssd1309 (libreria ssd1306, modalità i2c);
    -ds18b20;
    -max31855 (rimosso);

leggendo sul forum ho testato varie soluzioni come :

  • Dividere il main loop in varie funzioni;
  • Evitare la classe string;
  • Ottimizzare il codice;

inoltre, ho testato il codice su diversi arduino nano ed uno, oltre a cambiare schermo (ssd1309 con libreria ssd1306, utilizzato in modalità i2c). Ovviamente ho provato il tutto senza componenti, ovvero solo con arduino, e sono incappato nello stesso problema. Per scrupolo, ho testato un semplice algoritmo che stampa millis() sull’ssd1309 per vedere se il problema fosse a livello di librerie, ma testato per oltre un’ora non ha dato problemi.
L’

questo è il codice, ci sono variabili inutilizzate che provengono dal codice completo che ho cercato di ottimizzare.

#include <Adafruit_SSD1306.h>
#include <splash.h>
#include <OneWire.h>
#include <DallasTemperature.h>


#define ONE_WIRE_BUS 4
#define SCREEN_WIDTH 128 
#define SCREEN_HEIGHT 64 
#define OLED_RESET     -1 
#define SCREEN_ADDRESS 0x3C

OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature temp(&oneWire);
DeviceAddress tempDeviceAddress;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

volatile float t1=0;
volatile float tlast=0;
volatile float tbest=0;
volatile float x0=0;
volatile int giro=0;
float lastTime= millis();
float tempc=0;
float x=0;
int y=0;
float x1=0;
int y1=0;
float x2=0;
int y2=0;
float flag=0;

void setup()   { 
  delay(100);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  attachInterrupt(0, tempo, FALLING);
  temp.begin();
  temp.setResolution(tempDeviceAddress, 9);
  temp.setWaitForConversion(false);
  temp.requestTemperatures();
  display.setTextWrap(false);
  display.setTextColor(WHITE);
  display.setTextSize(1);
  display.clearDisplay();
  display.display();
  delay(1000);
}

void loop() {
  display.setCursor(0,4);
  display.print(F("LAP"));
  if(giro<10){
    display.setCursor(6,13);
  }else{
    display.setCursor(3,13);    
  }
  display.print(giro);
  display.drawLine(0,24,128,24,WHITE);
  display.drawLine(0,25,128,25,WHITE);
  display.setCursor(18,30);
  display.print(F("BEST:"));
  display.setCursor(82,30);
  display.print(F("LAST:"));
  display.setTextSize(2);
  display.setCursor(16,4);
  display.print(F(":"));
  display.setCursor(30,4);
  timer();
  if(giro==0){
     display.print(F("0.0.000"));
  }else{
  display.print(x,3);
  }
  display.setTextSize(1);
  tempi();
  display.setCursor(10,42);
  display.print(x1,3);
  display.setCursor(75,42);
  display.print(x2,3);
  display.drawLine(64,26,64,64,WHITE);
  if(millis()-lastTime>1000){
    tempc=temp.getTempCByIndex(0);
    temp.requestTemperatures();
    lastTime = millis();
  }
  display.drawLine(0,52,128,52,WHITE);
  display.setCursor(0,57);
  display.println(tempc);
  display.drawLine(0,52,128,52,WHITE);
  display.display();
  delay(10);
  display.clearDisplay();
  }

void tempi(){
  float ses= 60;
  
  y1=tbest/ses;
  x1=tbest-(ses*y1);
  y2=tlast/ses;
  x2=tlast-(ses*y2);
  
}
void timer(){
  float mille=1000;
  float ses=60;
  
  x=((millis()/mille)-flag)-x0;
  if(x>60){
    y=y+1;
    flag=flag+ses;
  }
}

void tempo() {
  float mille=1000;
  float ses= 60;
  
  float t2=millis()/mille;
  tlast=t2-t1;
  t1=t2;
  if(tbest==0){
    tbest=tlast;
  }
  if(tlast<tbest){
    tbest=tlast;
    }
  x0=x0+(tlast-(ses*y));
  y=0;
  if(giro==0){
    tbest=0;
    tlast=0;
  }
  giro=giro+1;
}

Provato da solo x 1h no freeze, quindi con tutto insieme frizza prima?
Usi interrupt x cosa? Dai un pò più di info, almeno io no ho capito bene quel che fa il programma.
Ome alimenti il tutto?

Buongiorno, scusami ma ieri ho buttato giù due righe dopo due giorni di test e non ho spiegato bene il tutto.

In breve, è una telemetria di un gokart che tramite interrupt (reedswitch che si chiude su banda magnetica) legge i tempi sul giro, oltre a leggere contemporaneamente la temperatura da un ds18b20, inizialmente era presente anche un max6675 cambiato poi con un max31855. Comunque ho rimosso il max31855 per cercare di risolvere questo problema.
Il funzionamento è quello di vedere a schermo il timer che parte da 0 (x) verificatosi l’interrupt e va ad incrementare, successivamente al secondo passaggio (interrupt) regista il tempo ed il timer riparte da 0. Per far ciò alla variabile x (che ho basato su millis()) di volta in volta vado a sottrare i tempi sul giro. Ovviamente ho scomposto i tempi in minuti (y) e secondi (x) ma la y non viene stampata poiché usavo la classe string che ho tolto cercando di risolvere il problema, inutilmente.

Attualmente sono presenti il Reed switch con il ds18b20 (testato anche senza quest’ultimo, stesso problema) e lo schermo SSD1309.

Nel void loop sono presenti solo formattazioni dello schermo con la richiesta asincrona di temperatura per per evitare di aspettare 750ms (troppi). Poi ci sono 3 funzioni: una chiamata dall’interrupt e due chiamate dal void loop (così da liberare RAM una volta concluse)

Void tempi(): stampa a schermo i tempi sul giro passati dalla void tempo (), migliore ed ultimo

Void timer(): effettua le sottrazioni alla x al fine di ripartire da 0 al passaggio sulla banda magnetica. A millis convertito in secondi, vado a sottrare una flag ogni 60 secondi del valore di 60, al fine di emulare i secondi ed i minuti ed una x0 che somma tutti i secondi residui dei giri precedenti. La y sono i minuti

Void tempo (): viene chiamata dall’interrupt una volta passato sulla banda magnetica registra i tempi sul giro, verifica se sono migliori e calcola i minuti ed i secondi dell’ultimo giro da sottrarre alla x.

Il freeze ( blocco dell’arduino, schermo acceso ma bloccato, led di Arduino bloccati) si verifica a banco non con il kart in moto, sono reduce da vari fallimenti a causa di disturbi di quest’ultimo. Arduino è schermato e sono utilizzati cavi schermati.
Non so come uscirne, il bello è che ho fatto una precedente implementazione con ssh1106 + ds18b20 e funziona perfettamente, utilizzato in pista senza codice ottimizzato (viene usata anche la classe string). Il problema è che per compilare il tutto ho lavorato su un portatile che ho sostituito, da quando compilo sul nuovo pc ho casualmente notato il problema essendo random (non ci lavoravo da un po’). Ho ovviamente provato il vecchio codice ma ho lo stesso problema, anche se collego solo Arduino al pc vedo che dopo tot tempo freeza. Inoltre, ho fatto una reinstallazione pulita di Arduino con tutte le librerie, niente. Ho l’impressione che vada in overflow ma non comprendo dove sbaglio.

L’alimentazione è prettamente da USB del PC, provato anche con una litio 18650+ step up, stesso problema. Nella vecchia implementazione usavo una classica 9v.

Ho provato a sostituire arduino nano, utilizzare arduino uno ecc. ma ho lo stesso problema. L’unica cosa che non ho ancora sperimentato è tornare al vecchio SSH1106 che ho abbandonato per uno schermo più grande (ssd1309). Per escludere un blocco derivante da un problema con la libreria ho compilato un semplice sketch dove stampo a schermo (ssd1309) millis(), dopo un’ora di test non ho avuto problemi. Ciò mi fa pensare che il problema è l’utilizzo della RAM.
Grazie per le eventuali risposte

Cosa dice il messaggio dell’IDE al termine della compilazione? Quanta RAM è occupata?

Il codice che ho riportato nel primo post utilizza:

  • 19126 byte (62%) dello spazio per i programmi
  • 459 byte (22%) della memoria dinamica

Il codice completo, con string, termocoppia ed altri fronzoli utilizzava:

  • 24854 byte (80%) dello spazio per i programmi
  • 669 byte (32%) della memoria dinamica

Con il codice completo ho provato ad utilizzare anche la funzione freeRam(), presa da github ma con scarsi risultati, mi dava un 381 (byte?) costante. Probabilmente la utilizzavo in modo errato.

Edit:
Andando per esclusione, può essere un problema della libreria che gestisce l’ssd1306? Magari gestisce male la RAM lasciando poco spazio alle variabili del cronometro. Questo spiegherebbe perché con l’ssh1106 non ho problemi. Possibile?

Edit 2:
Ho iniziato una serie di test con la libreria ssd1306Ascii, ovvero una versione “solo testo”.
In fase di compilazione ottengo:

  • 9146 byte (29%) dello spazio per i programmi
  • 159 byte (7%) della memoria dinamica

il problema è che questa libreria è molto scarna, inoltre il setcursor per le righe va a multipli di 8, per me una grande limitazione. Avete qualche libreria “light” da consigliare per l’ssd1306 o direttamente per il 1309? che ne pensate della U8g2?

Edit 3:
Risolto, il problema era la libreria adafruit che utilizzava troppe risorse, causando overflow con il mio codice.

:+1:

Alla fine quale lib per il display hai usato ?

Ho utilizzato la libreria ssd1306Ascii

Ssd1306Ascii

Ho effettuato due test da un’ora con batteria litio e step up a 9v collegato al Vin di Arduino. Nessun freeze, ieri modifico lo sketch (aggiungendo qualche stampa un più) ed ha iniziato nuovamente a freezare in modo Random :sweat_smile:.

Andando per esclusione, visto che ora il codice è veramente basico (usa il 7% della RAM, non ha classe string ecc), non credo sia ancora in problema lato software.

Leggendo su internet ho letto che un possibile problema può essere lo schermo, che essendo grande, tira troppa corrente e manda in palla arduino.
Ora sto effettuando dei test senza ssd1309 e tramite serial, stampo un ok per vedere se va ancora. Ho fatto già una prima ora ed adesso sto facendo la seconda.
Provo a misurare la corrente e vedere se rientro nei limiti dei 40mA. Intanto ho aggiunto un ceramico (104) tra vcc e grd dello schermo.
Consigli?

Edit:
Ho terminato le due ore di test senza schermo con esito positivo. Quindi in ordine:

  • ho saldato il condensatore C7 mancante sullo schermo (ho utilizzato un 475C al tantalio)
  • ho saldato un ceramico (104) tra vcc e grd sullo schermo
  • ho saldato una resistenza da 10k tra vcc e res dello schermo (è uno schermo spi-i2c quindi presenta 7 pin, di cui ne uso solo 4)
  • ho caricato la litio a 4.2v

ho fatto già una prima ora di test con la litio senza alcun freeze, ora sono a mezz’ora del secondo test da un’ora. Da quanto visto, credo che tutto il sistema sia molto sensibile all’alimentazione, i precedenti test dei giorni scorsi erano stati fatti a pila carica, scesa sotto i 3.7-3.6 volt ho iniziato ad avere problemi. Ovviamente la litio è nuova ed è molto sovradimensionata per il tutto (samsung 30q)