Codice azionamento passerella idraulica imbarcazione.

Il thread è a metà tra software ed hardware. La premessa è che sto smanettando con la passerella indraulica della barca. Dalle belle e semplici schede a rele si è passati ai microcontrollori che più spesso danno problemi con la felicità dei costruttori che invece che riparare sostituiscono la scheda a suon di centinaia di euro.
Avendone aperta più di una ho verificato che spesso si tratta di un semplice PIC e 4 mosfet.
Ora vorrei tornare af una semplice versione a rele pilotati da Arduino. Il concetto oprativo è questo:

  • premo tasto alza -> il rele da corrente alla pompa ed all'attuatore di alzata;
  • premo tasto abbassa -> un secondo rele da corrente alla pompa ed all'attuatore di abbassamento e cosi via per avanzamento ed arretramento.
    Parallelando poi i contatti NA dei rele con un semplice modulo radiocomando a 4 canali (cineseria da 10 euro già utilizzati con successo per illuminazione) vorrei azionarla con telecomando quando scendo a terra e quando rientro.
    Il codice non mi sembra complesso...ora la domanda che è più hardware che software...intravedete problemi o particolari attenzioni per la realizzazione pratica?...io non ne vedo.

Intanto come hardware potresti usare qualcosa tipo lo schema allegato.
Ciao

Grazie, pensavo a qualcosa del genere, in fin dei conti quello che la scheda fa è molto semplice...quella su cui sto lavorando ora e che vorrei sostituire con una replica fatta con arduino è esattamente questa in foto. In pratica i mosfet comandano gli attuatori. L alogica è affidata ad un PIC16F...non parte l'impulso e mi sono scocciato.
Voglio provare Arduino e rele!

Ovviamente lo schema che ti ho proposto parte dal presupposto che per invertire l'azionamento dell'attuatore è sufficiente invertire la polarità.
Inoltre bisogna vedere se ci sono dei fine corsa oppure se l'azionamento va a tempo o se si deve tenere premuto il pulsante finchè la passerella non è completamente alzata o abbassata.

Non capisco a che cosa possa servire un microcontrollore...
Metti un relè con autoritenuta (uno scambio che tiene alimentata la bobina) che la abbassa, un interruttore di fine corsa, un altro relè con autoritenuta che la alza e un altro interruttore di fine corsa... :slight_smile:

...infatti. Mi sono posto la stessa domanda. Le ditte sono portate evidentemente a complicare (?) quando invece serve solo la chiusura del teleruttore della pompa e delle 4 valvole di azionamento.
@salvogi: la pompa non inverte la rotazione (non è un trim) ma pompa solo il fluido che attraverso 4 valvole viene indirizzato verso lo specifico attuatore.
Ora volgio misurare la corrente di attivazione necessaria e provo un circuito terraterra con i pulsanti...se poi la corrente fosse alta utilizzerò dei rele.
L'idea del microcontrollore era per replicare la scheda del produttore e gestire la logica dei componenti in modo più mirato (attivazione pompa e dopo un breve ritardo attivatione valvole movimento).
@ Datman: credo che il rele con autoritenuta non vada bene. Il movimento deve essere comandato dall pressione sul pulsante. Il fine corsa...diciamo è a vista...cmq la passerella raggiunto il fine corsa fisico non si muove più ed il fluido circola nel loop.

PS: nella scheda che sto testando non ho l'impulso che attiva il MOSFET e non so come verificarlo

...dunque voglio provare un codice.
Mi suggerite di utilizzare diversi cicli "if" per ognuno dei 4 bottoni o cicli "case"?

...sono un pò rico dal caldo ma mi sa che il case non posso utilizzarlo...vero?

vince59:
...dunque voglio provare un codice.
Mi suggerite di utilizzare diversi cicli "if" per ognuno dei 4 bottoni o cicli "case"?
...sono un pò rico dal caldo ma mi sa che il case non posso utilizzarlo...vero?

Il 'case' è solo un 'if' scritto in altro modo.
Nessuno dei due è un ciclo.

Quello che manca mi sembra essere la descrizione di una sequenza ben precisa di azioni da compiere... che poi si traducono in macchinese usando: sequenze di istruzioni (operazioni su porte e variabili), decisioni (if/switch) e cicli (while/for).

