Carro allegorico con arduino

salve a tutti, ho ben pensato di proporre ai miei amici di effettuare la movimentazione di un carro allegorico che rappresenta la bambola del film coreano squidgam*, controllata da un arduino uno r3, che sarà mai... :melting_face:
Bene armato della mia scarsissima dote di programmazione della enorme ignoranza in materia ma dalla più grande voglia di mettermi in gioco ed imparare, la prima cosa che ho fatto è avere uno schema mentale dei movimenti:
1° ruota il corpo di 180° verso muro finecorsa stop movimento
tempo di attesa di 4 secondi
2° ruota testa di 180° verso "pubblico" (nello sketch verso avanti) finecorsa stop movimento
tempo di attesa 2 secondi
3° accende 2 lampade negli occhi per x tempo alterna accensione spegnimento 2 volte
4°ritorna la testa vero il muro finecorsa stop
5° ritorna la bambola verso pubblico (nello sketch verso avanti) finecorsa stop
Ho anche inserito un pulsante di emergenza che vorrei fosse alto, nel caso in cui ci fosse un guasto allo stesso fermasse comunque tutti i movimenti.
fatto ciò provo a scrivere il mio primo sketch da solo, con l'aiuto iniziale di tinkercad
il risultato non è dei migliori ma non mi arrendo provo e riprovo :exploding_head: mi servirebbe un pò di aiuto per capire dove, cosa sbaglio

const int pinAvvio = 2;         // Pulsante di avvio
const int pinCorpoMuro = 3;     // Relè corpo muro
const int pinTestaAvanti = 4;    // Relè testa avanti
const int pinOcchi = 5;          // Relè occhi
const int pinTestaMuro = 6;      // Relè testa muro
const int pinCorpoAvanti = 7;    // Relè corpo avanti
const int pinEmergenza = 12;     // Pulsante di emergenza

const int pinFCCorpoMuro = 8;    // Finecorsa corpo muro
const int pinFCTestavanti = 9;   // Finecorsa testa avanti
const int pinFCTestamuro = 10;   // Finecorsa testa muro
const int pinFCCorpoAvanti = 11; // Finecorsa corpo avanti

void setup() {
  pinMode(pinAvvio, INPUT);
  pinMode(pinCorpoMuro, OUTPUT);
  pinMode(pinTestaAvanti, OUTPUT);
  pinMode(pinOcchi, OUTPUT);
  pinMode(pinTestaMuro, OUTPUT);
  pinMode(pinCorpoAvanti, OUTPUT);
  pinMode(pinEmergenza, INPUT_PULLUP);

  pinMode(pinFCCorpoMuro, INPUT);
  pinMode(pinFCTestavanti, INPUT);
  pinMode(pinFCTestamuro, INPUT);
  pinMode(pinFCCorpoAvanti, INPUT);
}

void loop() {
  if (digitalRead(pinEmergenza) == LOW) {
    // Se il pulsante di emergenza è premuto, spegni tutti i relè
    digitalWrite(pinCorpoMuro, LOW);
    digitalWrite(pinTestaAvanti, LOW);
    digitalWrite(pinOcchi, LOW);
    digitalWrite(pinTestaMuro, LOW);
    digitalWrite(pinCorpoAvanti, LOW);
    // Aggiungi eventuali altre azioni di emergenza necessarie
  } else if (digitalRead(pinAvvio) == HIGH) {
    // Primo Movimento
    digitalWrite(pinCorpoMuro, HIGH);
    while (digitalRead(pinFCCorpoMuro) == LOW) {
      // Attendi fino a raggiungere il finecorsa
    }
    digitalWrite(pinCorpoMuro, LOW);

    // Pausa di 4 secondi
    delay(4000);

    // Secondo Movimento
    digitalWrite(pinTestaAvanti, HIGH);
    while (digitalRead(pinFCTestavanti) == LOW) {
      // Attendi fino a raggiungere il finecorsa
    }
    digitalWrite(pinTestaAvanti, LOW);

    // Pausa di 2 secondi
    delay(2000);

    // Terzo Movimento
    digitalWrite(pinOcchi, HIGH);
    delay(3000);
    digitalWrite(pinOcchi, LOW);
    delay(2000);
    digitalWrite(pinOcchi, HIGH);
    delay(4000);
    digitalWrite(pinOcchi, LOW);

    // Quarto Movimento
    digitalWrite(pinTestaMuro, HIGH);
    while (digitalRead(pinFCTestamuro) == LOW) {
      // Attendi fino a raggiungere il finecorsa
    }
    digitalWrite(pinTestaMuro, LOW);

    // Pausa di 3 secondi
    delay(3000);

    // Quinto Movimento
    digitalWrite(pinCorpoAvanti, HIGH);
    while (digitalRead(pinFCCorpoAvanti) == LOW) {
      // Attendi fino a raggiungere il finecorsa
    }
    digitalWrite(pinCorpoAvanti, LOW);
  }
}

