===== ⏲️ 🍄 Compte - Temps Interactif ===== {{tag>arduino processing montage 3Ddesign 3DPrint}} Un dispositif qui mêle affichage écran (Processing) ET bouton physique (Arduino) pour créer un simple compte-temps pour une réalisation chroonométrée. On lance l'activité, le compteur démarre pour tous et le premier qui a fini appuie sur le bouton pour arrêter le compteur. ==== étape 1 : du code simple de chaque côté==== Le code Processing pour un chrono sans interaction. // objet global Chrono, déclaration de classe StopChronoTimer sc; // Communication avec Arduino import processing.serial.*; Serial myPort; // Create object from Serial class static String val; // Data received from the serial port int btnVal = 0; void setup() { size(800, 600); println (millis()); sc = new StopChronoTimer(); sc.start(); String portName = "COM3";//à changer selon config matériel myPort = new Serial(this, portName, 9600); } void draw() { top(); } /// FONCTIONS void top() { // dessine le texte-temps sur l'écran background(220); fill(000); textSize(128); textAlign(CENTER); // nf est utile pour transformer des chiffres en string text https://processing.org/reference/nf_.html text(nf(sc.heures(), 2)+":"+nf(sc.minutes(), 2)+":"+nf(sc.secondes(), 2), width/2,height/2); } // CLASSES class StopChronoTimer { int startTime = 0; int stopTime = 0; boolean running = false; void start() { startTime = millis(); running = true; } void stop() { stopTime = millis(); running = false; } int gTempsPasse() { int tempsP; if (running) { tempsP = (millis()-startTime); } else { tempsP =(stopTime - startTime); } return tempsP; } int secondes() { return (gTempsPasse()/1000) % 60; } int minutes() { return (gTempsPasse()/(1000*60)) % 60; } int heures() { return (gTempsPasse()/(1000*60*60)) % 24; } } Arduino : un bouton poussoir qui change une valeur... /* Projet Compte-Temps interactif avec Processing Matériel : Arduino UNO + Bouton Poussoir + LED(& sa résistance) Quand on appuie sur le bouton le compteur à l'écran s'arrête ! v1 : utilisation du Serial pour parler à Processing donc halte aux grands messages */ // Cablage const int btnPin = A0; // bouton poussoir 6 pattes : 4 fonctionnent, c'est le même côté qui est mis en relation ! (1 sur gnd et 1 sur A0) const int ledPin = 8; // on envoie du courant sur l'anode via le pin 8 //Variables int btnVal = 0; bool btnEtat = false; bool OldbtnEtat = false; bool ledEtat = LOW; void setup() { //Init Serial USB Serial.begin(9600); //// Serial.println(F("Coucou c'est le début")); //Init btn avec une résistance interne pour éviter les fronts pinMode(btnPin, INPUT_PULLUP); // init Led pinMode(ledPin, OUTPUT); } void loop() { testBtn(); } void testBtn() { ////Read pushbutton btnVal = analogRead(btnPin); //// Serial.print(btnVal); //// Serial.print(F("-->")); if (btnVal < 200) { btnEtat = true; //// Serial.println(F("Bouton Appuyé")); // alors on envoie une info au serial Serial.println("btnA"); } else { btnEtat = false; //// Serial.println(F("Bouton relâché")); Serial.println("btnR"); } // si l'tat du bouton a changé if (OldbtnEtat != btnEtat) { //// Serial.println(F("Bouton change")); // ET si l'etat actuel est pressé if (btnEtat == true) { // alors on change la variable état de led ledEtat =! ledEtat; //// Serial.println( ledEtat ); // et on actionne la led en fonction digitalWrite(ledPin,ledEtat); } } OldbtnEtat = btnEtat; // delay(800); } C'est tout bête il me faut une carte Uno, un bouton poussoir et là j'ai mis une LED pour témoigner que ça se passe bien... Chaun des 2 codes ici "fonctionnent", mais comment faire pour que ces 2 pgm interagissent? Objectif : qu'un appui sur le bouton poussoir arrête le chrono affiché à l'écran. ==== étape 2 : Communication via Serial ==== ici Arduino envoie un état booléen et Processing doit le traiter visuellement Avant de bidouiller le chrono je passe par un petit code pour juste écouter, traiter et afficher les appuis. \\ **Quelques embûches //(sinon c'est pas drôle)// :** \\ * A partir du moment où Processing écoute la carte Arduino on ne peut plus utiliser les "serial-consoles" des outils. * On a besoin de transformer le signal reçu (comme 1 texte?) en un entier, en passant par un float. 2 variables sont donc nécessaires pour traiter une valeur simple. * On utilise la détection de fin de ligne obtenue avec println * On a besoin d'une variable d'historique pour ne compter l'appui bouton que si il fait changer l'état **Quelques ressources utiles à creuser :** * [[https://www.youtube.com/watch?v=S3HpkjcoqCM|vidéo Youtube en fr]] de PlaisirArduino et [[https://plaisirarduino.fr/processing-et-arduino/|son article qui va avec]] sur le sujet * [[https://learn.sparkfun.com/tutorials/connecting-arduino-to-processing/all| tuto en anglais chez Sparkfun]] * // /* J'ai réussi à lire les données du Serial, maintenant je veux que Processing COMPTE chaque changement de valeur et garde ce compte en mémoire que l'affichage change, par ex faire grossir un cercle... v1 ça fonctionnait mais sans historique le cercle grossit à toute vitesse donc j'ajoute une variable pour historique d'état : je peux compter les clics et le cercle grossit 1 fois par clic (et pas en constant tant que cliqué) */ import processing.serial.*; Serial myPort; // Create object from Serial class String dt; // Data received from the serial port float valD; int btnVal = 0; // une valeur int pour d'etat bouton, 0 relache int oldBtn = 0; // une valeur pour conserver l'état précédent int cptBtn = 1; // une variable pour compter les appuis PFont matypo; // ça c'est parcequ'il aime pas les car du Serial il râle si on précise pas void setup() { size(800, 600); // printArray(Serial.list()); // pour voir le port qui parle String portName = "/dev/ttyACM3";// Changer le nom selon valeur reçue du listing ligne pcdte. myPort = new Serial(this, portName, 9600); // valeur cadence en baud comme dans Arduino // myPort.bufferUntil(lf); // charge de 10 caractères ??? // printArray(PFont.list()); // pour voir les typo dispo sur le poste matypo = createFont("Open Sans", 48); textFont(matypo); } void draw() { while (myPort.available() >0) { dt = myPort.readStringUntil('\n'); if (dt !=null) { // si on lit qqchose on le trasnforme en int avec ces 2 lignes valD = float(dt); btnVal = int(valD); } } background(255); fill(#245682); translate(width/2, height/2); if(btnVal==0 && oldBtn !=0){ // si c relache sur les 2 on ne fait rien mais si ça vient de changer oldBtn = 0; } else if (btnVal ==1 && oldBtn !=1){ cptBtn = cptBtn + 1; oldBtn = 1; } ellipse(0, 0, cptBtn*5, cptBtn*5); fill(#c2c3c4); textSize(48); text(btnVal, 0, 0); textSize(36); text("recoit: " +btnVal, 10, 50); text("nb Clic: " +cptBtn, 10, 80); } ==== étape 3 : mix === Je mélange les 2 codes processing pour un chrono interactif * Ajout d'un compteur millisecondes pour que ça aille plus vite (les heures ne sont plus affichées) * Le bouton physique Arduino arrête le chrono à l'écran et affiche un meilleur temps "score" * Pour relancer le chrono le MJ utilise sa souris de PC * Possibilité de passer en Plein écran plutôt qu'en 800x600px * /* MON PROJET : un compteur de seconde centré dans l'écran + Piloté par Arduino et un bouton ( stop / start) - Utiloser millis() pour créer une horloge hors temps officiel, solution approhée par https://forum.processing.org/one/topic/timer-in-processing.html */ // objet global Chrono, déclaration de classe StopChronoTimer sc; // Communication avec Arduino import processing.serial.*; Serial myPort; // Create object from Serial class String dt; // Data received from the serial port float valD; int btnVal = 0; // une valeur pour l'etat du bouton int oldBtn = 0; // une valeur pour conserver l'état précédent int cptBtn = 1; // une variable pour compter les appuis PFont matypo; // ça c'est parcequ'il aime pas les car du Serial il râle si on précise pas void setup() { // fullScreen(); size(800,600); // printArray(Serial.list()); // utile pour voir le port qui parle String portName = "/dev/ttyACM3";// Changer le nom selon valeur reçue du listing ligne pcdte. myPort = new Serial(this, portName, 9600); // valeur cadence en baud comme dans Arduino sc = new StopChronoTimer(); sc.start(); } void draw() { while (myPort.available() >0) { dt = myPort.readStringUntil('\n'); if (dt !=null) { // si on lit qqchose on le trasnforme en int avec ces 2 lignes valD = float(dt); btnVal = int(valD); } } top(); } /// FONCTONS void top() { // dessine le texte-temps sur l'écran background(#fecb00); fill(000); textSize(208); textAlign(CENTER); // nf est utile pour transformer des chiffres en string text https://processing.org/reference/nf_.html text(nf(sc.minutes(), 2)+":"+nf(sc.secondes(), 2)+":"+nf(sc.millisec(), 2), width/2, height/2); if (btnVal==0 && oldBtn !=0) { // si c relache sur les 2 on ne fait rien mais si ça vient de changer /// } else if (btnVal ==1 && oldBtn !=1) { cptBtn = cptBtn + 1; oldBtn = 1; score(); } } void score() { int[] score = new int[3]; score[0] = sc.minutes(); score[1] = sc.secondes(); score[2] = sc.millisec(); fill(#ff2503); textSize(24); text("MEILLEUR SCORE "+nf( score[0], 2)+":"+nf( score[1], 2)+":"+nf( score[2], 2), width/4, height/2+100); noLoop(); } // Pour relancer un compteur j'utilise la souris c OK // mais il ne faut pas cliquer si le compteur n'est pas déjà arrêté void mouseReleased() { btnVal = 0; oldBtn = 0; sc.start(); loop(); } // CLASSES class StopChronoTimer { int startTime = 0; int stopTime = 0; boolean running = false; void start() { startTime = millis(); running = true; } void stop() { stopTime = millis(); running = false; } /* int[] score(int s, int m, int h){ int s = return secondes(); int m = return minutes(); int h = return heures(); } */ void fin() { stopTime = 0; startTime = 0; running = false; } int gTempsPasse() { int tempsP; if (running) { tempsP = (millis()-startTime); } else { tempsP =(stopTime - startTime); } return tempsP; } int millisec(){ return (gTempsPasse()/10 ) %100; } int secondes() { return (gTempsPasse()/1000) % 60; } int minutes() { return (gTempsPasse()/(1000*60)) % 60; } int heures() { return (gTempsPasse()/(1000*60*60)) % 24; } } ==== étape 4 : le bouton - champi === Je veux un gros bouton mais je n'ai que des petits caps Je vais donc prendre les dimensions de l'existant et modéliser un push button qui va bien... {{ :projets:freecad_btnchampi0.png?nolink |}} Bon évidemment le premier essai n'est pas au top mais mon fichier FreeCAD est enregistré avec les modif : * élargissement du diamètre accueil bouton ( de 11,2 mesuré mais trop serrant à 11,5 mm) * diamètre pied champi fixé à 24 mm Il faudra regénérer un mesh si on veut le réimprimer Mon premier jet est mal adapté, à bricoler pour que le caps bouton rentre dedans. ==== étape 5 : montage et miniaturisation === colonnes