switch fin de course

Bonjour,
je fait un montage pour mon fils qui a besoin d'ouvrir et de fermer une trappe avec trois positions. Il y a trois boutons pour commander, un pour le 0° donc fermé, un pour 15° et un pour 30°.
pas de problèmes pour envoyer, via les trois boutons, le moteur 12v (genre vérin ) trois relais: 2 pour inverser le sens du 12v, un pour on et off courant, et de stopper le vérin a chaque position grâce a un Switch. La ou il y a problème c'est que j’arrête le déplacement en simulant la position en appuyant sur un Switch, si je relache le switch et que j'appelle une nouvelle position c'est ok mais en vrai le switch reste fermé et la, ça ne marche plus. Tant que un des switch est a 1 ça ne marche pas.
Je cherche la solution depuis trois heures sans résultat. une petie aide me serrai bien utile. :slight_smile:

/******* commande pour trappe **********/

#define Bouton0 8
#define Bouton15 9
#define Bouton30 10

#define RelaiA 2
#define RelaiB 3
#define RelaiC 4

#define Sw0 5
#define Sw15 6
#define Sw30 7

int stateBouton0;
int stateBouton15;
int stateBouton30;

int stateSw0;
int stateSw15;
int stateSw30;

int etatsw = 0;

void setup() {

  pinMode(Bouton0, INPUT);
    pinMode(Bouton15, INPUT);
      pinMode(Bouton30, INPUT);
      
  pinMode(RelaiA, OUTPUT);
    pinMode(RelaiB, OUTPUT);
      pinMode(RelaiC, OUTPUT);
      
  pinMode(Sw0, INPUT);
  pinMode(Sw15, INPUT);
  pinMode(Sw30, INPUT);

}

void loop() {

  stateBouton0 = digitalRead(Bouton0);
    stateBouton15 = digitalRead(Bouton15);
      stateBouton30 = digitalRead(Bouton30);
     
  stateSw0 = digitalRead(Sw0);
    stateSw15 = digitalRead(Sw15);
      stateSw30 = digitalRead(Sw30);

    
/********************** Bouton 0° ************************/    
  if(stateBouton0 == 1 ) {
    
    digitalWrite(RelaiC,0); 
     delay(50);  //  petit delai pour laisser le temps a la comutation d'inversion de sens sinon les contacts des relais n'aime pas 
    digitalWrite(RelaiA,0); // relais inversion sens +/- sur moteur
    digitalWrite(RelaiB,0); // relai invertson sens =/- sur moteur
      delay(50);
    digitalWrite(RelaiC,1); // relais on/off 
  }
else if (stateBouton0 == 0 && stateSw0 == 1)
{
 digitalWrite(RelaiC,0);
}

 /********************** Bouton 15° ************************/    
  if(stateBouton15 == 1 ) {
   
    digitalWrite(RelaiC,0);
     delay(50);
    digitalWrite(RelaiA,1);
    digitalWrite(RelaiB,1);
      delay(50);
    digitalWrite(RelaiC,1);
  }

else if (stateBouton15 == 0 && stateSw15 == 1 )
{
 digitalWrite(RelaiC,0);
}

 /********************** Bouton 30° ************************/    
  if(stateBouton30 == 1 ) {
     
    digitalWrite(RelaiC,0);
     delay(50);
    digitalWrite(RelaiA,1);
    digitalWrite(RelaiB,1);
      delay(50);
    digitalWrite(RelaiC,1);
  }
 
else if (stateBouton30 == 0 && stateSw30 == 1)
{
 digitalWrite(RelaiC,0);
}
}

je viens de me rendre compte qu'il y a aussi un problème d'inversion de sens quand le volet est a 30° .
j'essaie avec une variable intermédiaire ( etatsw) pour faire une bascule.
J'y retourne :slight_smile:

Il manque le schéma de câblage. Pour moi, trois relais pour un seul moteur c'est un de trop. deux relais c'est 4 possibilités, on en a besoin de 3 (avant arrière et stop).

Sans le schéma de câblage, on ne peut comprendre quels sont les ordres à envoyer.

