Go Down

Topic: Input che aziona 5 relay in sequenza e si interrompe al rilascio improvviso (Read 2677 times) previous topic - next topic

docsavage

visto che sembra che siamo in dirittura di arrivo, mi permetto di scrivere le mie idee, come al solito risultano alternative a quello che ho letto finora

ho pensato, non me ne vogliate se sono monomaniaco, ad un array di piedini per le uscite ed a un ciclo for per ciclarlo

la condizione per accendere o spegnare le uscite è un semplice test del tipo tempo maggiore a gradino per numero dell'uscita

contando la millis() e il suo overflow diventa una cosa del genere

al premere del tasto segno i millisescondi

poi, se tasto è premuto, accendo le varie uscite per una condizione del tipo:

millis meno istante memorizzato > gradino per numero dell'uscita

è quindi venuta fuori una cosa del genere:

Code: [Select]

#define PIEDINI 5
int out[PIEDINI] = {2, 3, 4, 5, 6};
// definisco le uscite in uso

#define IN 7
// definisco il piedino di ingresso
// che DEVE avere il debounce hardware
// essendo importanti i tempi di esecuzione NON posso usare il debounce SW

// definisco il passo di tempo da attendere
int passo = 1000; //1000 millisecondi un secondo

// la variabile che registra il momento di pressione del tasto
unsigned long h;

// la variabile che registra lo stato del pulsante
byte st;



e con questo abbiamo definito il campo giochi
con i piedini di uscita scritti in un array
che mi permette sia di espanderli, sia di usare piedini non contigui
Code: [Select]



void setup() {
  // attivo le uscite e gli ingressi
  for (int i = 0; i < PIEDINI; i++) {
    pinMode(out[i], OUTPUT);
  }
  pinMode(IN, INPUT);
}



l'inizializzazione dei piedini, praticamente una cosa obbligata
con l'unico trucco di parametrizzarla e chiuderla in un ciclo
facilmente espandibile

la loop, per una volta lunga, non ci sono funzioni complesse che ho dovuto sviluppare separatamente
Code: [Select]


void loop() {
  // se il tasto è cambiato
  if (digitalRead(IN) != st) {
    h = millis();
    // memorizzo il tempo
  }
  st = digitalRead(IN);


tutte le volte che il tasto cambia. memorizzo il tempo

Code: [Select]

  if (st) {
    // se l'ingresso è attivo
    // accendo le uscite in funzione di un tempo
    for (int i = 0; i < PIEDINI; i++) {
      digitalWrite(i, ((millis() - h) > (i * passo)));
      // accendo o spengo l'uscita a seconda se millis supera h+N gradini
    }
  }
  else {
    // ingresso non attivo, spengo tutte le uscite
    for (int i = 0; i < PIEDINI; i++) {
      digitalWrite(out[i], LOW);
    }
  }
}



qui c'è tutto il lavoro

prima il ramo else, il più semplice da capire

se NON c'è st, (siccome st=digitalread....) è scritto prima è come dire
se il tasto NON è premuto
un ciclo for spegne le uscite

adesso il ramo if

se il tasto è premuto
un ciclo for per ogni uscita confronta il tempo passato dalla pressione del tasto con il NUMERO dell'uscita moltiplicato la costante passo

per la prima uscita il NUMERO è 0, la condizione è vera da subito
per la seconda uscita, numero 1, condizione vera dopo un "passo" di millisecondi
e via così..........

per risparmiare qualche variabile uso la condizione come argomento della digitalwrite corrispondente

esito di tutto questo?

non lo so, nel fine settimana sono in una casa differente e non ho la mia UNO

ma compilare compila e mi dice:

Lo sketch usa 1142 byte (3%) dello spazio disponibile per i programmi. Il massimo è 32256 byte.
Le variabili globali usano 24 byte (1%) di memoria dinamica, lasciando altri 2024 byte liberi per le variabili locali. Il massimo è 2048 byte.