Per l'emergenza metti un tasto in serie all'alimentazione che, se premuto, spegne tutto, tanto più che hai strutturato il programma con i delay e quindi per l'emergenza software servirebbe un interrupt! Comunque l'interruttore generale è più sicuro.

1 Like

grazie mille per l'ottimo consiglio, facilita molto il lavoro, però se ha voglia di spiegarmi cosa e come si usa un interrupt io sono lieto di imparare. Per il resto dello sketch accorgimenti? errori?

Prima di interessarti agli interrupt, impara a usare millis() al posto dei delay e le macchine a stati finiti.

in realtà quando ho iniziato a scriverlo qualche giorno fa non conoscevo neanche il delay o peggio digitalwrite e digitalread, ma non mi arrendo, più che altro ho postato qui lo sketch per metterlo all'attenzione di persone capaci e capire insieme se ci sono errori che possono mettere in pericolo le persone visto che si tratta di un carro allegorico.

Per la sicurezza i finecorsa dovrebbero essere contatti normalmente chiusi, in modo che se accidentalmente si stacca un cavo è come se fossero premuti e il movimento di blocca.

Meglio ancora se il finecorsa interrompe il positivo, così se il cavo staccato va in corto con la massa risulta sempre premuto.

1 Like

metterò 2 finecorsa per ognuno uno viene letto da arduino l'altro va a monte della bobina del contattore, avevo pensato in realtà di usarne uno con il doppio contatto nc no ma l'idea che all'interno dello stesso ci sia segnale arduino e tensione di alimentazione bobina mi ha procurato un brivido lungo la schiena che neanche a dirlo.

Alla brutta brutta basta trasformare tutti quei delay in cicli contenenti un delay(10) e una lettura dell'ingresso di stop, con un bel... goto condizionato alla sezione di stop.

Funzionare funzionerebbe, ma sarebbe proprio brutto... come i primi programmi che scrivevo in Sinclair Basic :sweat_smile:

cioè??? se non è un disturbo potresti gentilmente spiegarmi meglio???

Principio: sostituire un delay con un ciclo che impiega lo stesso tempo. Invece di:

delay(1000);

scrivere:

for(int n=0; n<100; n++) {
    delay(10);
}

Abbiamo scomposto un delay da un secondo in cento delay da 10 millisecondi. Qual è il vantaggio? Che dentro al ciclo, durante quel secondo, possiamo anche leggere cento volte lo stato di un pulsante:

for(int n=0; n<100; n++) {
    delay(10);
    if (digitalRead(...) == ...) {
        ...fa qualcosa
    }
}

Poi sul cosa fare e come, dipende da come è strutturato il tutto il resto, se si usano funzioni o si fa tutto il lavoro dentro la funzione loop ecc.

1 Like

Quindi se ho capito bene sostituire il delay con i cicli da te illustrati sarebbe un bene, considerando che l'arduino deve stare "in ascolto" degli "fc" per fermare i vari movimenti.

anche se in realtà non ho inserito ovviamente delay durante i singoli movimenti, solo come pausa tra un movimento e l'altro, credo che l'opzione migliore sia quella delle macchine a stati finiti, ma ho provato uno sketch e non va nulla. :grimacing:
:grimacing:

Ma se devi fare un solo movimento fino al raggiungimento del fine corsa, per come lo hai strutturato il codice va bene. Si il fungo di emergenza nel caso estremo spegne tutto togliendo alimentazione.
Decidi tu se togliere alimentazione ai soli motori oppure tutto incluso arduino ecc. Se rimuovi l'alimentazione totalmente sei al sicuro anche da rischio di corto circuiti e relative fiamme. Immagino debba lavorare con una batteria e quindi occhio anche a quella, magari messa dentro una cassetta di metallo con il suo fusibile in modo che non si possa accidentalmente cortocircuitare. Prevenire è meglio che curare.