comment inverser le sens avec un seul relais? Je n'ai pas trouvé :frowning:
sinon le problème de switch est résolu avec la fonction while (tant que le switch de destination n'est pas a 1 on continu de faire tourner le moteur)

comment inverser le sens avec un seul relais? Je n’ai pas trouvé

Ce n’est pas le nombre de relais qui compte c’est le nombre de contact sur un relai.
Si tu prend un relais avec double contacts inverseur tu constitue un pont en H.
Le principe du pont en H existait avant que les transistors soient inventés.

Le monde des relais n’est pas limité aux produits très bas de gamme d’Ebay ou Ali.

comment inverser le sens avec un seul relais? Je n'ai pas trouvé

Il faut effectivement un relais spécial, mais avec deux relais seulement, on fait les deux sens et l'arrêt:

Avec le montage ci-dessus:
Relais 1 au repos (= comme dessiné) et relais 2 au repos(= comme dessiné) : moteur arrêté
Relais 1 au repos et relais 2 alimenté : moteur tourne dans un sens
Relais 1 alimenté et relais 2 au repos : moteur tourne dans l'autre sens
Relais 1 alimenté et relais 2 alimenté : moteur arrêté, mais ne sert à rien dans notre cas (on n'en a pas besoin).
Il n'y a pas besoin du troisième relais.

Si tu prend un relais avec double contacts inverseur tu constitues un pont en H.

Le schéma ci-dessus est un pont en H. Pas besoin d'avoir un double contact qui nécessiterait d'avoir un deuxième relais pour arrêter le moteur. Avec un pont en H à transistors et à 4 commandes, on peut en plus déconnecter le moteur, ce qui permet la roue libre. Avec les contacts commandés 2 par 2, on n'a pas la roue libre on a un freinage.

Par rapport au .ino ou il y a 3 relais A, B et C, deux suffisent. Du coup on n'a pas besoin du delay(50) pour éviter un démarrage invrersé.

Dans ce type de problème, je conseille d'écrire (de réécrire) la commande pour la position 0°, puis pour la position 30°, et quand elle est au point, on rajoute la position 15°. Pour les deux positions extrêmes, le sens du moteur est toujours le même, c'est plus facile. Pour la position centrale le sens du moteur rajoute une difficulté, donc à voir après.

Note: Comme le code comporte des commentaires, c'est nettement plus agréable à déchiffrer que la pluspart du temps! Bon point.

Il serait bon de s'assurer dans le setup que les relais soient au repos (je suppose qu'on en garde que 2)

digitalWrite(RelaiA,0);
digitalWrite(RelaiB,0);

Pour faire tourner dans un sens:digitalWrite(RelaiA,1);
et pour l'arrêterdigitalWrite(RelaiA,0);

Pour faire tourner dans l'autre sens:digitalWrite(RelaiB,1);
et pour l'arrêterdigitalWrite(RelaiB,0);


Pour l'ancien code (avec 3 relais):

/********************** Bouton 0° ************************/   
  if(stateBouton0 == 1 ) {
   
    digitalWrite(RelaiC,0);
     delay(50);  //  petit delai pour laisser le temps a la comutation d'inversion de sens sinon les contacts des relais n'aime pas
    digitalWrite(RelaiA,0); // relais inversion sens +/- sur moteur
    digitalWrite(RelaiB,0); // relai invertson sens =/- sur moteur
      delay(50);
    digitalWrite(RelaiC,1); // relais on/off
  }
else if (stateBouton0 == 0 && stateSw0 == 1)
{
 digitalWrite(RelaiC,0);
}

Que je peux traduire par:

/********************** Bouton 0° ************************/   
Si je demande d'aller à 0°, je ferme la trappe
Sinon si je ne demande pas d'aller à 0° et que je suis en position 0°, j'arrête le moteur.

Va se poser un problème si je ne relâche pas le bouton: je n'ai plus la condition d'arrêt et le moteur continue de tourner.

Il y a au moins deux solutions:
Code bloquant: Quand on appui sur un bouton le moteur se met en marche et on n'a plus accès à rien tant qu'il n'est pas arrivé à sa position -> on peut donc faire un code pour le bouton 0°:

Si on appuie sur le bouton
{
  Mettre en marche
  Tant qu'on n'est pas en position on ne fait rien (on boucle)
  On arrête le moteur
}

Code non bloquant: Quand on appui sur un bouton le moteur se met en marche et quand il arrive à sa position il s'arrête. Mais pour cela il faut mémoriser quelque par qu'on et en train de tourner. Vu que si la trappe est en train de bouger, il faut arrêter le moteur mais pas forcément sur toutes les positions: si on va de 30° à 0°, on ne s'arrête pas sur 15°, si on va de 30° à 15° on s'arrête sur 15°! On peut alors mémoriser la position finale et s'arrêter si "position finale"="switch". On peut donc faire un code pour le bouton 0°:

Si on appuie sur le bouton
{
  Mettre en marche
  Mémoriser "on va à 0°"
}
Si "on va à 0°" et on est à 0°
{
  Effacer la mémorisation 0°
  On arrête le moteur
}

Mais on aura des problèmes si on appuie sur deux boutons en même temps!

La première solution est la bonne (plus simple) si on n'a rien d'autre à faire.

Bien vue pour les deux relais . Jevais changer ça.
en attendant voici le code avec while qui fonctionne.
le trappe est fermée, le switch 0° est sur 1. quand on appui sur le bouton 15° ca fait tourné le moteur tant que le switch 15° n'est pas a 1.
je n'ai pas pensé au faite d'appuyer sur deux boutons en même temps :confused:

/********************** 0-15 ************************/  
  if(stateBouton15 == 1 && stateSw0 == 1 ) {
        digitalWrite(RelaiC,0); 
        delay(50); 
    while(stateSw15 == 0 ){   // tant que le switch 15° nest pas a 1
      digitalWrite(RelaiA,1); // relais inversion sens +/- sur moteur
      digitalWrite(RelaiB,1); // relais invertson sens +/- sur moteur
      delay(50);
      digitalWrite(RelaiC,1); // relais on/off
      stateSw15 = digitalRead(Sw15); // lecture du switch 15°
  }  
   digitalWrite(RelaiC,0);
  }

Dans le code, il y a toujours les 3 relais. Et notamment si le schéma est bon, coller le deux relais 1 et 2 ne doit pas faire tourner le moteur. Avec le schéma, au repos (normalement on dessine les contacts en position de repos, cad relais non alimenté) les deux relais au repos, le moteur est entre le - et le -, il ne doit pas tourner. si les deux relais sont actifs, le moteur est entre le + et le +, il ne doit pas tourner non plus. Il faut le schéma complet.

je n'ai pas pensé au fait d'appuyer sur deux boutons en même temps.

Avec le dernier code, on met en route le moteur et on l'arrête quand on arrive en position 15°. On ne peut rien faire d'autre quand le moteur tourne, le code est plus simple. Si on joue du piano avec tous les boutons pendant ce temps, il ne se passe rien. C'est donc correct (ce type de fonctionnement c'est celui de mon ascenseur, il ne mémorise pas les appuis sur les boutons et une fois parti, il ira jusqu'à l'étage demandé).