Grazie della precisazione sulla terminologia...ho da imparare ancora.

Ho buttato giù due righe con gli "if", e funziona, però testandolo sul monitor seriale noto un pò di ritardo nella risposta probabilmente dovuto al "delay" ed ho provato con "millis".
Cosa mi suggerisci di utilizzare?
Onestamente, ho letto poco, ma non mi è del tutto chiaro come impostare la variabile del "case" affinchè rilevi la variazione di stato di uno dei quattro pulsanti.
...spero di aver descritto in modo appropriato.
Ecco il codice buttati giù con cui azionando un tasto:

  • si attiva la pompa;
  • dopo un breve ritardo;
  • si attiva la valvola;
    ...ho commesso errori?...suggerimenti?
    i tasti hanno tutti resistenza di pull-up.
    Considerando che il dispositivo sarà alimentato in maniera più o meno continuativa il millis che problemi potrebbe darmi?
const byte btnStateUp = 2;
const byte btnStateDown = 3;
const byte btnStateOut = 4;
const byte btnStateIn = 5;

long previousMillis = 0;
long interval = 100;

//int pausa = 100;                // attendi x durata time

void setup() {
  Serial.begin(9600);

  pinMode(btnStateUp, INPUT);   // pulsante ALZO
  pinMode(btnStateDown, INPUT); // pulsante ABBASSA
  pinMode(btnStateOut, INPUT);  // pulsante ALLUNGA
  pinMode(btnStateIn, INPUT);   // pulsante ACCORCIA

  pinMode(A0, OUTPUT); // valvola POMPA
  pinMode(A1, OUTPUT); // valvola ALZO
  pinMode(A2, OUTPUT); // valvola ABBASSA
  pinMode(A3, OUTPUT); // valvola ALLUNGA
  pinMode(A4, OUTPUT); // valvola ACCORCIA

  digitalWrite(A0, HIGH);
  digitalWrite(A1, HIGH);
  digitalWrite(A2, HIGH);
  digitalWrite(A3, HIGH);
  digitalWrite(A4, HIGH);
}

void loop() {
  {
    if (digitalRead (btnStateUp) == LOW)
    {
      Serial.println ("alzo passerella");
      analogWrite (A0, LOW); // valvola POMPA
      //   delay (pausa);
      //    ---------------- ritardo -----------------

      unsigned long currentMillis = millis();
      if (currentMillis - previousMillis > interval)
      {
        previousMillis = currentMillis;
      }
  //    ---------------------------------------------
       analogWrite (A1, LOW); // valvola ALZO
    }
    else
    {
      digitalWrite(A0, HIGH);   //spegne POMPA
      digitalWrite(A1, HIGH);   //spegne ALZO
    }
    {
      if (digitalRead  (btnStateDown) == LOW)
      {
        Serial.println ("abbasso passerella");
        analogWrite (A0, LOW);
        //     delay (pausa);
  //    ---------------- ritardo -----------------

        unsigned long currentMillis = millis();
        if (currentMillis - previousMillis > interval)
        {
          previousMillis = currentMillis;
        }
  //    ---------------------------------------------
        analogWrite (A2, LOW);
      }
      else
      {
        digitalWrite(A0, HIGH);   //spegne POMPA
        digitalWrite(A2, HIGH);   //spegne ALZO
      }
    }
    {
      if (digitalRead  (btnStateOut) == LOW)
        Serial.println ("allungo passerella");
      analogWrite (A0, LOW);
      //  delay (pausa);
  //    ---------------- ritardo -----------------
      unsigned long currentMillis = millis();
      if (currentMillis - previousMillis > interval)
      {
        previousMillis = currentMillis;
      }
  //    ---------------------------------------------
      analogWrite (A3, LOW);
    }
    {
      if (digitalRead  (btnStateIn) == LOW)
        Serial.println ("accorcio passerella");
      analogWrite (A0, LOW);
     // delay (pausa);
  //    ---------------- ritardo -----------------
      unsigned long currentMillis = millis();
      if (currentMillis - previousMillis > interval)
      {
        previousMillis = currentMillis;
      }
  //    ---------------------------------------------
      analogWrite (A4, LOW);
    }

    /*
      switch (pushBtn) {
      case (pushBtn == ALZA):
      //do something when var equals 1

      digitalWrite(1, LOW);        // aziona pompa
      delay(pausa);                // ritardo
      digitalWrite(2, LOW);        // aziona valvola
      delay(pausa);                // pausa

      break;
      case 2:
      //do something when var equals 2

      digitalWrite(1, LOW);        // aziona pompa
      delay(pausa);                // ritardo
      digitalWrite(2, LOW);        // aziona valvola
      delay(pausa);                // pausa
      }
      break;

      default:
      // if nothing else matches, do the default
      // default is optional
      break;
      }
    */
  }
}

