Risolto: Accendere/Spegnere relay da INPUT durante funzione millis()

Ciao a tutti,

vi spiego brevemente che cosa ho bisogno. Ho un Arduino Uno R3, con schermo LCD Nokia 5110 e un modulo da 4 relays. Ho creato un menu per poter automatizzare una serra incubatrice. Praticamente, in attesa che mi arrivi il RTC, sto usando la funzione millis per temporizzare i relays, tutto funziona bene ma non posso spegnere i relays quando decido io tramite input da pulsante. Ho fatto svariate ricerche ma senza risultati.

Sarei grato a chi mi possa consigliare su come poter accendere/spegnere i relays indipendentemente dalla funzione millis e, se fosse possibile, far si che millis continui il conteggio.

Mi spiego meglio con un esempio pratico: il relay numero 1 controlla le luci dell'incubatrice, si accende per 18 ore e si spegne per 6 ore. Durante il periodo che è acceso devo fare dei piccoli lavori di manutenzione e controllo delle piante quindi, per il fastidio della forte luce, ho bisogno di spegnere la luce manualmente tramite pulsante.

Allego il codice del progetto.

P.S. Non sono programmatore ma un semplice hobbista dal pollice verde. Il progetto è ancora un "work in progess".

Grazie a tutti!

incubatrice codice.ino (27.1 KB)

Mi sa' che hai sbagliato allegato
Edit: Era il file che dava errore

Comunque non vedo problematiche, devi inserire la lettura dei pulsanti fuori dalla millis() e di conseguenza spegnere il relay

illya95:
il relay numero 1 controlla le luci dell'incubatrice, si accende per 18 ore e si spegne per 6 ore. Durante il periodo che è acceso devo fare dei piccoli lavori di manutenzione e controllo delle piante quindi, per il fastidio della forte luce, ho bisogno di spegnere la luce manualmente tramite pulsante.

Allora, intanto mi (ti) chiedo perché hai usato i pin 0 ed 1 per i pulsanti:questi sono dedicati alla comunicazione seriale, e così facendo ti precludi uno dei principali modi per fare debugging. considerando anche che il tuo codice è abbastanza lungo e pieno di blocchi quasi tutti uguali (ti consiglio di studiare un poco gli array, ti semplifichi la vita enormemente....) è difficile capire quando qualcosa non funziona come ci si aspetta che faccia.

Comunque sia, attualmente hai così mappato i pin ():
0: UP
1: DOWN
2: OK
3: Display
4: Display
5: Display
6: Relè 1
7: Backlight
8: Relè 2
9: Relè 3
10: Relè 4
A0: pH sensor
A1: Temp

Intanto per avere il pieno controllo della configurazione ti consiglio di definire sempre TUTTI i pin in testa al programma, magari usando delle #define e non delle inutili variabili int che occupano spazio, e poi nel resto del codice usare SOLO quelli e mai i valori numerici. Cosa che tra l'altro ti permette anche di evitare persino alcuni commenti per "ricordare" l'uso del pin che stai modificando o leggendo, rendendo il listato ancora più leggibile...

Ti ricordo che anche i pin analogici si possono usare come digitali, per cui per prima cosa libera i pin 0 ed 1 cambiando la mappatura in:

0: (serial)
1: (serial)
2: OK
3: Display
4: Display
5: Display
6: Relè 1
7: Backlight
8: Relè 2
9: Relè 3
10: Relè 4
11: UP
12: DOWN
A0: pH sensor
A1: Temp
A2: STOP