qualche anima buona vuole provare per me a vedere se funziona?
E' stato bello... ma appunto: è stato
Ocio, che adesso c'è qualcuno che dice le stesse cose per le quali mi martellavate... Il tempo è galantuomo

docsavage

oh bella, a pensarci lo ho semplificato ancora.......


basta invece della if (st) e della sua else
mettere solo questo

Code: [Select]


  // accendo le uscite in funzione di un tempo
  for (int i = 0; i < PIEDINI; i++) {
    digitalWrite(i, (millis() - h) > (i * passo)*st);
    // accendo o spengo l'uscita a seconda se millis supera h+N gradini

  }



la condizione if, e la sua else sono state "implicitate" passatemi il neologismo
dentro nella condizione di test del tempo

credo che se funziona la prima funziona anche questa

qualche anima buona che me la prova?
E' stato bello... ma appunto: è stato
Ocio, che adesso c'è qualcuno che dice le stesse cose per le quali mi martellavate... Il tempo è galantuomo

docdoc

Il modo corretto di usare millis() NON è vedere se ha raggiunto tinizio+x (che funziona solo per 50 giorni poi il ritorno a zero del contatore causa problemi)
Vero, ma intanto bisogna vedere se Arduino lo si lascia acceso per più di 50 giorni, e in questo caso probabilmente non è così.

Ma se anche fosse, intanto deve capitare che l'evento di accensione inizi MENO di un secondo dal limite di 2^32 del millis() e quindi finisca oltre, e la cosa è abbastanza poco probabile direi non vi pare?...

E poi è comunque una condizione semplice da gestire, sempre SE il programma deve poter girare per più di 50 giorni ed evitare che non vada in overflow.

Di metodi per gestire questa situazione ce ne sono, e ben noti, in siti come QUESTO ci sono tutte le info necessarie. Ma, ripeto, magari farlo solo se si ritiene probabile il caso e quindi gestire l'overflow.

Alex "docdoc" - ** se ti sono stato d'aiuto, un punto karma sarà gradito, clicca su "add" qui a sinistra, vicino al mio nome ;) **

Etemenanki

... come al solito risultano alternative a quello che ho letto finora

ho pensato, non me ne vogliate se sono monomaniaco, ad un array di piedini per le uscite ed a un ciclo for per ciclarlo ...
Ehm ... a me sembrava di aver fatto piu o meno la stessa cosa, anche se in modo diverso ... :D

(che poi, rileggendolo, mi sa che il primo for si poteva pure evitarlo ... :D)
"Sopravvivere" e' attualmente l'unico lusso che la maggior parte dei Cittadini italiani,
sia pure a costo di enormi sacrifici, riesce ancora a permettersi.

docsavage

per alternativo non mi riferivo all'uso di un array e di un for

mi riferivo alla condizione, scusa se non mi sono spiegato bene

E' stato bello... ma appunto: è stato
Ocio, che adesso c'è qualcuno che dice le stesse cose per le quali mi martellavate... Il tempo è galantuomo

docsavage

(che poi, rileggendolo, mi sa che il primo for si poteva pure evitarlo ... :D)
scusa, a quale ciclo for ti riferisci?

forse mi sono perso......
E' stato bello... ma appunto: è stato
Ocio, che adesso c'è qualcuno che dice le stesse cose per le quali mi martellavate... Il tempo è galantuomo

Etemenanki

No problem doc ... del resto non essendo un programmatore, faccio confusione pure io (soprattutto io, in effetti :D)

Ad esempio, mi sono reso conto che si potrebbe gestire tutto quanto usando solo un paio di if ... una cosa di questo tipo (sempre ammesso di non aver scritto cavolate :D)

Code: [Select]

...
definisci i pin dei rele' come 1, 2, 3, 4, 5, per comodita'
byte tempo = 1; // per sapere se il secondo e' passato
byte rele = 0; //per accendere le uscite
unsigned long prevmil = millis(); //per controllo tempo
...

setup()
...

