Projet Bar a Cocktail fonctionnel mais ....

Bonsoir a tous,

Ci apres vous trouverez le code qui me permet de controler un bar a cocktail Via Bluetooth. Shema joint également.

Celui ci fonctionne mais j aurais souhaite, grace a votre aide, en ameliorer le concept.

  • Je commande donc un moteur DC qui avance ou recule le chariot -donc le verre- sous les bouteilles
  • Ensuite je déclenche (ou non) le servo moteur qui appui sur les bouchons doseurs.
  • Et enfin, via les 4 relais, je commande les mini-pompes (pour les boissons gazeuses)

Je voudrais pouvoir ajouter un capteur de distance ultrasons afin que les ordres transmis via Bluetooth ne s exécute que si et seulement si un verre est positionné sur son emplacement de départ (le chariot).

Et pourquoi ne pas y ajouter 2 interrupteurs de fin de course de chaque coté du chariot.

N ayant jamais eu a intégrer de tels capteurs, j aimerais pouvoir bénéficier de vos lumières. un peu d aide serait bienvenue.

#include <SoftwareSerial.h>
#include <Servo.h>

SoftwareSerial Bluetooth(10, 11); // RX | TX

Servo servo;

int pompe1 = 3;    // les 4 pompes déclenchées par les relais
int pompe2 = 4;
int pompe3 = 5;
int pompe4 = 6;
int state = 0;

int servoPin = 7;
int servoAngle = 0;   // Position du servo moteur en degres

int Moteur1_Retour = 8;
int Moteur1_Avance = 9;

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

  pinMode(pompe1,OUTPUT);
  pinMode(pompe2,OUTPUT);
  pinMode(pompe3,OUTPUT);

  pinMode(Moteur1_Avance, OUTPUT);
  pinMode(Moteur1_Retour, OUTPUT);

  servo.attach(servoPin);
}

void loop() {

  if(Serial.available() > 0)
  {
    char data;
    data = Serial.read();
    Serial.write(Serial.read());

    switch (data)      // Delai et sens a revoir mais fonctionnel
    {
      case '1': // Whisky Coca
        digitalWrite(Moteur1_Avance, HIGH);
        delay(4000);
        digitalWrite(Moteur1_Avance, LOW);
        servo.write(45);
        delay(2500);
        servo.write(165);
        delay(500);
        digitalWrite(Moteur1_Avance, HIGH);
        delay(5000);
        digitalWrite(Moteur1_Avance, LOW);
        servo.write(45);
        delay(2500);
        servo.write(165);
        delay(500);
        digitalWrite(Moteur1_Retour, HIGH);
        delay(3000);
        digitalWrite(Moteur1_Retour, LOW);
        digitalWrite(pompe1, HIGH);
        delay(5000);
        digitalWrite(pompe1, LOW);
        break;
      case '2': // Vodka Orange
         digitalWrite(Moteur1_Avance, HIGH);
        delay(3000);
        digitalWrite(Moteur1_Avance, LOW);
        servo.write(45);
        delay(2500);
        servo.write(165);
        delay(500);
        digitalWrite(Moteur1_Avance, HIGH);
        delay(5000);
        digitalWrite(Moteur1_Avance, LOW);
        servo.write(45);
        delay(2500);
        servo.write(165);
        delay(500);
        digitalWrite(Moteur1_Retour, HIGH);
        delay(3000);
        digitalWrite(Moteur1_Retour, LOW);
        digitalWrite(pompe1, HIGH);
        delay(5000);
        digitalWrite(pompe1, LOW);
        break;
      case '3': // Rhum Coca
         digitalWrite(Moteur1_Avance, HIGH);
        delay(6000);
        digitalWrite(Moteur1_Avance, LOW);
        servo.write(45);
        delay(2500);
        servo.write(165);
        delay(500);
        digitalWrite(Moteur1_Avance, HIGH);
        delay(5000);
        digitalWrite(Moteur1_Avance, LOW);
        servo.write(45);
        delay(2500);
        servo.write(165);
        delay(500);
        digitalWrite(Moteur1_Retour, HIGH);
        delay(7000);
        digitalWrite(Moteur1_Retour, LOW);
        digitalWrite(pompe1, HIGH);
        delay(5000);
        digitalWrite(pompe1, LOW);
        break;
      case '4': //Pastis par temps bleu
        digitalWrite(Moteur1_Avance, HIGH);
        delay(5000);
        digitalWrite(Moteur1_Avance, LOW);
        servo.write(45);
        delay(2500);
        servo.write(165);
        delay(500);
        digitalWrite(Moteur1_Avance, HIGH);
        delay(5000);
        digitalWrite(Moteur1_Avance, LOW);
        servo.write(45);
        delay(2500);
        servo.write(165);
        delay(500);
        digitalWrite(Moteur1_Retour, HIGH);
        delay(3000);
        digitalWrite(Moteur1_Retour, LOW);
        digitalWrite(pompe1, HIGH);
        delay(5000);
        digitalWrite(pompe1, LOW);
        break;
      case '5': // Coca Cola
        digitalWrite(Moteur1_Avance, HIGH);
        delay(5000);
        digitalWrite(Moteur1_Avance, LOW);
        delay(500);
        digitalWrite(pompe1, HIGH);
        delay(5000);
        digitalWrite(pompe1, LOW);
        break;
      case '6': // Jus d'Orange
        digitalWrite(Moteur1_Avance, HIGH);
        delay(5000);
        digitalWrite(Moteur1_Avance, LOW);
        delay(500);
        digitalWrite(pompe1, HIGH);
        delay(5000);
        digitalWrite(pompe1, LOW);
        break;
      default: //Si Bluetooth ne recoit aucune valeur, mets le moteur en OFF
        digitalWrite(Moteur1_Avance, LOW);
        digitalWrite(Moteur1_Avance, LOW);
        digitalWrite(Moteur1_Retour, LOW);
        digitalWrite(Moteur1_Retour, LOW);
    }
  }
}