Evito il pin 13 perché in genere lo uso come LED di stato. Una volta cambiate queste impostazioni (ovvero messe le #define all'inizio, e usato i relativi simboli nel resto del codice) vediamo come implementare la cosa, che potrebbe essere del tipo:

#define P_OK 2
#define P_LCD_RST 3
#define P_LCD_CE 4
#define P_LCD_DC 5
#define P_RLY1 6
#define P_BACKLIGHT 7
#define P_RLY2 8
#define P_RLY3 9
#define P_RLY4 10
#define P_UP 11
#define P_DOWN 12
#define P_PH A0
#define P_TEMP A1
#define P_STOP A3
...
bool stop = false;
...
//This are the pins for the Nokia 5110 LCD display - Adafruit_PCD8544 (D/C,CE,RST)
Adafruit_PCD8544 display = Adafruit_PCD8544 ( P_LCD_DC, P_LCD_CE, P_LCD_RST);
...
void setup() {
  ...
  pinMode(P_OK, INPUT_PULLUP);
  pinMode(P_RLY1, OUTPUT);
  pinMode(P_RLY2, OUTPUT);
  pinMode(P_RLY3, OUTPUT);
  pinMode(P_RLY4, OUTPUT);
  pinMode(P_UP, INPUT_PULLUP);
  pinMode(P_DOWN, INPUT_PULLUP);
  pinMode(P_STOP, INPUT_PULLUP);
  
  digitalWrite(P_BACKLIGHT,HIGH);
  ...
}


void loop() {
  
  digitalWrite(P_RLY1, relayone);
  digitalWrite(P_RLY2, relaytwo);
  digitalWrite(P_RLY3, relaythree);
  digitalWrite(P_RLY4, relayfour);
  ...
  if ( digitalRead(P_STOP) == LOW )
    stop = true;
  ... eccetera...

P.S. Non sono programmatore ma un semplice hobbista dal pollice verde. Il progetto è ancora un "work in progess".

Eh, si, l'avevo capito leggendo il codice... :wink:

brunello22:
Comunque non vedo problematiche, devi inserire la lettura dei pulsanti fuori dalla millis() e di conseguenza spegnere il relay

Non ti seguo. La lettura dei pulsanti è già fuori da millis(). Ora ho notato che sullo schermo lo stato dei relays cambia ma i relays non scattano. Hai qualche idea?

@docdoc

Ti ringrazio per la tua risposta tuttavia ritengo che per l'uso al quale è destinato il mio progetto possa andare bene anche cosi la mappatura dei pin.

Visto che hai letto il mio codice, e capito che non sono uno del mestiere, vorrei sapere se hai qualche consiglio pratico riguardo la mia questione. Come ho già risposto a @brunello22 , sullo schermo ho il cambio di stato dei relays ma non sul modulo relays cosi come l'input da pulsante per spegnere/accendere i relays: sullo schermo ok ma non sul modulo.

Ti ringrazio anticipatamente.

tu hai ad inizio loop questo:

void loop() {

  digitalWrite(6, relayone);
  digitalWrite(8, relaytwo);
  digitalWrite(9, relaythree);
  digitalWrite(10, relayfour);

poi vedo che in giro per il programma cambi sia valore di relayone e degli altri e anche richiami alcune funzioni per i relè...tipo:

      if (relayone)
      {
        relayone = false;
        turnRelayOff();
      }
      else
      {
        relayone = true;
        turnRelayOn();
      }

dove le due funzioni fanno:

void turnRelayOn()
{
  digitalWrite(6, LOW);
}

void turnRelayOff()
{
  digitalWrite(6, HIGH);
}

secondo me ci deve essere un solo punto in cui si fa il digitalWrite di un pin...e il valore che deve assumere deve essere impostato in un solo punto facendo tutte le verifiche e considerazioni che sono necessarie...non sparse nel programma.

illya95:
Ti ringrazio per la tua risposta tuttavia ritengo che per l'uso al quale è destinato il mio progetto possa andare bene anche cosi la mappatura dei pin.

Forse non hai capito.

Primo, il consiglio è quello di non usare i pin 0 ed 1 perché utili per fare debug dell'applicazione, cosa importante quando le cose non vanno come ti aspetti. Come in questo caso.

Secondo, la mappatura dei pin puoi farla pure come ti pare, ma se metti tutte le definizioni dei pin all'inizio del codice e possibilemte usando delle #define o almeno definirle come "const byte" (evitare delle inutili variabili per informazioni che non cambieranno mai nel codice), e nel resto del codice usi sempre quei simboli o costanti, hai il pieno controllo della configurazione.

E la combinazione di questi due fatti ti permetterebbe di avere un miglior controllo su tuo codice, e capire meglio come funziona il tuo codice o, in caso di problemi, di come non funziona o non fa quello che ti aspetti che faccia. Come in questo caso.

Per cui ti consiglio caldamente di fare le modifiche che ti ho consigliato, ma se vuoi mantenere un codice poco gestibile, mal strutturato e mal configurato, per me va benissimo.

ORSO2001:
secondo me ci deve essere un solo punto in cui si fa il digitalWrite di un pin...e il valore che deve assumere deve essere impostato in un solo punto facendo tutte le verifiche e considerazioni che sono necessarie...non sparse nel programma.

Eh, infatti stavo cercando di farglielo capire. :wink:

Ringrazio per le risposte ma abbiate pazienza. Vi spiego meglio la situazione, il codice che avete visto nell'allegato ci ho messo quasi 2 mesi a scriverlo quindi se ora dovessi "ordinarlo" per bene ci metterei altri 2 mesi probabilmente e non è il caso.

Deduco che di elettronica e di programmazione ve ne intendete parecchio ed è per questo che vorrei un consiglio pratico da voi per risolvere quel problemino che non mi fa funzionare i relays che scommetto voi l'avete già notato e risolto in un batter d'occhio.
Non mi sembra io chieda troppo e ad essere sinceri continuando a sottolineare il fatto che il mio codice è disordinato e mal strutturato non siete d'aiuto. Spero capiate il fatto che sono solo un hobbista con un Arduino in mano e che meglio di così non potrei riuscire.

Quindi, se vi fa piacere aiutare un pollice verde, io sono qui.

P.S. Sarebbe molto d'aiuto se mi faceste vedere dove e cosa devo modificare esattamente.

illya95:
il codice che avete visto nell'allegato ci ho messo quasi 2 mesi a scriverlo quindi se ora dovessi "ordinarlo" per bene ci metterei altri 2 mesi probabilmente e non è il caso.

Credo che la cosa sia più semplice di quanto tu non stia immaginando.

Capisco che avendo poca dimestichezza tu possa temere di "danneggiare" il codice ed impelagarti in problemi aggiuntivi, ma non si trata di riscrivere nulla ma, a parte le cose che ti ho scritto e che puoi copiare direttamente, fare solo una serie di sostituzioni (essenzialmente cercare tutti i digitalRead e digitalWrite e cambiare il numero del pin con il relativo simbolo) ed ottimizzazioni.

Prova questo listato che vedi in allegato, è il tuo sul quale ho apportato le modifiche che mi sembravano quantomeno necessarie e verifica se ti funziona tutto, così potrai partire da qui. Ovviamente ci sono tante altre ottimizzazioni possibili per migliorare la leggibilità del codice (che non è una questione "estetica" ma serve per avere più facilmente sott'occhio il flusso, in particolare per quelle miriadi di illeggibili "if ..else if ...") ma per ora evito. Tutte le parti che ho aggiunto o modificato le ho racchiuse tra linee di commento ("// -------------------------------").

Non mi sembra io chieda troppo e ad essere sinceri continuando a sottolineare il fatto che il mio codice è disordinato e mal strutturato non siete d'aiuto. Spero capiate il fatto che sono solo un hobbista con un Arduino in mano e che meglio di così non potrei riuscire.

Non è come dici, non è affatto una critica nei tuoi confronti. Proprio perché sei un hobbista e stai affrontando un programma di un migliaio di righe è ancora più importante cercare di apprendere come gestirlo al meglio, e quindi tu stesso capire anche come apportare le modifiche che chiedi, ed è esattamente quello che cerco di farti capire in modo che d'ora in avanti tu possa trovarti meglio.

Ma devi anche comprendere che per noi, che siamo "estranei" e se il tuo progetto funziona o meno potrebbe non interessarci direttamente, per poterti consigliare su come realizzare quella funzionalità che chiedi dovremmo prima capire come è fatto un listato da 1000 righe e strutturato in modo non proprio coerente (o quantomeno troppo "logorroico"), e farlo nel nostro tempo libero. Se mi metto a scriverti codice e consigli, è per aiutarti ma anche per aiutare tutti gli altri che potrebbero contribuire a farlo. E' lo spirito del forum, non credi? :wink:

Ora prova il codice allegato e fammi sapere, in ogni caso se hai domande scrivi qui.

incubatrice codice modificato.ino (27.9 KB)

Ho visto il codice da te modificato, hai ragione, è molto più pulito. Grazie.

Ho risolto il problema del perchè non funzionavano i relays: c'era il cavo dei 5v collegato male quindi non arrivava corrente. I relays funzionano bene di nuovo. Per quanto riguarda la mappatura dei pin, ci sono in uso 2 pin in più (pin 11 e 13) che non ho dovuto mappare in quanto non uso la libreria SPI. Per collegare il display Nokia sto usando un level shifter (5v 3.3v, 74HC4050). Il display usa 8 pin che passano attraverso il 4050 e vanno ad Arduino, per questo che non ho voluto entrare nel merito della mappatura ma focalizzarmi solo sul problema dei relays. So bene anche che dovrei evitare di usare RX e TX per collegare input specialmente se passa corrente (ti blocca il bootloader), ma era l'unico modo.

E davvero, per l'uso che ne ho bisogno io è più che perfetto. Comunque ti ringrazio per l'aiuto e per le dritte.

illya95:
Ho visto il codice da te modificato, hai ragione, è molto più pulito. Grazie.

Bene. Ma lo hai provato? Ovviamente dato che la configurazione è ora in testa al programma, puoi decidere di mappare i pin secondo quello che ritieni più opportuno per te, anche rimettere i pin 0 ed 1.

per questo che non ho voluto entrare nel merito della mappatura ma focalizzarmi solo sul problema dei relays. So bene anche che dovrei evitare di usare RX e TX per collegare input specialmente se passa corrente (ti blocca il bootloader), ma era l'unico modo.

E perché? Guarda che anche i pin analogici puoi usarli come digitale, come ti ho scritto in precedenza, quindi a parte A0 ed A1 ne hai almeno altri due, A2 ed A3 (se A4 ed A5 li usi per I2C, altrimenti anche questi sono disponibili).

Poi per il resto del codice ti consiglio di cercare di ottimizzarlo, vedrai che utilizzando questi metodi ti troverai non solo un codice più compatto e leggibile, ma ti sarà più facile apportare modifiche (a questo o ai tuoi futuri progetti)!

Potresti iniziare a compattare maggiormente almeno il loop(), ma non solo (anche la gestione delle pagine del menu), portando dentro ad apposite funzioni il codice per "blocchi logici", poi prova ad imparare ad usare gli array vedrai quanto sarà tutto più semplice.

Prendiamo ad esempio le varie funzioni accessorie come le "turnRelay*": mettendo i pin dei relè dentro ad un array puoi avere UNA sola funzione per accendere o spegnere qualsiasi relé!

Quindi invece di tutte queste righe:

...
#define P_RLY1 6
#define P_RLY2 8
#define P_RLY3 9
#define P_RLY4 10
...
void setup() {
...
  pinMode(P_RLY1, OUTPUT);
  pinMode(P_RLY2, OUTPUT);
  pinMode(P_RLY3, OUTPUT);
  pinMode(P_RLY4, OUTPUT);
...
... e tutte le righe dove fai turnRelayOff() e turnRelayOn() e  relayone = false o true...
}
...
void turnBacklightOn() {
  digitalWrite(P_BACKLIGHT, LOW);
}

void turnBacklightOff() {
  digitalWrite(P_BACKLIGHT, HIGH);
}

void turnRelayOn() {
  digitalWrite(P_RLY1, LOW);
}

void turnRelayOff() {
  digitalWrite(P_RLY1, HIGH);
}

void turnRelayTwoOn() {
  digitalWrite(P_RLY2, LOW);
}

void turnRelayTwoOff() {
  digitalWrite(P_RLY2, HIGH);
}

void turnRelayThreeOn() {
  digitalWrite(P_RLY3, LOW);
}

void turnRelayThreeOff() {
  digitalWrite(P_RLY3, HIGH);
}

void turnRelayFourOn() {
  digitalWrite(P_RLY4, LOW);
}

void turnRelayFourOff() {
  digitalWrite(P_RLY4, HIGH);
}

vedi qui (la prima definizione sostituisce completamente i simboli #define dei pin relè):

...
byte pinRele[4] = { 6, 8, 9, 10 };
bool relay[4];
...
void setup() {
...
  for ( int i=0; i<=3; ++i) {
    pinMode(pinRele[i], OUTPUT);
    relay[i] = false;
  }
...
}

void loop() {
...
  // Attivo il relè 3 (invece di turnRelayThreeOn e relaythree=true)
  rele(3, true);
...
  // Spengo il relè 3 (invece di turnRelayThreeOff e relaythree=false)
  rele(3, false);
...
}

void rele(byte num, bool stato) {
  if ( stato )
    digitalWrite(pinRele[num], HIGH);
  else
    digitalWrite(pinRele[num], HIGH);
  relay[num] = stato;
}
...

Non ti pare?

Hai ragione, sarebbe tutto più scorrevole e leggibile, ordinato, ecc. Ma quanto tempo ci vorrebbe per me? Troppo, credimi, e non avendolo mi limito al concetto "l'importante è che funzioni".
Nulla di personale, solo questione di tempo.