Le dernier code (laissons le relais C pour l'instant), RalaisA (et les autres aussi d'ailleurs) est mis à 1 sans arrêt pendant que le moteur tourne, cela ne sert à rien, une fois qu'il est mis à 1, il y restera. Mais ce n'est pas faux du tout, Mais c'est surtout que ces relais ne sont plus désactivés quand on n'en a plus besoin (consommation inutile).
On pourrait écrire:

/********************** 0-15 ************************/
if (stateBouton15 == 1 && stateSw0 == 1 ) {
  // Mise en marche
  digitalWrite(RelaiC, 0);
  delay(50);
  digitalWrite(RelaiA, 1); // relais inversion sens +/- sur moteur
  digitalWrite(RelaiB, 1); // relais invertson sens +/- sur moteur
  delay(50);
  digitalWrite(RelaiC, 1); // relais on/off
  // Rotation
  while (stateSw15 == 0 ) { // tant que le switch 15° nest pas a 1
    stateSw15 = digitalRead(Sw15); // lecture du switch 15°
  }
  // Arrêt
  digitalWrite(RelaiC, 0);
  delay(50);
  digitalWrite(RelaiA, 0); // relais au repos, on n'en a plus besoin
  digitalWrite(RelaiB, 0); // relais au repos, on n'en a plus besoin
}