Rigolo votre projet - envoyez nous une photo du truc fonctionnel :wink:

Pour le moment votre code est plein de delay() ce qui est bloquant. si vous voulez utiliser des interrupteurs de fin de course ou des système de détection, il va falloir modéliser différemment votre code de façon à ce que la boucle tourne en permanence et teste des conditions puis prenne les décisions qui conviennent.

c'est typiquement une définition de programme qui se prête bien à la programmation par machine à états (cf mon tuto éventuellement)

Bonjour,
Oui je suis certain que ca doit etre largement optimisable.
Je mettrais des photos du tout dans "projets finis", quand j aurais finis de le monter, pour le moment j attends encore quelques pièces .
J ai encore quelques interrogations, notamment sur les "delay" dont vous parlez. si ma courroie ne se dérègle pas, par exemple a cause du poids du verre qui se rempli, les délais de traction du chariot sont bons, sinon il va me falloir des capteur de proximité qui auront pour fonction d arrêter le chariot pile sous la bouteille désiré .
On verra a l'usage ... je manquerais pas d y revenir ici.
En tout cas, merci pour le lien, je vais y regarder de près.

le problème de delay() c'est qu'une fois appelé vous ne pouvez pas vérifier autre chose, l'appel est bloquant. vous ne pouvez pas dire

Tant Que (delay(5000) ET PAS "sous la bouteille") alors faire bouger le verre

Pour cela vous pouvez envisager de faire une fonction attente() un peu plus perso (en utilisant millis() ) qui n'est pas bloquante mais qui vous retourne un booléen pour vous dire si le délai est expiré ou pas. Par exemple regardez le code de la réponse 5 de ce post

/*
  non blocking delay
  In:
    duration of delay
  Returns:
    false if delay is in progress, else true
*/
bool waitALittle(unsigned long duration)
{
  static unsigned long startTime = 0;

  // if no start time set, 'start' the delay
  if (startTime == 0)
  {
    startTime = millis();
    return false;
  }

  // check if delay has lapsed
  if (millis() - startTime >= duration)
  {
    startTime = 0;
    return true;
  }

  return false;
}

(source de @sterretje)