Scusa, ma gli analogWrite?

..hai ragione Steve. Me ne sono accorto...il caldo fa anche questi effetti. Ora correggo.
Ora funziona ma NON riesco ad ottenre il ritardo...dove sbaglio?

const byte btnStateUp = 2;
const byte btnStateDown = 3;
const byte btnStateOut = 4;
const byte btnStateIn = 5;

long previousMillis = 0;
long interval = 500;

// int pausa = 2000;                // attendi x durata time

void setup() {
  Serial.begin(9600);

  pinMode(btnStateUp, INPUT);   // pulsante ALZO
  pinMode(btnStateDown, INPUT); // pulsante ABBASSA
  pinMode(btnStateOut, INPUT);  // pulsante ALLUNGA
  pinMode(btnStateIn, INPUT);   // pulsante ACCORCIA

  pinMode(7, OUTPUT); // valvola POMPA
  pinMode(8, OUTPUT); // valvola ALZO
  pinMode(9, OUTPUT); // valvola ABBASSA
  pinMode(10, OUTPUT); // valvola ALLUNGA
  pinMode(11, OUTPUT); // valvola ACCORCIA

  digitalWrite(7, HIGH);
  digitalWrite(8, HIGH);
  digitalWrite(9, HIGH);
  digitalWrite(10, HIGH);
  digitalWrite(11, HIGH);
}

void loop() {

  // ------------- ALZO ----------------

  {
    if (digitalRead (btnStateUp) == LOW)
    {
      Serial.println ("alzo");
      digitalWrite (7, LOW); // valvola POMPA
      //  delay (pausa);
      //    ---------------- ritardo -----------------
      unsigned long currentMillis = millis();
      if (currentMillis - previousMillis > interval)
      {
        previousMillis = currentMillis;
        digitalWrite (8, LOW);
      }
      //    ---------------------------------------------
      //digitalWrite (8, LOW); // valvola ALZO
    }
    else
    {
      digitalWrite(7, HIGH);   //spegne POMPA
      digitalWrite(8, HIGH);   //spegne ALZO
    }
  }
  {

    // ----------- ABBASSO ----------------

    if (digitalRead  (btnStateDown) == LOW)
    {
      Serial.println ("abbasso");
      digitalWrite(7, LOW);
      //     delay (pausa);
      //    ---------------- ritardo -----------------
      unsigned long currentMillis = millis();
      if (currentMillis - previousMillis > interval)
      {
        previousMillis = currentMillis;
        digitalWrite (8, LOW);
      }
      //    ---------------------------------------------
      digitalWrite (9, LOW);
    }
    else
    {
      digitalWrite(7, HIGH);   //spegne POMPA
      digitalWrite(9, HIGH);   //spegne ALZO
    }
  }
  {

    // ------------ ALLUNGO ------------------

    if (digitalRead  (btnStateOut) == LOW)
    {
      Serial.println ("allungo");
      digitalWrite (7, LOW);
      //  delay (pausa);
      //    ---------------- ritardo -----------------
      unsigned long currentMillis = millis();
      if (currentMillis - previousMillis > interval)
      {
        previousMillis = currentMillis;
        digitalWrite (10, LOW);
      }
      //    ---------------------------------------------

    }
    else
    {
      digitalWrite(7, HIGH);   //spegne POMPA
      digitalWrite(10, HIGH);   //spegne ALZO
    }
  }
  {

    // ---------------- ACCORCIO --------------------

    if (digitalRead  (btnStateIn) == LOW)
    {
      Serial.println ("accorcio");
      digitalWrite (7, LOW);
      // delay (pausa);
      //    ---------------- ritardo -----------------
      unsigned long currentMillis = millis();
      if (currentMillis - previousMillis > interval)
      {
        previousMillis = currentMillis;
        digitalWrite (11, LOW);
      }
      //    ---------------------------------------------

    }
    else
    {
      digitalWrite(7, HIGH);   //spegne POMPA
      digitalWrite(11, HIGH);   //spegne ALZO
    }
  }

  /*
    switch (pushBtn) {
    case (pushBtn == ALZA):
    //do something when var equals 1

    digitalWrite(1, LOW);        // aziona pompa
    delay(pausa);                // ritardo
    digitalWrite(2, LOW);        // aziona valvola
    delay(pausa);                // pausa

    break;
    case 2:
    //do something when var equals 2

    digitalWrite(1, LOW);        // aziona pompa
    delay(pausa);                // ritardo
    digitalWrite(2, LOW);        // aziona valvola
    delay(pausa);                // pausa
    }
    break;

    default:
    // if nothing else matches, do the default
    // default is optional
    break;
    }
  */
}