On peut ne pas utiliser la variable stateSW15 du tout, cela devient alors:

/********************** 0-15 ************************/
if (stateBouton15 == 1 && stateSw0 == 1 ) {
  // Mise en marche
  digitalWrite(RelaiC, 0);
  delay(50);
  digitalWrite(RelaiA, 1); // relais inversion sens +/- sur moteur
  digitalWrite(RelaiB, 1); // relais invertson sens +/- sur moteur
  delay(50);
  digitalWrite(RelaiC, 1); // relais on/off
  // Rotation
  while (digitalRead(Sw15) == 0 ); // tant que le switch 15° nest pas a 1
  // Arrêt
  digitalWrite(RelaiC, 0);
  delay(50);
  digitalWrite(RelaiA, 0); // relais au repos, on n'en a plus besoin
  digitalWrite(RelaiB, 0); // relais au repos, on n'en a plus besoin
}

Et si on supprime le relais C, et que:

  • relais A et B au repos -> arrêt du moteur (il vaut mieux que si les deux relais soient au repos le moteur soit arrêté car cela évite d'alimenter en permanence un relais)
  • relais A collé, relais B au repos -> rotation dans un sens
  • relais B collé, relais A au repos -> rotation dans l'autre sens sens
    le code devient:
/********************** 0-15 ************************/
if (stateBouton15 == 1 && stateSw0 == 1 ) {
  // Mise en marche
  digitalWrite(RelaiA, 1); // relais marche avant
  // Rotation
  while (digitalRead(Sw15) == 0 ); // tant que le switch 15° nest pas a 1
  // Arrêt
  digitalWrite(RelaiA, 0); // relais au repos, on n'en a plus besoin
}

Merci Vileroi,
j'ai écrit "en attendant, je pose le code qui fonctionne."
je viens d'ouvrir mon ordi et je découvre ton travail. tu est très réactif, merci encore.
sur le morceau de papier ou j'ai écrit dans le train, la correction du code, je voie que c'est exactement ce que tu viens de poster, ça me fait plaisir de voir que j'y arrive grâce a t'es conseils et malgré mon peut de expérience, (quelques mois de pratique)
je vais tester ça des que je rentre.

/******* commande pour trappe **********/

#define Bouton0 8
#define Bouton15 9
#define Bouton30 10

#define Sw0 5
#define Sw15 6
#define Sw30 7

#define RelaiA 2
#define RelaiB 3

int stateBouton0;
int stateBouton15;
int stateBouton30;

int stateSw0;
int stateSw15;
int stateSw30;

void setup() {

  pinMode(Bouton0, INPUT);
  pinMode(Bouton15, INPUT);
  pinMode(Bouton30, INPUT);
  
  pinMode(Sw0, INPUT);
  pinMode(Sw15, INPUT);
  pinMode(Sw30, INPUT);
      
  pinMode(RelaiA, OUTPUT);
  pinMode(RelaiB, OUTPUT);
  digitalWrite(RelaiA,0);
  digitalWrite(RelaiB,0);
       
}

void loop() {

  stateBouton0 = digitalRead(Bouton0);
  stateBouton15 = digitalRead(Bouton15);
  stateBouton30 = digitalRead(Bouton30);
     
  stateSw0 = digitalRead(Sw0);
  stateSw15 = digitalRead(Sw15);
  stateSw30 = digitalRead(Sw30);
   
/********************** Bouton 0-15 ************************/  
  if(stateBouton15 == 1 && stateSw0 == 1 ) {

    while(stateSw15 == 0 ){   // tant que le switch 15° nest pas a 1
      digitalWrite(RelaiB,1); // relai invertson sens +/- sur moteur
      stateSw15 = digitalRead(Sw15); // lecture du switch 15°
  }  
   digitalWrite(RelaiB,0);
  }
  
/********************** Bouton 0-30 ************************/    
  if(stateBouton30 == 1 && stateSw0 == 1) {    

    while(stateSw30 == 0 ){   
      digitalWrite(RelaiB,1);
      stateSw30 = digitalRead(Sw30);
  }
   digitalWrite(RelaiB,0);
  }

 /********************** Bouton 15-30 ************************/   
  if(stateBouton30 == 1 && stateSw15 == 1) {  

    while(stateSw30 == 0 ){
      digitalWrite(RelaiB,1);
      stateSw30 = digitalRead(Sw30);
  }
   digitalWrite(RelaiB,0);
  }

 /********************** Bouton 15-0 ************************/   
  if(stateBouton0 == 1 && stateSw15 == 1) {  

    while(stateSw0 == 0 ){
      digitalWrite(RelaiA,1);
      stateSw0 = digitalRead(Sw0);
  }
   digitalWrite(RelaiA,0);
  }

/********************** Bouton 30-15 ************************/    
  if(stateBouton15 == 1 && stateSw30 == 1) {  

     while(stateSw15 == 0 ){
       digitalWrite(RelaiA,1);
       stateSw15 = digitalRead(Sw15);
  }
   digitalWrite(RelaiA,0);
  }

/********************** Bouton 30-0 ************************/    
  if(stateBouton0 == 1 && stateSw30 == 1) {    

     while(stateSw0 == 0 ){
       digitalWrite(RelaiA,1);
       stateSw0 = digitalRead(Sw0);
  }
   digitalWrite(RelaiA,0);
  }
}

Cela doit fonctionner...


Pour la beauté du code:
la fonction digitalRead(Bouton15); retourne LOW ou HIGH (c'est dit dans la référence). Pour moi, c'est un booléen. Du coup quand on écrit stateBouton15 = digitalRead(Bouton15); la variable stateBouton15 devrait être un booléen et pas un entier signé (en plus on gagne un octet de mémoire).
Et du coup if (stateBouton15 == 1) peut (ou doit?) sécrire if (stateBouton15) Comme il en est de même avec stateSw0, on peut écrire:

bool stateBouton15;
bool stateSw15;
....
if (stateBouton15 && stateSw0)

Hé oui en effet c'est bien plus juste.
Merci. :slight_smile:
pour l'instruction "while" peut on écrire: while(!stateSw30)
ou bien: while(stateSw30 ==0 )
ou encore: while(stateSw30 = false )

pour l'instruction "while" peut on écrire: while(!stateSw30)
ou bien: while(stateSw30 ==0 )
ou encore: while(stateSw30 = false )

ou encore while (stateSw30 = LOW)
La dernière est peut être la plus correcte, la première est plus compacte... Perso, j'utilise la première, même si la dernière est plus lisible par un non expert.


Une autre solution (je l'avais programmé en grafcet pour un ascenseur), consiste à dire:

  • si j'appuie sur le bouton 0°, je mémorise dans memoire0
  • si j'appuie sur le bouton 15°, je mémorise dans memoire15
  • si j'appuie sur le bouton 30°, je mémorise dans memoire30
  • je commence l'ouverture si (memoire30 et que je n'y suis pas déjà) ou si (memoire15 et que je suis en position 0°)
  • je commence la fermeture si (memoire0 et que je n'y suis pas déjà) ou si (memoire15 et que je suis en position 30°)
  • je m'arrête si (memoire0 et je suis en position 0°) ou si (memoire15 et je suis en position 15°) ou si (memoire30 et je suis en position 30°)

L'intérêt de ceci est de donner un code un peu plus petit (pet être plus complexe à comprendre et à coder), et permet de mémoriser l'appui sur les boutons. Par exemple si je suis en position 0° et que j'appuie sur 15°, il y a ouverture. Et si j'appuie sur 30°, il ne va plus s'arrêter sur 15°, mais ira jusqu'à 30°.

Si on n'a que cela à programmer, on se moque complètement que le code soit plus grand ou plus petit!

je vais juste rajouter, soit un écran, soit des leds pour visualiser l'état d'ouverture de la trappe, donc un code pas très long.
Je vais quand même étudier ta dernière proposition, pour le fun :).
je suis sur un autre projet: affichage des instruments de vol et valeurs moteur pour mon ULM. j'ai déja fait un Badin "vitesse air" qui fonctionne bien et un genre de navigation GPS qui me pose pleins de problèmes, mais quand mes competances seront plus fortes j'y reviendrai.

je vais juste rajouter, soit un écran, soit des leds pour visualiser l'état d'ouverture de la trappe

Moi j'ai toujours un faible pour les écrans. Entre trois boutons plus trois leds de position plus deux leds pour le sens de marche OU un écran avec touchscreen bien propre, bien mignon, ya pas photo. Plus besoin de boutons...
Bon courage.

certe, c'est plus moderne on va dire :slight_smile:

J'ai remis a jour le code, :slight_smile:

#define Bouton0 8
#define Bouton15 9
#define Bouton30 10

#define Sw0 5
#define Sw15 6
#define Sw30 7

#define RelaiA 2
#define RelaiB 3

bool stateBouton0;
bool stateBouton15;
bool stateBouton30;

bool stateSw0;
bool stateSw15;
bool stateSw30;

void setup() {

  pinMode(Bouton0, INPUT);
  pinMode(Bouton15, INPUT);
  pinMode(Bouton30, INPUT);
  
  pinMode(Sw0, INPUT);
  pinMode(Sw15, INPUT);
  pinMode(Sw30, INPUT);
      
  pinMode(RelaiA, OUTPUT);
  pinMode(RelaiB, OUTPUT);
  digitalWrite(RelaiA,0);
  digitalWrite(RelaiB,0);
       
}

void loop() {

  stateBouton0 = digitalRead(Bouton0);
  stateBouton15 = digitalRead(Bouton15);
  stateBouton30 = digitalRead(Bouton30);
     
  stateSw0 = digitalRead(Sw0);
  stateSw15 = digitalRead(Sw15);
  stateSw30 = digitalRead(Sw30);
   
/********************** Bouton 0-15 ************************/  
  if(stateBouton15 && stateSw0 ) {

    while(stateSw15 == 0 ){   // tant que le switch 15° nest pas a 1
      digitalWrite(RelaiB,1); // relai invertson sens +/- sur moteur
      stateSw15 = digitalRead(Sw15); // lecture du switch 15°
  }  
   digitalWrite(RelaiB,0);
  }
  
/********************** Bouton 0-30 ************************/    
  if(stateBouton30 && stateSw0 ) {    

    while(stateSw30 == 0 ){   
      digitalWrite(RelaiB,1);
      stateSw30 = digitalRead(Sw30);
  }
   digitalWrite(RelaiB,0);
  }

 /********************** Bouton 15-30 ************************/   
  if(stateBouton30 && stateSw15 ) {  

    while(stateSw30 == 0 ){
      digitalWrite(RelaiB,1);
      stateSw30 = digitalRead(Sw30);
  }
   digitalWrite(RelaiB,0);
  }

 /********************** Bouton 15-0 ************************/   
  if(stateBouton0 && stateSw15 ) {  

    while(stateSw0 == 0 ){
      digitalWrite(RelaiA,1);
      stateSw0 = digitalRead(Sw0);
  }
   digitalWrite(RelaiA,0);
  }

/********************** Bouton 30-15 ************************/    
  if(stateBouton15 && stateSw30 ) {  

     while(stateSw15 == 0 ){
       digitalWrite(RelaiA,1);
       stateSw15 = digitalRead(Sw15);
  }
   digitalWrite(RelaiA,0);
  }

/********************** Bouton 30-0 ************************/    
  if(stateBouton0 && stateSw30 ) {    

     while(stateSw0 == 0 ){
       digitalWrite(RelaiA,1);
       stateSw0 = digitalRead(Sw0);
  }
   digitalWrite(RelaiA,0);
  }
}

Note: un petit cnt-T sous l'IDE d'Arduino permet de mieux indenter le code et il est plus lisible (les instructions de même niveau s'allignent).

Pour moi, cela doit fonctionner. On peut éventuellement simplifier le code et s’apercevoir que si au moment ou l'on débranche le moteur tournait, au moment ou on rallume, les switchs de positions sont tous inactifs. Aucune condition n'est alors valide et on ne peut ni ouvrir ni fermer.

Voici deux extraits de code:

  /********************** Bouton 0-30 ************************/
  if (stateBouton30 && stateSw0 ) {
    while (stateSw30 == 0 ) {
      digitalWrite(RelaiB, 1);
      stateSw30 = digitalRead(Sw30);
    }
    digitalWrite(RelaiB, 0);
  }
  /********************** Bouton 15-30 ************************/
  if (stateBouton30 && stateSw15 ) {
    while (stateSw30 == 0 ) {
      digitalWrite(RelaiB, 1);
      stateSw30 = digitalRead(Sw30);
    }
    digitalWrite(RelaiB, 0);
  }

A par la condition, le cors est le même. Si on a:
si (condition n°1) faire Trucmuche
si (condition n°1) faire Trucmuche
on peut le remplacer par:
si (condition n°1) et (condition n°1) faire Trucmuche
On peut donc remplacer ces deux paragraphes par un seul.
Quand doit-on ouvrir quand on appuie dur 30°? Finalement dans tous les cas! non seulement quand on est sur la position 0° ou sur la position 15°, mais aussi si on est au milieu (possible après une mise sous tension si on a coupé entre deux positions). Cela permettra de démarrer au début quel que se=oit l'état.
Note: si à la mise sous tension on est n'importe où, les boutons 30° et 0° peuvent être utilisés? 15° non car on ne peut pas savoir si on est entre 0° et 15° ou entre 15° et 30°.
Il n'y a que si on est en 30° qu'il ne faut pas ouvrir. La condition est donc pour les deux extraits du dessus "si on appuie sur 30° et qu'on n'est pas en 30°"
Ces parties deviennent:

/*************** Bouton 0-30 & Bouton 15-30 ****************/
    if (stateBouton30 && !stateSw30 ) {
    while (stateSw30 == 0 ) {
      digitalWrite(RelaiB, 1);
      stateSw30 = digitalRead(Sw30);
    }
    digitalWrite(RelaiB, 0);
  }

Il en est de même pour la fermeture 15-0 et 15-30. Pour le reste on ne peut pas grouper, car on ne mémorise pas l'ordre. L'effet mémoire est obtenu par le code.
On peut simplifier ou pas le code, c'est selon chacun. J'ai l'habitude de simplifier pour diminuer le nombre de variables, mais c'est au détriment parfois de la simplicité de lecture. En tout cas pour cette partie, on peut remarquer que la variable stateSW30 n'est écrite que pour être lue à la boucle d'après. On peut simplifier par:

  /*************** Bouton 0-30 & Bouton 15-30 ****************/
  if (stateBouton30 && stateSw0 ) {
    while (digitalRead(Sw30) == 0 ) {
      digitalWrite(RelaiB, 1);
    }
    digitalWrite(RelaiB, 0);
  }

et un couple { } devient inutile (une seule instruction) et on peut écrire:

  /*************** Bouton 0-30 & Bouton 15-30 ****************/
  if (stateBouton30 && stateSw0 ) {
    while (digitalRead(Sw30) == 0 ) digitalWrite(RelaiB, 1);
    digitalWrite(RelaiB, 0);
  }

enfin digitalRead(Sw30) est un booléen et

while (digitalRead(Sw30) == 0 )

c'est pareil que

while (!digitalRead(Sw30))

==0 ou ==false c'est équivalent,
a==0 équivaut à a==false qui est équivalent à !a==true qui est aussi !a
Bien entendu, si cela ne te parle pas, ne le fait pas. Cela fonctionne pareil, mais peut économiser quelques variables... pour quand les codes deviennent trop importants. Et cela finit par arriver!

j'ai un peu de mal avec cette écriture:

  /*************** Bouton 0-30 & Bouton 15-30 ****************/
  if (stateBouton30 && stateSw0 ) {
    while (digitalRead(Sw30) == 0 ) digitalWrite(RelaiB, 1);
    digitalWrite(RelaiB, 0);
  }