Sketch che dopo poco si blocca senza cause apparenti

Buongiorno a tutti, sono circa due settimane che son piantato in modo definitivo con un piccolo progettino su arduino, basato su un progetto preso dal web e cucito su mie esigenze (possibilità di avere tasti e lcd su bus i2C).

Lo sketch viene caricato senza errori, tutto funziona per alcuni secondi (mai sempre gli stessi) e poco dopo tutto si "freeza", a display rimane chiaramente quello che era visualizzato in quel momento e i tasti non interagiscono più, per farlo ripartire occorre spegnere o resettare il tutto.

In pratica il progetto è composto da un display lcd su I2c , un I/O expander su I2c (sempre basato il pcf 8574) , con arduino viene generato il pilotaggio Step e Dir per pilotare un stepper motor driver e si regola la velocità del motore ed anche il verso di rotazione dello stesso.

Il progetto è stato tratto da un sito http://www.lucadentella.it/2013/05/30/allegro-a4988-e-arduino-3/

ed adattato per avere i controlli e LCD su I2c

Ho provato a cambiare sia arduino sia modello di arduino ma nulla cambia. Dopo pochi secondi si blocca.

Allego il file se qualche esperto e sopprattutto attento osservatore riesce a capire quali errori ho commesso, io ho perso la speranza.

Grazie 1000 a tutti

Motore_Feeder.ino.ino (4.92 KB)

Buona sera,
essendo il tuo primo post nella sezione Italiana del forum, nel rispetto del nostro regolamento, ti chiedo cortesemente di presentarti QUI (spiegando bene quali conoscenze hai di elettronica e di programmazione ... possibilmente evitando di scrivere solo una riga di saluto) e di leggere con MOLTA attenzione il su citato REGOLAMENTO ... Grazie. :slight_smile:

Fatto, era in pancia ora è on line

Grazie 1000 comunque per la segnalazione

Da una super rapida occhiata al codice non ho visto particolari pozzi neri dove si può infilare l'esecuzione.

Nella updateLCD vedo

Serial.print("mia var:");
Serial.println(actual_speed);

Quando si blocca stai monitorando la seriale?
Se si, smette di scrivere ovviamente, giusto?

A naso direi disturbi hardware che influiscono sull'esecuzione, non mi concentrerei unicamente sul software per la ricerca del problema.

Ho però dato solo una rapidissima occhiata al codice, quindi metto la pulce all'orecchio più che puntare il dito :wink:

Visto che non usi cose che potrebbero portare a blocchi più o meno "conosciuti" l'unica cosa che mi viene in mente, e che è capitata anche a me in passato (non bloccava il programma ma faceva impazzire l'lcd), è che aggiornare l'lcd ad ogni giro di loop sia la causa del blocco, prova a non aggiornare l'lcd e vedere se il programma continua a rispondere.
In ogni caso io procederei ad aggiornare il display se e solo se almeno un dato è variato altrimenti non farei nulla, più fine è aggiornare solo la porzione variata ma quella è un'altra storia

io ho avuto un problema analogo, sempre lcd che impazziva e comandi che non rispondevano più,perchè avevo montato tutto troppo volante e mi sa che ogni tanto qualcosa capitava, si toccava, interferiva... quando ho fissato tutto non ho più avuto il problema.

Rispondo a maubarzi dicendo che ho messo questa mattina per prova quel

Serial.print("mia var:");
Serial.println(actual_speed);

Proprio per vedere se a terminale vedevo qualcosa.

In pratica a terminale quando si blocca vedo continuare sempre l'output della stringa ma essendo bloccato il tastierino non riesco a variare più nulla, gli step invece non si fermano e continuano ad esser generati (sempre a quella frequenza prima del blocco).

Disturbi Hardware non direi anche perchè stiamo parlando ancora di prototipi su breadboard con terminali molto molto corti

a Fabpolli