vince59:
Ora funziona ma NON riesco ad ottenre il ritardo...dove sbaglio?

Bisogna ragionare a fasi di funzionamento:

SE sono fermo E premo alzo:
    on valvola pompa
    annoto tempo attuale
    imposto non fermo
ALTRIMENTI SE non sono fermo E non è premuto alzo:
    off valvole
    imposto fermo
ALTRIMENTI SE non sono fermo E trascorso > intervallo:
    on valvola alzo

Poi i movimenti sono mutuamente esclusivi o si potrebbero attivare in coppia, che so, alzo e ritrai contemporanei?

Claudio, ho cercato di mettere in codice proprio il principio di azionamento in parte da te esposto.
Ovvero:
se premo ALZO (o qualsiasi altro tasto x movimento):

  • ON alimentazione pompa;
  • annoto tempo (è un ritardo la cui necessità dovrò verificare NON in simulazione);
  • trascorso il ritardo prefissato;
  • ON valvola attuatore;
    se rilascio il pulsante e/o nessun tasto è premuto:
  • OFF alimentazione pompa;
  • OFF valvola attuatore.

I movimenti sono esclusivi dato che bisogna pigiare dei tasti quindi meglio uno per volta.

Cmq a tavolino sembra andare...il millis era troppo corto e NON realizzavo...messo a due secondi si vede che va.

Dunque, dopo qualche prova, ancora al banco ho elaborato un codice piuttosto semplice che sembra funzionare.
Praticamente aziono 4 elettrovalvole e la pompa idraulica premendo un tasto. Laddove fossero premuti due tasti in contemporanea la pompa deve bloccarsi. Ho ottenuto ciò con degli if.
Ora dovrei inserire un altra condizione...ovvero, dovendo montare anche un modulo radiocomandato con cui vorrei attivare i medesimi relé (in parallelo) vorrei escludere la sovrapposizione dei tasti e del radiocomando.
A livello di codice ho pensato di monitorare "semplicemente" la pompa idraulica.
La logica sarebbe questa.
Pigio il pulsante sulla tastiera...se il motore sta già girando (telecomando azionato) NON DEVE accadere nulla.
Diversamente si attiva quello che deve attivarsi.
Stessa cosa per il telecomando...arriva l'impulso ma se il motore sta già girando (TASTO pigiato) NON deve accadere nulla.
Vi sembra relaizzabile?
Cmq ecco il codice attuale.

const byte btnStateUp = 2;
const byte btnStateDown = 3;
const byte btnStateOut = 4;
const byte btnStateIn = 5;

long previousMillis = 0;
long interval = 0;

// int pausa = 500;                // attendi x durata time