Bonsoir,
Oui l idee me semble pas mal mais je ne suis pas sur de maitriser le(s) variable(s).
Une autre idee, peut etre, serait d utiliser un capteur de distance sr-04 , on pourrais ainsi arreter le chariot lorsque celui ci se trouve a tel ou tel distance .... qui correspondrait pile a l emplacement sous la bouteille !
De plus, il n y aurait plus besoin d interrupteur de fin de course . la chariot pourrait s arreter au bon endroit .
J ai conscience qu on ne parlerait plus de délai de course du chariot mais simplement de distance en cms depuis le capteur sr-04.

Qu en dites vous ?

Oui à essayer - il faudra que le SR-04 ait une vue bien dégagée
Comment est le montage physique du charriot ? Vous avez un rail de guidage ? En plaçant des petits contacteurs au niveau de chaque bouteille et un au bout rend switch) ça sera sans doute plus précis

Bonsoir,
Oui, c est bien un rail de guidage.
Je poste un photo promis des que j ai termine de le monter.
A quels types de contacteur pensez vous ?

En attendant, j ai modifier le code pour y joindre le SR04 . Ca me permet d'oter bon nombre de variable "relay",
c est précis également. Mais je ne le saurais qu une fois que le verre se remplira physiquement.

case '1':                                                         //                         Whisky Coca
     if ( distance = 100)                         // Enclenche le chariot si celui ci est sur son emplacement de depart
       digitalWrite(Motor_A_Forward, HIGH);                
       if ( distance = 50)                                 // Arrete le chariot a 50 cm du capteur 
       digitalWrite(Motor_A_Forward, LOW);                    
       servo.write(45);                                  
       delay(2500);
       servo.write(165);                                    // Declenche le bouchon doseur grace au servo moteur  
       digitalWrite(Motor_A_Forward, HIGH);                    
       if ( distance = 20)                                // Arrete le chariot a 20 cm du capteur 
       digitalWrite(Pompe1, HIGH);
       delay(5000);
       digitalWrite(Pompe1, LOW);                     // Declenche la pompe N1   
       digitalWrite(Motor_A_Forward, LOW);
       delay(500);
       digitalWrite(Motor_A_Reverse, HIGH);      // Retour du chariot a son emplacement de depart
       if ( distance = 100)
       digitalWrite(Motor_A_Reverse, LOW);
       break;

Attention il y a de multiple soucis avec ce code

  • il faut '==' au lieu de '=' pour tester une égalité, sinon vous faites une affectation et là vous verrez que plus rien ne va fonctionner...

  • il faut des accolades {} autour des instructions à exécuter si le 'if' est vrai

  • vous ne pouvez pas utiliser de delay() et ensuite tester la position une seule fois, il faut enlever le delay() et mettre une boucle qui lit la position et tant qu'elle n'est pas bonne le moteur continue

  • pour tester la position vaut mieux un intervalle if (d >= 49 && d <= 51) {...} par exemple car vous n'êtes pas sûr d'arriver pile au bon endroit (50 tout rond) et si vous le dépassez le moteur va alors continuer sans fin

Côté testeur jetez un œil à des "Interrupteurs Fin de Course a Roulette" ou alors "Interrupteur Fin de Course a Levier long" (que vous courberez un peu pour qu'il n'accroche pas le charriot et le laisse passer dans les 2 sens) sur eBay ou votre vendeur favori

Autre option sans contact un détecteur infrarouge soit sous forme de U (genre LM393) et vous faites passer un morceau de votre charriot entre les branches de U ou alors un Module de capteur infrarouge d'évitement d'obstacles qui par reflection sur le charriot sera capable de dire s'il est devant le module

Ok merci des ces précieux conseils.

Pour les rupteurs fin de course, j avais un peu anticipé et commander des 'OOTDTY' , mais je crois bien, en effet, que ceux a roulettes seraient plus judicieux.
Je verrais pour les intégrer au code quand je les aurais recu.

Tant que je n ai pas recu le chariot definitif pour test, je suis un peu dans l expectative. Mais j en apprends un peu plus chaque jour, donc merci encore pour l aide précieuse.

En attendant je tentes de modifier le codes en fonction des vos dernières indications.