void loop()
{
   if (digitalRead(PULSANTE) == HIGH) //pulsante premuto
   {
      if ((rele != 5) && (tempo = 1)) //se rele non tutti accesi e secondo trascorso
      {
         digitalWrite(rele + 1, HIGH); //uso il valore di rele+1 per accendere i pin
         tempo = 0; //azzero tempo per controllare quando passato un'altro secondo
         prevmil = millis(); //e resetto prevmil per lo stesso motivo
         rele++ //incremento variabile rele
      }
   }
   else //pulsante non premuto
   {
      digitalWrite(1, LOW);
      digitalWrite(2, LOW);
      digitalWrite(3, LOW);
      digitalWrite(4, LOW);
      digitalWrite(5, LOW);
      rele = 0;
      tempo = 1;
   }
   if (prevmil - millis() >= 1000) tempo = 1; //tempo=1 dopo un secondo da ultimo azzeramento
   ...
   ... resto del programma
   ...
}


... che dici, fa abbastanza pena ? ... :D
"Sopravvivere" e' attualmente l'unico lusso che la maggior parte dei Cittadini italiani,
sia pure a costo di enormi sacrifici, riesce ancora a permettersi.

Etemenanki

"Sopravvivere" e' attualmente l'unico lusso che la maggior parte dei Cittadini italiani,
sia pure a costo di enormi sacrifici, riesce ancora a permettersi.

docsavage

Non credo faccia pena,
anchesì tu sei una delle mie figure di riferimento......

nemmeno io sono un programmatore, almeno non di lavoro

ho programmato:
TI59
ZX Spectrum
123
dBase
Clipper
Basic, Basica, GWBasic
anche il compilatore basic di IBM: BASCOM, quasi intercambiabile con BASICA
oltre a quick basic e qbasic (compilato e interpretato, diciamo così)
e questo negli anni 80
negli anni 90
VisualBasic for application, in particolare con Excel e Access
ma MAI riconosciuto per lavoro

e adesso Arduino
insomma per campare adesso mi tocca fare pompe



per tornare a bomba

senza entrare troppo nella organizzazione del tuo programma,
io farei solo queste modifiche, che mi balzano all'occhio a prima vista
Code: [Select]