Come posso far aggiornare l'lcd solo se faccio variazioni, scusami magari la domanda è banale per un esperto, ma io non saprei come fare

Grazie comunque intanto per le risposte

Un altra cosa che ho notato che mantenendo sempre il codice identico, e cambiando sia i tipi di arduino e schede i2c/lcd , il carattere custom cambia da modello a modello e non è mai come dovrebbe essere.

// custom LCD square symbol for progress bar
byte square_symbol[8] = {
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
};

In questo modo il carattere dovrebbe essere tutto nero ed invece a seconda del modello di arduino mi cambia, non è che questo mi dia problemi ma potrebbe essere sintomatico.

Grazie 1000

Se il serial continua a scrivere non c'è un blocco, quindi il problema è software, forse sei entrato in condizioni che non fanno passare più per la corretta lettura del tastierino.

gionata73:
Come posso far aggiornare l'lcd solo se faccio variazioni, scusami magari la domanda è banale per un esperto, ma io non saprei come fare

Ti devi fare della variabili uguali per tipo di quelle che usi per scrivere sull'lcd dove memorizzerai il valore della variabile "reale", poi ogni volta che entri nell'aggiornamento dell'lcd verifichi se la variabile ha un valore differente da quella memorizzata, se tutte uguali non fai nulla, altrimenti aggiorni, se non ho visto male tu stampi due valori devi fare una cosa tipo questa:

int actual_speed_old = -1000;
int actual_direction_old = -2;

void setup(){
....
}
...
void updateLCD() {
  if(actual_speed_old !=actual_speed || actual_direction_old != actual_direction)
  {
    ... tutte le tue operazioni sull'lcd...
    actual_speed_old = actual_speed;
    actual_direction_old = actual_direction;
  }
}

Per fare la prova commenta la parte del for che ti stampa la progressbar

/*
for(int i = 1; i <= 14; i++) {
    
    if(actual_speed > (5 * i) - 1) lcd.write(byte(0));
    else lcd.print(" ");
  }
*/

Modificato come indicato da maubarzi ma purtroppo il comportamento non cambia assolutamente.

A livello di seriale vedo il refresh continuo dell'ultimo valore ma non interagisce ne display ne pulsanti.

Dovrei provare a sostituire a parte dei pulsanti su I2C con pulsanti collegati ai pin digitali ma quando avevo fatto la prova collegando la tastiera dello shield configurata sulla porta a0 (essendo una tastiera che funziona a partitori di tensione) si bloccava comunque.

Questo progetto non vedrà mail la luce temo! :confused:

#include <Wire.h>
#include <TimerOne.h>
#include <LiquidCrystal_I2C.h>

#include "Arduino.h"
#include "PCF8574.h"  
// Set i2c address
PCF8574 pcf8574(0x3C);

// buttons code 
#define btnRIGHT  0
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5

// directions
#define FORWARD   HIGH
#define BACKWARD  LOW

// debounce time (milliseconds)
#define DEBOUNCE_TIME  200

// PINs for Pololu controller
#define PIN_STEP  2
#define PIN_DIR   3

// lookup table speed - ticks (interrupts)
//const int speed_ticks[] = {-1, 600, 300, 200, 150, 120, 100, 86, 75, 67, 60, 55, 50, 46, 43};
//const int speed_ticks[] = {-1, 300, 150, 100, 75, 60, 50, 43, 37, 33, 30, 27, 25, 23, 10};
const int speed_ticks[] = {-1, 100, 90, 80, 70, 60, 50, 40, 35, 30, 25, 20, 15, 10, 8};//questa è l'array migliore per la mia situazione