A suivre ...

Il faudra restructurer le code sous forme de machine à état si vous mettez plein de capteurs et actionneurs - cf mon tuto éventuellement la dessus

Bon, en essayant d'intégrer vos remarques ;

case '1': //                         Whisky Coca
     if ( distance >= 99 && distance <= 101)  {                       
       digitalWrite(Motor_A_Forward, HIGH);   }              
       if ( distance >= 49 && distance <= 51)  {                            
       digitalWrite(Motor_A_Forward, LOW);                    
       servo.write(45);                                  
       delay(2500);
       servo.write(165);                               
       digitalWrite(Motor_A_Forward, HIGH);   }                   
       if ( distance >= 19 && distance <= 21) {                        
       digitalWrite(Pompe1, HIGH);
       delay(5000);
       digitalWrite(Pompe1, LOW);                        
       digitalWrite(Motor_A_Forward, LOW);
       delay(500);
       digitalWrite(Motor_A_Reverse, HIGH);}
       if ( distance <= 100)  { 
       digitalWrite(Motor_A_Reverse, LOW);}
       break;

Quant a ceci :

"- vous ne pouvez pas utiliser de delay() et ensuite tester la position une seule fois, il faut enlever le delay() et mettre une boucle qui lit la position et tant qu'elle n'est pas bonne le moteur continue "

je ne comprends pas quoi ni comment faire.

ensuite, concernant le code sous forme de machine a etat, j y travailles. meme si je ne suis pas sur de bien suivre.

Avec un bout de code isolé impossible de commenter vraiment ? Est-ce dans la loop() et va être appelé automatiquement plein de fois ?

Sinon ou distance est-elle calculée et mise à jour pendant que le moteur bouge ?

Bonsoir a Tous.
Et pardon de ne pas être revenu plus tot.
Donc comme promis, voici une photo du bar quasiment fini, en tous cas fonctionnel même si sur cette photo, il manque les valves pour les pompes fixées a l arriere ainsi que le servo moteur fixé sur le chariot, demontés pour le moment, pour fignoler un peu le look, passer un coup de vernis etc ...

Donc oui, ca fonctionne comme initialement prevu, mais avec le temps et le nombres d ordres lancés, mon chariot finit toujours par se décaler au fur et a mesure de la soiree, d abord de quelques mm puis de quelques cm.

J ai donc teste le capteur a Ultra son SR04, mais celui ci s avere trop peu efficace lorsque l on dépasse les 20/30 cm, mon chariot va plus vite que la vitesse du "ping".

La deuxieme tentative, pour pouvoir arreter le chariot a son emplacement de depart (lors du retour) , est donc de monter 2 rupteurs fin de course( un de chaque coté). J ai donc tenté de les integrer au code.... mais je dois faire une erreur quelques part car ca ne fonctionne pas.

Ci apres,voici le code 'version light', si quelqu un pouvait me dire ou je me trompes, ca serait top.

// J ajoutes 2 capteurs fin de course

#include <SoftwareSerial.h>
#include <Servo.h>

Servo servo;

int Pompe1 = 8;
int Pompe2 = 9; 
int Pompe3 = 10;
int Pompe4 = 11;
int state = 0;

const int button1 = 5;              
const int button2 = 6;
int buttonState1 = 0;              // variable for reading the pushbutton1 status
int buttonState2 = 0;              // variable for reading the pushbutton2 status              

int servoPin = 7;
int servoAngle = 0;   // Position du servo moteur en degres

int Motor_A_Forward = 3;
int Motor_A_Reverse = 4;

void setup() {

  Serial.begin(9600);
  
  servo.attach(servoPin);
  
  pinMode(Motor_A_Reverse, OUTPUT);
  pinMode(Motor_A_Forward, OUTPUT);

  pinMode(Pompe1,OUTPUT);
  pinMode(Pompe2,OUTPUT);
  pinMode(Pompe3,OUTPUT);
  pinMode(Pompe4,OUTPUT);

  pinMode(button1, INPUT_PULLUP);
  pinMode(button2, INPUT_PULLUP);

}