if (digitalRead(PULSANTE)) //pulsante premuto, non serve ==HIGH
   {
      if ((tempo) && (rele<5)) //se rele non tutti accesi e secondo trascorso
//inverto la condizione solo in previsione di un ulteriore test
// uso un esplicito <5 per evitare che se per caso "scappa" oltre il 5 ricomincia a contare
// potrebbe scappare solo per errori di programmazione da altre parti, che non ci sono, ma non si sa mai

      {
         digitalWrite(++rele, HIGH); //uso il valore di rele+1 per accendere i pin ma lo esplicito con un ++
// davanti, e non dietro come di solito, per provocare il calcolo PRIMA che venga valutata l'espressione
         tempo = 0; //azzero tempo per controllare quando passato un'altro se condo
         prevmil = millis(); //e resetto prevmil per lo stesso motivo
         //rele++ //incremento variabile rele, non serve già fatto
      }


scusami, non ho resistito....
quando ti sei chiesto se faceva pena, non ho resistito a guardarla con occhio critico
ma noterai che ho scritto solo banalità

ti basta come risposta alla tua domanda?

o per meglio essere espliciti:

mi sembrava buona anche prima
E' stato bello... ma appunto: è stato
Ocio, che adesso c'è qualcuno che dice le stesse cose per le quali mi martellavate... Il tempo è galantuomo

AlPinkish

oh bella, a pensarci lo ho semplificato ancora.......


basta invece della if (st) e della sua else
mettere solo questo

Code: [Select]


  // accendo le uscite in funzione di un tempo
  for (int i = 0; i < PIEDINI; i++) {
    digitalWrite(i, (millis() - h) > (i * passo)*st);
    // accendo o spengo l'uscita a seconda se millis supera h+N gradini

  }




la condizione if, e la sua else sono state "implicitate" passatemi il neologismo
dentro nella condizione di test del tempo

credo che se funziona la prima funziona anche questa

qualche anima buona che me la prova?
Nel provarlo spero di non aver fatto errori, ma solo i primi 3 relay si accendo, tutti insieme...
non so se posso linkare tikercard, ma qui c'è un test online di quanto hai scritto.


Per il discorso dei 50 giorni, è chiaro il motivo per cui si tende a scrivere un codice affidabile nel tempo, ma considerando l'osservazione di docdoc:
Quote
Vero, ma intanto bisogna vedere se Arduino lo si lascia acceso per più di 50 giorni, e in questo caso probabilmente non è così.
vi confermo che il sistema sarà riavviato ogni giorno per necessità dell'impianto stesso. Quindi il problema, in questi termini non c'è. In ogni caso è interessante conoscere la soluzione anche per il caso dei 50+ giorni.

Ma tornando al codice che ha funzionato, almeno per le prove che ho fatto io, ho solo bisgogno di capire come docdoc lo aveva concepito con i millis:
Code: [Select]
// numero di relè
#define NUMRELE 5
// Pin di ogni relè
int pin[5] = { 3,4,5,6,7 };
// Relè attualmente attivo (-1 indica "nessun relè")
int releON = -1;
// Flag fine ciclo relè
bool fineCiclo = false;
// Pin del pulsante
#define PULSANTE 12

void setup() {
  Serial.begin(115200); //attivo comunicazione seriale
  // Inizializzazione pin dei relè e li spengo
  for (int i=0; i<NUMRELE; i++) {
    pinMode(pin[i], OUTPUT);
 digitalWrite(pin[i], LOW);
  }
  // Inizializzazione pin pulsante
  pinMode(PULSANTE, INPUT);

  }

void loop() {
  // Vedo se il tasto è stato premuto
  if ( digitalRead(PULSANTE) == HIGH ) {
    if ( !fineCiclo ) {
      // Passo al successivo rele?
      // (all'inizio poiché parto da -1, il primo sarà 0)
      releON++;
      if ( releON < NUMRELE ) { // Se non ho ancora finito
        digitalWrite(pin[releON], HIGH); //accendo
        Serial.print("Relè ");
        Serial.print(releON);
        Serial.println(" acceso");
        delay(1000); // aspetto 1 secondo
      }
      else
      { // Ho finito!
        fineCiclo = true; // Segnalo che ho finito
      }
    }
  }
  else
  { // Tasto non premuto o rilasciato, resetto le variabili
    spengo();
    resetto();
  }

  funzione_esterna();

} // fine loop

void spengo() {
  for (int i=0; i<NUMRELE; i++) {
    digitalWrite(pin[i], LOW);
  }
  releON = -1;
}

void resetto() {
  fineCiclo = false;
  releON = -1;
}

void funzione_esterna(){
  if ( fineCiclo ) {
    Serial.println(" tutti i relay sono stati accesi e la nuova funzione è stata richiamata correttamente");
    //nuova funzione da preparare
  }
}
Homo Faber Fortunae Suae

docsavage

Ho sbagliato la condizione

invece di essere:
 digitalWrite(i, (millis() - h) > (i * passo)*st);

deve essere
 digitalWrite(i, ((millis() - h) > (i * passo))*st);


per la questione dei tre led soli che vanno, non saprei, ci penso dopo cena
E' stato bello... ma appunto: è stato
Ocio, che adesso c'è qualcuno che dice le stesse cose per le quali mi martellavate... Il tempo è galantuomo

docsavage

ci ho pensato dopo cena

invece di essere:
Code: [Select]

 digitalWrite(i, ((millis() - h) > (i * passo))*st);

deve essere
Code: [Select]

 digitalWrite(out[i], ((millis() - h) > (i * passo))*st);


che vergogna, scambiare l'indirizzo col valore....
adesso mi ritaglio le orecchie da asino

chiedo scusa e ..........
vado a vergognarmi davanti alla TV

buona serata
E' stato bello... ma appunto: è stato
Ocio, che adesso c'è qualcuno che dice le stesse cose per le quali mi martellavate... Il tempo è galantuomo

AlPinkish

Ottimo funziona perfettamente ed è anche molto snellito come codice.

Dato che non sono così avanti in C, potresti spiegarmi come funziona teoricamente il cambiamento di stato dei pin in uscita di questa stringa:


Code: [Select]
digitalWrite(out[i], ((millis() - h) > (i * passo))*st);

pardon! ma per ora so cambiare gli stati logici solo "esplicitamente" con la forma LOW e HIGH.

Questo è anche uno dei motivi per cui non so usare i millis in questo modo.
Homo Faber Fortunae Suae

docsavage

Ci vuole un minimo di teoria

In C e quindi anche con arduino, tutte la volte che in un programma devi usare un valore, puoi usare una o più  di queste cose


Un valore

Una variabile dello stesso tipo del valore che  useresti (o al limite di un tipo che sia automaticamente convertibile in quello)

Una espressione, che è  come dire un calcolo, un conto, per esempio una somma

Una funzione, che restituisca un valore dello stesso tipo o al limite di un tipo che si possa convertire automaticamente nel tipo giusto

E questo cosa può  essere contorta quanto vuoi

Esempio una somma tra il risultato di una funzione e un secondo calcolo tra una variabile e una funzione che ha come argomento  un calcolo tra.....

A questo livello servono le parentesi, non solo al compilatore, ma anche a noi poveri umani, che non ragioniamo a velocità  elettronica


Io ho scritto

Digitalwrite out [  i ],

Che significa
Attiva la uscita che ha il numero contenuto nello iesimo  elemento dello array out

E fin qui spero che sia chiaro

E assegna il valore che esce da

Il risultato della moltiplicazione tra

Lo stato del pulsante

E

il confronto tra
1 la differenza tra l'ora memorizzata nella variabile h e l'ora attuale che è  il risultato della funzione millis
2 il risultato della moltiplicazione tra il numero di passo e la quantità  di millisecondi di un passo



Ora, se il pulsante NON è  premuto lo stato del pulsante vale falso, che convertito automaticamente a numero significa zero

Quindi le moltiplicazione darà  sempre come risultato 0
Che  convertito automaticamente in valore logico vale falso

E quindi se il pulsante NON è  premuto le uscite sono tutte spente

Se invece il pulsante fosse premuto lo stato sarebbe vero, ossia 1 in numerico
Adesso guardiamoil seguito della moltiplicazione
Il confronto tra il tempo passato e una costante moltiplicata per il numero ordinale dell'uscita
Per la prima uscita il risultato della moltiplicazione sarà sempre zero
Qui di il tempo passato sarà  sempre maggiore di zero
Quindi il confronto sarà sempre vero, in numero vale 1
Moltiplicato la stato del pulsare fa 1 per 1, che vale ancora 1, che viene automaticamente convertì in vero, uscita accesa
Per la seconda uscita il conto vale mille
E quindi uscita alta dopo 1 secondo

E via così


Spero sia chiaro, spiegato così
E' stato bello... ma appunto: è stato
Ocio, che adesso c'è qualcuno che dice le stesse cose per le quali mi martellavate... Il tempo è galantuomo

Claudio_FF

Beh, sintetizzando basta sapere che:

  • 1 = true = HIGH
  • 0 = false = LOW
  • Un'espressione logica/condizionale (< > <= && ecc) produce true/false (cioè sempre 1/0) e quindi il suo risultato è usabile in un'espressione aritmetica: a=(b!=c)*10;
  • Un valore diverso da zero viene valutato true: if(20)z=1;



PS: al post #26 ho aggiunto anche la versione funzionante "hardware simulato".
********* IF e SWITCH non sono cicli ! *********
**** Una domanda ben posta è già mezza risposta ****
*** La corrente si misura in mA, la quantità di carica in mAh ***

Go Up