Se al contrario serve muovere più motori allo stesso tempo non resta la macchina a stati finiti e millis().

Ciao.

1 Like

grazie per la gentile risposta @Maurotec

no gruppo elettrogeno stabilizzato/termico/alimentatore/arduino

si, in pratica controllo le bobine dei teleruttori tramite moduli relè optoisolati, utilizzerò i contatti degli stessi per evitare l'azionamento dell'inverso

ho appena testato lo sketch che sembra funzionare, ho modificato qualcosina rispetto il primo che avevo pubblicato, eventualmente dopo la risposta di @Claudio_FF sostituirò i delay

in ogni caso ripubblico lo sketch così potete controllare se ne avete voglia ed indirizzarmi meglio

ps devo ancora togliere il pin che fa riferimento all'emergenza così com'è non serve a nulla se non a non far avviare con il tasto avvio

const int pinAvvio = 2;         // Pulsante di avvio
const int pinCorpoMuro = 3;     // Relè corpo muro
const int pinTestaAvanti = 4;    // Relè testa avanti
const int pinOcchi = 5;          // Relè occhi
const int pinTestaMuro = 6;      // Relè testa muro
const int pinCorpoAvanti = 7;    // Relè corpo avanti
const int pinEmergenza = 12;     // Pulsante di emergenza
const int pinFCCorpoMuro = 8;    // Finecorsa corpo muro
const int pinFCTestavanti = 9;   // Finecorsa testa avanti
const int pinFCTestamuro = 10;   // Finecorsa testa muro
const int pinFCCorpoAvanti = 11; // Finecorsa corpo avanti

void setup() {
  pinMode(pinAvvio, INPUT);
  pinMode(pinCorpoMuro, OUTPUT);
  pinMode(pinTestaAvanti, OUTPUT);
  pinMode(pinOcchi, OUTPUT);
  pinMode(pinTestaMuro, OUTPUT);
  pinMode(pinCorpoAvanti, OUTPUT);
  pinMode(pinEmergenza, INPUT_PULLUP);

  pinMode(pinFCCorpoMuro, INPUT);
  pinMode(pinFCTestavanti, INPUT);
  pinMode(pinFCTestamuro, INPUT);
  pinMode(pinFCCorpoAvanti, INPUT);
}

void loop() {
  if (digitalRead(pinEmergenza) == HIGH) {
    // Se il pulsante di emergenza è premuto, spegni tutti i relè
    digitalWrite(pinCorpoMuro, LOW);
    digitalWrite(pinTestaAvanti, LOW);
    digitalWrite(pinOcchi, LOW);
    digitalWrite(pinTestaMuro, LOW);
    digitalWrite(pinCorpoAvanti, LOW);
    // Aggiungi eventuali altre azioni di emergenza necessarie
  } else if (digitalRead(pinAvvio) == HIGH) {
    // Primo Movimento
    digitalWrite(pinCorpoMuro, HIGH);
    while (digitalRead(pinFCCorpoMuro) == LOW) {
      // Attendi fino a raggiungere il finecorsa
    }
    digitalWrite(pinCorpoMuro, LOW);

    // Pausa di 4 secondi
    delay(4000);

    // Secondo Movimento
    {digitalWrite(pinTestaAvanti, HIGH);
    while (digitalRead(pinFCTestavanti) == LOW) {
      // Attendi fino a raggiungere il finecorsa
    }
     digitalWrite(pinTestaAvanti, LOW);
    }
    
    // Pausa di 2 secondi
    delay(2000);

    // Terzo Movimento
    digitalWrite(pinOcchi, HIGH);
    delay(2000);
    digitalWrite(pinOcchi, LOW);
    delay(1000);
    digitalWrite(pinOcchi, HIGH);
    delay(2000);
    digitalWrite(pinOcchi, LOW);
    
    delay(3000);
  
    // Quarto Movimento
    digitalWrite(pinTestaMuro, HIGH);
    while (digitalRead(pinFCTestamuro) == LOW) {
      // Attendi fino a raggiungere il finecorsa
    }
    digitalWrite(pinTestaMuro, LOW);
    

    // Pausa di 3 secondi
    delay(3000);

    // Quinto Movimento
    digitalWrite(pinCorpoAvanti, HIGH);
    while (digitalRead(pinFCCorpoAvanti) == LOW) {
      // Attendi fino a raggiungere il finecorsa
    }
    digitalWrite(pinCorpoAvanti, LOW);
  }
}