void setup() {
  Serial.begin(9600);

  pinMode(btnStateUp, INPUT);   // pulsante ALZO
  pinMode(btnStateDown, INPUT); // pulsante ABBASSA
  pinMode(btnStateOut, INPUT);  // pulsante ALLUNGA
  pinMode(btnStateIn, INPUT);   // pulsante ACCORCIA

  pinMode(7, OUTPUT); // valvola POMPA
  pinMode(8, OUTPUT); // valvola ALZO
  pinMode(9, OUTPUT); // valvola ABBASSA
  pinMode(10, OUTPUT); // valvola ALLUNGA
  pinMode(11, OUTPUT); // valvola ACCORCIA

  digitalWrite(7, HIGH);
  digitalWrite(8, HIGH);
  digitalWrite(9, HIGH);
  digitalWrite(10, HIGH);
  digitalWrite(11, HIGH);
}

void loop() {

// ------------- ALZO ----------------
  {
    if (digitalRead (btnStateUp) == LOW && digitalRead (btnStateDown) == HIGH && digitalRead (btnStateOut) == HIGH && digitalRead (btnStateIn) == HIGH)
    {
      digitalWrite (7, LOW); // aziono POMPA
      // delay (pausa);
      //    ---------------- ritardo -----------------
      unsigned long currentMillis = millis();
      if (currentMillis - previousMillis > interval)
      {
        previousMillis = currentMillis;
        digitalWrite (8, LOW); // aziono valvo
        Serial.println ("alzo");
      }
//    ---------------------------------------------
      //digitalWrite (8, LOW); // aziono valvola ALZO
    }
    else
    {
      digitalWrite(7, HIGH);   //spegne POMPA
      digitalWrite(8, HIGH);   //spegne ALZO
    }
  }
  {
// ----------- ABBASSO ----------------
    if (digitalRead  (btnStateDown) == LOW && digitalRead (btnStateUp) == HIGH && digitalRead (btnStateOut) == HIGH && digitalRead (btnStateIn) == HIGH)
    {

      digitalWrite(7, LOW);  // aziona POMPA
      //    delay (pausa);
      //    ---------------- ritardo -----------------
      unsigned long currentMillis = millis();
      if (currentMillis - previousMillis > interval)
      {
        previousMillis = currentMillis;
        digitalWrite (9, LOW);
        Serial.println ("abbasso");
      }
//    ---------------------------------------------
      //  digitalWrite (9, LOW);
    }
    else
    {
      digitalWrite(7, HIGH);   //spegne POMPA
      digitalWrite(9, HIGH);   //spegne ABBASSO
    }
  }
  {
// ------------ ALLUNGO ------------------
    if (digitalRead  (btnStateOut) == LOW && digitalRead (btnStateUp) == HIGH && digitalRead (btnStateDown) == HIGH && digitalRead (btnStateIn) == HIGH)
    {

      digitalWrite (7, LOW);
      //  delay (pausa);
      //    ---------------- ritardo -----------------
      unsigned long currentMillis = millis();
      if (currentMillis - previousMillis > interval)
      {
        previousMillis = currentMillis;
        digitalWrite (10, LOW);
        Serial.println ("allungo");
      }
//    ---------------------------------------------
    }
    else
    {
      digitalWrite(7, HIGH);   //spegne POMPA
      digitalWrite(10, HIGH);   //spegne ALLUNGO
    }
  }
  {
// ---------------- ACCORCIO --------------------
    if (digitalRead  (btnStateIn) == LOW && digitalRead (btnStateUp) == HIGH && digitalRead (btnStateDown) == HIGH && digitalRead (btnStateOut) == HIGH)
    {
      digitalWrite (7, LOW);
      // delay (pausa);
      //    ---------------- ritardo -----------------
      unsigned long currentMillis = millis();
      if (currentMillis - previousMillis > interval)
      {
        previousMillis = currentMillis;
        digitalWrite (11, LOW);
        Serial.println ("accorcio");
      }
//    ---------------------------------------------
    }
    else
    {
      digitalWrite(7, HIGH);   //spegne POMPA
      digitalWrite(11, HIGH);   //spegne ACCORCIO
    }
  }
}
  /*
    switch (pushBtn) {
    case (pushBtn == ALZA):
    //do something when var equals 1

    digitalWrite(1, LOW);        // aziona pompa
    delay(pausa);                // ritardo
    digitalWrite(2, LOW);        // aziona valvola
    delay(pausa);                // pausa

    break;
    case 2:
    //do something when var equals 2

    digitalWrite(1, LOW);        // aziona pompa
    delay(pausa);                // ritardo
    digitalWrite(2, LOW);        // aziona valvola
    delay(pausa);                // pausa
    }
    break;

    default:
    // if nothing else matches, do the default
    // default is optional
    break;
    }
  */