void loop() {

  if(Serial.available() > 0)
  {
    char data;
    data = Serial.read();
    Serial.write(Serial.read());

    switch (data)
    {
      case '1': //                             5Whisky 20Coca
        digitalWrite(Motor_A_Forward, HIGH);
        delay(1000);
        digitalWrite(Motor_A_Forward, LOW);                // Avance le chariot vers bouteille de Whisky
        
        servo.write(45);
        delay(2500);
        servo.write(165);                                // Declenche le bouchon doseur grace au servo moteur
        
        digitalWrite(Motor_A_Forward, HIGH);
        delay(1000);
        digitalWrite(Motor_A_Forward, LOW);                // Avance le chariot vers  la bouteille de Coca
        
        digitalWrite(Pompe1, HIGH);
        delay(2000);
        digitalWrite(Pompe1, LOW);                       // Declenche la pompe N1 pendant 5 secondes


        if (buttonState1 == LOW) {                           // Revient a la position de depart
        digitalWrite(Motor_A_Reverse, LOW);             // C EST ICI QUE CA NE FONCTIONNE PAS
        delay (500);                                                 // RUPTEUR INOPERANT
        digitalWrite(Motor_A_Reverse, HIGH);
        }
        break;
        
        
      default: //Si le module bluetooth ne recoit aucune valeur, tout mettre en OFF
      
        digitalWrite(Motor_A_Reverse, LOW);
        digitalWrite(Motor_A_Forward, LOW);
         pinMode(Pompe1,OUTPUT);
         pinMode(Pompe2,OUTPUT);
         pinMode(Pompe3,OUTPUT);
         pinMode(Pompe4,OUTPUT);
    }
  }
}

Bonsoir

Le post 9 est toujours d'actualité... joli montage sinon !

Bsr, JML l insomniaque, j avais promis donc ....
je ferais une petite video pour monter le tout, y compris l appli que j ai fait sous android (capture ecran ci apres).
et pour sur ca ira dans projet fini des que ce sera peaufiner.
D ailleurs si ca interesse quelqu un, je laisserai l appli en telechargement libre également.

Et crois moi, j essai de m inspirer de ton propre montage mais y a des trucs que je comprends pas.
et pourtant je fais des efforts !

Bonjour

si vous rajoutez des détecteurs de position ils peuvent vous servir de référentiel d'état.

Votre machine en gros pourrait ressembler à cela:

Vous avez deux détecteurs de fin de course pour éviter de dérailler (repos gauche et droite) et des détecteurs sous chaque bouteille.

Au début du programme vous allez ranger le mobile sur une des deux positions de repos pour savoir exactement où vous êtes. c'est assez simple puisque'il suffit de bouger vers la Gauche (ou droite au choix) jusqu'à ce qu'un événement capteur reposG soit reçu.

Ensuite il suffit d'attendre un événement "ordre de boisson" par exemple citronnade (ici non alcoolisées pour ne pas faire de la publicité :slight_smile: ) qui détermine vers quel capteur on doit bouger. Vous savez que vous êtes tout à gauche, donc vous déclenchez un mouvement vers la droite (état = enMouvement)

Vous savez que votre étatCible c'est Citronnade qui sera atteint que quand le détecteur citronnade est activé. Donc votre loop() reste simplement en attente d'évènements. ça permet d'avoir des ordres Stop, arrêt d'urgence, changement d'avis sur la boisson etc. Votre mobile lui continue de bouger tranquillement, vous ignorez les déclenchements de détecteurs tant que ce n'est pas celui de l'étatCible (et bien sûr les 2 bouts)

Au bout d'un moment votre loop() va détecter que le bon capteur s'est activé

à ce moment là vous changez d'état. Vous êtes dans l'état Citronnade, l'action à déclencher c'est ouverture pendant un certain temps de la vanne pour servir la boisson.

Là encore, vous ouvrez la vanne mais ne faites pas un delay() le temps de remplir le verre. simplement vous vous mettez dans un état où l'événement attendu est un timeOut sur la durée d'écoulement, ou la masse / poids du verre ou ce que vous voulez mesurer pour dire "action effectuée, verre rempli". Une fois reçu cet évènement, vous pouvez fermer la vanne et vous relancez le déplacement moteur vers le lieu de livraison, par exemple tout à Droite.