come posso fare per far ripartire il ciclo da capo dopo 5/10 minuti se non viene premuto prima il tasto di avvio? :thinking:

Mi preme prima (e credo anche a te) farti notare che i fine corsa se sono elettromeccanici soffrono di rimbalzi, sia in chiusura che in apertura.

while (digitalRead(pinFCCorpoAvanti) == LOW);

Questo puoi scriverlo senza le graffe se non deve essere eseguito del codice. Ora credo che il rimbalzo dei contatti con questo codice non dovrebbe essere un problema. Mentre con un macchina a stati con millis() il problema del rimbalzo è da prendere in considerazione.

Ciao.

Questa è una cosa di cui ho paura, da testare non appena la parte meccanica è conclusa, mi rincuora l'ultima parte del messaggio, durante i brevi test a banco non ho avuto problemi.

Chiedo venia non ho capito cosa intende, fa parte del codice, ossia non appena legge il finecorsa cambia lo stato del pin corpoavanti in low.

Sempre grazie per la disponibilità.

// Secondo Movimento
    digitalWrite(pinTestaAvanti, HIGH);
    while (digitalRead(pinFCTestavanti) == LOW);
      // Attendi fino a raggiungere il finecorsa
    
    digitalWrite(pinTestaAvanti, LOW);

Comparalo al tuo codice originale e testalo. Un blocco di codice è una entità in C/C++. Un blocco di codice si crea tramite le parentesi graffe.

void loop() { // inizio blocco di codice legato

     {byte x = 1} // blocco di codice innestato non legato
} // fine blocco di codice

La variabile x esiste solo nel blocco di codice innestato (o annidato).

Un blocco di codice è legato ad un comando strutturato come while for, if ecc al fine di estendere la legatura che di default è di un solo comando.

 while (digitalRead(pinFCTestavanti) == LOW) {
      // blocco di codice legato al comando strutturato while
      // qui si scrive il codice che si desidera venga ripetuto fintantoché
      // la condizione risulta vera
 }

Quando non si ha del codice che si vuole ripetere, il blocco di codice
non serve e si conclude il comando con il ;

if (0 == 1)
    Serial.println("1");

Legatura di un solo comando. Se al posto di 0 ci scrivi 1 stamperà 1.

Estensione della legatura tramite blocco di codice:

if (1 == 1) {
    Serial.println("1 == 1 è vero, per cui stampa");
    Serial.println("1");
}

Ti serve una variabile che passa da true (1) a false (0) ogni volta che premi il pulsante. Cioè premi il pulsante è la variabile acceso se valeva 0 diventa 1, se era 1 diventa 0. Io per questa cosa uso una libreria per la gestione dei pulsanti, JC_Button che gestisce i rimbalzi.

#include <JC_Button.h>
bool acceso;
const byte PIN_AVVIO = 2;         // costanti in maiuscolo
Button btnStart(PIN_AVVIO);

void setup()  {
    Serial.begin(115200);
    btnStart.begin()

}

void loop() {
    btnStart.read();
    if (btnStart.wasPressed()) {
         acceso = !acceso;    // inverte il valore della variabile acceso
         Serial.print("acceso =");
        Serial.println(acceso);
    }
}

Ma non l'ho mai usata con codice bloccante come il tuo, cioè con i delay.
Puoi vedere in azione la libreria cliccando sotto, si apre il simulatore online wokwi, premi play e pigi i pulsanti.

E non mi viene in mente come fare con il codice bloccante, tranne l'escamotage suggerito da @Claudio_FF, che puoi simulare seguendo il link al post: Start programma, stop e memoria posizione - #59 by Claudio_FF

Cacchio un poema ho scritto.

Ciao.

1 Like

Grazie grazie grazie, magari a differenza mia sono cose per voi ovvie ma come dicevo all'inizio questo è il mio primo codice, ovviamente molte cose mi sfuggono in ogni caso domani pomeriggio faccio i test suggeriti così mettendoci le mani su capisco meglio in ogni caso ancora grazie.

Si, non sono di primo pelo, ho cominciato nel 2009 e ci davo di delay pure io. Da allora seguo il forum e so come sono fatti i principianti perché lo sono stato e ancora ne ho ricordo.

PS: se avessi la soluzione diretta e facilmente comprensibile la posterei ma non la ho. Attendi magari @Claudio_FF si fa vivo e si interessa al topic.

Ciao.

1 Like