Stai usando una logica combinatoria priva di stato, cioè i soli ingressi opportunamente valutati decidono l'uscita. Comportamenti più complessi è più semplice descriverli usando una logica a a stati.

Ma comunque in questo caso la cosa più semplice mi sembra parallelare dal punto di vista logico i comandi pulsante locale e pulsante remoto, in maniera che risultino un unico comando:

bool comandoAlzo = (   digitalRead(btnStateUp) == LOW
                    || digitalRead(...intelecomando...) == ....  );

e quindi si andranno a valutare i comandi combinati invece dei singoli ingressi:

if (comandoAlzo && !comandoAbbasso && !comandoEstendi && !comandoRitrai)

Grazie Claudio, però sinceramente fatico un pò a comprendere del tutto la tua spiegazione.
I pulsanti in locale (quelli sul tastierino per intenderci) sono quelli che "..opportunamente valutati decidono l'uscita..." ed azionano i relè.
I pulsanti in remoto, in realtà non saranno altro che una scheda radiocomando 433 MHz a 4 canali che azioneranno lo stesso relè. IN pratica li parallelo fisicamente.
Questo è uno step iniziale perchè in realtà vorrei realizzare un circuito che invece dei relè utlizzi semiconduttori..tipo MOSFET o simili.

PS: tieni conto che sono un hobbista autodidatta per cui alcune nozioni mi mancano.

Per me i 4 canali del ricevitore del radiocomando è meglio leggerli con altri 4 ingressi di Arduino, così i relé li comandi solo da Arduino e non fa nessuna differenza premere un pulsante locale o uno remoto.

Grazie Claudio, ottimo suggerimento.
C'è un limite alle condizioni che posso porre internamente ad un if?

vince59:
C'è un limite alle condizioni che posso porre internamente ad un if?

In senso di quantità? Non credo. Attendiamo smentite.
Certo che se devi mettere dieci o venti condizioni assieme, forse c'è qualcosa da rivedere nel design del programma :wink:

Tra l'altro per azionamenti così semplici alle volte può essere più facile ragionare a relé. Il seguente schema simulato a programma sono 19 righe in tutto, letture ingressi, temporizzatori e scrittura uscite compresi, e prevede anche che la pompa non si fermi immediatamente, in modo che se si vuole premere un altro tasto non tocca aspettare di nuovo ripartenza pompa e ritardo (i pulsanti sono ovviamente mutuamente esclusivi, quando uno è premuto gli altri sono esclusi):

Ad esempio:

// la prima riga
f1 = (digitalRead(PULS_ALZO)==PRESSLVL || digitalRead(telec_al)==TELECLVL) && !f2 && !f3 && !f4;


// il temporizzatore T1 (ritardato all'avvio)
if (!f1) { T1=0; t1=millis(); } else if (millis()-t1 > TDEL) T1=1;


// il temporizzatore T5 (ritardato al rilascio)
if (f1 || f2 || f3 || f4) { T5=1; t5=millis(); } else if (millis()-t5 > TSTOP) T5=0;


// comando rele'pompa
digitalWrite(RL_POMPA, T5 ? ONLVL : OFFLVL);


// il comando alzo
alzo = T1 || (f1 && f5);


// il rele'ausiliario f5 con autoritenuta
f5 = T5 && (alzo || abbasso || attendi || ritrai || f5);

veramente interessante come codice....devo studiarlo e comprenderlo bene.
Claudio, pur comprendendo la logica dei comandi della prima riga, comprendo cosa sia stato costruito.
Ovvero, l'intera riga che parte f1:

f1 = (digitalRead(PULS_ALZO)==PRESSLVL || digitalRead(telec_al)==TELECLVL) && !f2 && !f3 && !f4;

che cosa è dal punto di vista tecnico?
Purtroppo, difficilmente arriverò ad un livello del genere.