Votre état est donc enMouvement, votre loop() reste simplement en attente d'évènements et celui cible c'est le capteur de droite ce coup ci. Donc pas de code spécifique, la boucle est toujours en attende d'évènements et en fonction de l'état courant et d'un état cible décide quoi faire quand un évènement arrive.

voilà avec cela vous pourriez devoir dessiner la machine a états - elle est un peu plus compliqué mais ce n'est pas insurmontable je pense

c'est plus clair ?

Bonjour
Sympa comme réalisation

Perso , je verrais bien un lecteur RFID bien positionné et lisant du tag autocollant collé sur les bouteilles "qui se présentent" , voir éventuellement 2 tag fixes faisant office de fin de course.
Ca pourrait permettre une gestion plus souple de ce qui est présent sur "bar"

NB : J'ai fais voir la photo à ma femme , sa réponse a été :
"essaye de mettre ça ici et je demande le divorce ! " :grin:

Merci de ces éclaircissements, je vais travailler la dessus.

La solution (très provisoire) que j ai trouvé étant de dérailler manuellement la courroie d entrainement du chariot toutes les 10 boissons environ pour remettre le chariot a sa position initial.
Car a vrai dire, si celui ci retourne bien sur ce plot, et donc que le point de départ se fassent bien de cet endroit, le timing est bon (pas parfait mais bon) .

Je pourrais aussi remettre simplement le SR04 uniquement pour arrêter la chariot a bon endroit lors du retour, ca suffirait (lui j arrives a le coder maintenant, merci JML).

Mais j aime bien le principe de la machine a état, je vais donc essayer ca ... advienne que pourra !!!

Les Tags RFID, oui, j y avais pensé ... pourquoi pas ! Essayez de votre coté, et quand madame aura demander le divorce, tenez moi informé ! :slight_smile:

Mais vraiment personne ne peut me dire ce qui cloche dans mon code ? au niveau du rupteur ...
Je n aime pas rester sur un échec et pire encore .... une interrogation .
Allez quoi, une bonne âme !
:slight_smile:

dans votre code vous faites

  if (Serial.available() > 0) {
    char data;
    data = Serial.read();
    Serial.write(Serial.read());

donc s'il y a un octet dispo, vous le lisez ( data = [color=red]Serial.read()[/color];) mais tout de suite après vous imprimez le caractère suivant Serial.write([color=red]Serial.read()[/color]);[ --> vous n'êtes pas sûr d'avoir un second caractère de dispo donc ce que vous imprimez n'a rien à voir avec ce que vous avez lu. il faudrait faire Serial.write(data);[ pour cela

Sinon votre code de test de retour à la position de départ n'est exécuté que si vous avez reçu un caractère et que c'est '1'...

ce delay() et ce commentaires ne sont pas d'accord entre eux....

        digitalWrite(Pompe1, HIGH);
        delay(2000);
        digitalWrite(Pompe1, LOW);                       // Declenche la pompe N1 pendant 5 secondes

5 secondes ou deux secondes?

dans la seconde partie de votre switch/case vous avez comme commentaire default: //Si le module bluetooth ne recoit aucune valeur, tout mettre en OFF mais en fait c'est exécuté si vous recevez autre chose que '1'...

bref - ce n'est pas codé comme il faut.

dans la boucle vous devez attendre des évènements; un truc sur le port série, un détecteur de fin de course par exemple. quand vous le recevez vous le traitez et changez d'état pour attendre un autre évènement

ça pourrait (en simplifié sans traiter les cas d'erreurs) ressembler à ça

(et sinon juste pour la beauté de la chose et l'optimisation mémoire, ce serait bien de déclarer toutes vos pins en const byte et pas en int

Bonsoir,
Je vais en tenir compte, je reviens des que tout et arrangé.
En ce qui concerne la machine a etat ... j y travailles !
Je progresse ... doucement mais surement.
En tout cas merci de ces commentaires constructifs !!!