// global variables
LiquidCrystal_I2C lcd(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

int actual_speed;
int actual_direction;

int actual_speed_old = -1000;
int actual_direction_old = -2;

int ticks;
int tick_count;

int button;
boolean debounce;
int previous_time;

// custom LCD square symbol for progress bar
byte square_symbol[8] = {
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
};

// string constants
char forward_arrow[] = "-->";
char backward_arrow[] = "<--";

void setup() {

for(int i=0;i<8;i++) {
    pcf8574.pinMode(i, INPUT);
  }
  pcf8574.begin();
  
  lcd.begin(16,2);
  lcd.setCursor(0,0);
  lcd.backlight();
  Serial.begin(9600);

  //Il Timer1 viene programmato per generare un interrupt ogni 0.1 msec, quindi 10 interrupt ogni msec
  //Timer1.initialize(20);  1 step / 2ms cioè (per un motore da 200 passi / giro) un giro ogni 0,4 secondi = 150 giri / min
 Timer1.initialize(100);
 Timer1.attachInterrupt(timerIsr);

  // init LCD and custom symbol  
  //lcd.init();
  //lcd.begin(16, 2);
  //lcd.setCursor(0,0);
  //lcd.createChar(0, square_symbol);
  
  // pins direction
  pinMode(PIN_STEP, OUTPUT);
  pinMode(PIN_DIR, OUTPUT);
  
  // initial values
  actual_speed = 0;
  actual_direction = FORWARD;
  tick_count = 0;
  ticks = -1;
  debounce = false;

  digitalWrite(PIN_DIR, actual_direction);  
  updateLCD();
}
  
void loop() {
  
  // check if debounce active
  if(debounce) {
    button = btnNONE;
    if(millis() > previous_time + DEBOUNCE_TIME) debounce = false;
  } else button = read_buttons();
  
  // if a button is pressed, start debounce time
  if(button != btnNONE) {
    
    previous_time = millis();
    debounce = true;  
  }
    
  // check which button was pressed
  switch(button) {
    
    case btnUP:
      increase_speed();
      break;
    case btnDOWN:
      decrease_speed();
      break;
    case btnLEFT:
      change_direction(BACKWARD);
      break;
    case btnRIGHT:
      change_direction(FORWARD);
      break;
   case btnSELECT:
     emergency_stop();
     break;
  }
  
  // finally update the LCD
  updateLCD();
}

// increase speed if it's below the max (70)
void increase_speed() {
  
  if(actual_speed < 70) {
    actual_speed += 5;
    tick_count = 0;
    ticks = speed_ticks[actual_speed / 5];
  }
}

// decrease speed if it's above the min (0)
void decrease_speed() {
  
  if(actual_speed > 0) {
    actual_speed -= 5;
    tick_count = 0;
    ticks = speed_ticks[actual_speed / 5];
  }
}

// change direction if needed
void change_direction(int new_direction) {
  
  if(actual_direction != new_direction) {
    actual_direction = new_direction;
    digitalWrite(PIN_DIR, actual_direction);
  }
}
// emergency stop: speed 0
void emergency_stop() {
 actual_speed = 60;
 tick_count = 0;
 ticks = speed_ticks[actual_speed / 5];

}

void updateLCD() {
  if(actual_speed_old !=actual_speed || actual_direction_old != actual_direction)
  {
   // print first line:
  // Speed: % --> (or <--)
  lcd.setCursor(0,0);
  lcd.print("Speed: ");
  lcd.print(actual_speed);
  lcd.print("% ");
    actual_speed_old = actual_speed;
    actual_direction_old = actual_direction;
  }
  
Serial.print("mia var:"); 
Serial.println(actual_speed);

  lcd.setCursor(13,0);
  if(actual_direction == FORWARD) lcd.print(forward_arrow);
  else lcd.print(backward_arrow);
  
  // print second line:
  // progress bar [#####         ]
  // 15 speed steps: 0 - 5 - 10 - ... - 70
  lcd.setCursor(0,1);
  lcd.print("[");
 
  for(int i = 1; i <= 14; i++) {
    
    if(actual_speed > (5 * i) - 1) lcd.write(byte(0));
    else lcd.print(" ");
  }
 
  lcd.print("]");
}

// timer1 interrupt function
void timerIsr() {

  if(actual_speed == 0) return;
  
  tick_count++;
  
  if(tick_count == ticks) {  
    
    // make a step
    digitalWrite(PIN_STEP, HIGH);
    digitalWrite(PIN_STEP, LOW);
    
    tick_count = 0;
  }
}

// read buttons connected to a single analog pin
int read_buttons() {
  
  PCF8574::DigitalInput val = pcf8574.digitalReadAll();
 
 if (val.p0==HIGH) return btnNONE;
 if (val.p1==HIGH)   return btnRIGHT;  
 if (val.p2==HIGH)  return btnUP; 
 if (val.p3==HIGH)  return btnDOWN; 
 if (val.p4==HIGH)  return btnLEFT; 
 if (val.p5==HIGH)  return btnSELECT; 
 delay (50);  
}

hai la serial print che funziona quindi la puoi usare per debuggare e vedere se il programma passa ancora su certi punti e vedi anche con che valore ci arriva.

Ad es. puoi farti restituire il valore della read_buttons che se non pigi nulla fa solo un delay(50)?
Non ho presente cosa ritorna il tastierino, scusami.

Se non vuoi controllare sempre le variabili, puoi aggiornare il display ogni secondo o mezzo secondo, usando millis(), con qualcosa del tipo:
if(millis-t1>500) {t1=millis(); lcd.print... ... ...}

In monitor seriale vedo solo :

mia var:15
mia var:15
mia var:15
...... etc etc all'infinito

15 ad esempio è lo speed visualizzato dal display (anche se sia display che pulsanti son bloccati )

Ipotizzo sia display che pulsanti , ma potrebbero anche solo essere i pulsanti, ma non potendo verificare ...

Scusami datman se son un pochino agli albori ma non capisco cosa intendi io debba mettere quei valori!

if(millis-t1>500) {t1=millis(); lcd.print... ... ...}

Non hai rimosso il for che stampa la progressbar... prova a rimuoverlo fosse mai quello...

metti una Serial.print su read_buttons e vedi cosa ti ritorna la pcf8574.digitalReadAll()

e soprattutto cosa ti ritorna la read_buttons

Quello che intende Datman è di usare un principio analogo al debounce che usi già per non aggiornare l'LCD per 1 secondo o un secondo e mezzo dopo ogni aggoirnamento.

Ho provato a commentare anche la parte che genera la progress ba ma si blocca comunque.

Ho messo anche sotto controllo seriale lo stato dei bottoni (l'ho fatto solo con i due bottoni di speed up e speed down per provare).

Quando si blocca il display anche nel monitor seriale non registro più variazioni premendo i pulsanti.

E' come se l'intero I2c andasse a farsi friggere.

La linea I2C deve essere corta, eh!

A questo punto scatta il metodo rasoiata di occam, stacca tutto ciò che non è necessario (motori, relé, RTC, ecc. ecc.) e lascia collegato SOLO il display.
Fai un piccolo programma che aggiorna il display ogni mezzo secondo e che dichiara e inizializza solo lo stretto necessario a far andare il display, quindi via TimerOne, e roba simile, lascia solo la LiquidCrystal e tutto ciò che le necessita e una roba simile a questa:

unsigned long idx = 0;

void setuo()
{
 ...inizializzazione ecc...
}

void loop()
{
  lcd.setCursor(0,0);
  lcd.print("Indice: ");
  lcd.print(idx++);
  delay(500);
}

E vedi se anche così si blocca o se nel display vedi scorrere i numeri due per secondo.
Se così funziona allora aggiungi la gestione dei pulsanti e verifica che il sistema continui a sentirne la pressione e aggiornare il display di conseguenza
Se anche così andasse alora aggiungi man mano le altre parti una alla volta fino a trovare il colpevole

ma ci entra nella read_buttons?
prova un serial print come prima istruzione