Piloter un moteur via communication série Arduino

Bonjour à tous,

Etant nouveau sur le forum, je commence par une petite présentation :wink:
Je m'appelle Alan, je suis étudiant en 2ème année d'école d'ingénieur à l'Ensmm. Une école d'ingénieur située en Franche-Comté spécialisée dans la mécanique et les micro techniques. Je suis actuellement en stage pour une durée de 6 mois pour un laboratoire d'analyse dans le bio-médical. Dans le cadre d'un projet innovant de micro-tamisage, je travail sur un prototype et afin d'effectuer des tests facilement, je me suis orienté vers une carte Arduino UNO et un simple moteur à CC. Bien-sûr, ces solutions technologiques ne sont pas les solutions finales retenues.

Pour en venir au sujet, j'essaye de programmer un programme capable de faire tourner un moteur à CC dans un sens, puis dans l'autre puis finir par des oscillations autour de l'axe vertical. Un peu le même principe qu'une machine à laver ^^

Cependant, ayant des notions simples en programmation (Python et Java), j'ai réaliser un programme en commandant les sorties PWM avec de simples boucles while(). Voici le programme que j'ai réalisé, qui en soit fonctionne mais n'est pas très claire :

int M1PWM = 6;
int M2PWM = 9;
int a;                  //rapport cyclique entre 0 et 255 (255 => 100% de la tension disponible)
int b;                  //VARIABLE durée de phase accélération/décélération pour vitesses constantes en ? 
int v1;                 //VARIABLE vitesse constante max sens 1 (0-255)
int v2;                 //VARIABLE vitesse constante max sens 2 (0-255)
int v3;                 //VARIABLE vitesse oscillations programme 1 (0-255)
int v4;                 //VARIABLE vitesse oscillations programme 2 (0-255)
int c;                  //VARIABLE temps entre chaque mouvement en ms
int d;                  //VARIABLE temps phase vitesse constante en ms
int e;                  //VARIABLE durée de phase accélération/décélération pour oscillations en ?
int f;                  //VARIABLE nombre d'oscilation programme 1 & 2
int compteur;           //VARIABLE compteur oscillations programme 1
int compteur2;          //VARIABLE compteur oscillations programme 2
int g;                  //VARIABLE temps d'une oscillation en ?
int tosci;              //VARIABLE temps oscillations programme 2 en ?
int tdemiosci;          //temps demi-oscillations programme 2 en ?


void setup() {
  // put your setup code here, to run once:
  pinMode(M2PWM, OUTPUT);
  pinMode(M1PWM, OUTPUT);

}

void loop() 
{  // put your main code here, to run repeatedly:
  
  a=0;                      //rapport cyclique entre 0 et 255 (255 => 100% de la tension disponible)
  b=255;                     //VARIABLE durée de phase accélération/décélération pour vitesses constantes en ? 
  v1=255;                   //VARIABLE vitesse constante max sens 1 (0-255) :  0 correspond à vitesse nulle et 255 coresspond à 100%
  v2=255;                   //VARIABLE vitesse constante max sens 2 (0-255)
  v3=100;                    //VARIABLE vitesse oscillations programme 1 (0-255)
  v4=100;                    //VARIABLE vitesse oscillations programme 2 (0-255)
  c=5;                   //VARIABLE temps entre chaque mouvement en ms
  d=5000;                   //VARIABLE temps phase vitesse constante en ms
  e=25;                     //VARIABLE durée de phase accélération/décélération pour oscillations en ?
  f=20;                    //VARIABLE nombre d'oscilation programme 1 & 2
  compteur=0;               //VARIABLE compteur oscillations programme 1
  compteur2=0;              //VARIABLE compteur oscillations programme 2
  g=100;                    //VARIABLE temps d'une oscillation en ?
  tosci=400;                //VARIABLE temps oscillations programme 2 en ?
  tdemiosci=tosci/2;        //temps demi-oscillations programme 2 en ?


 /// VITESSE CONSTANTE SENS 1
  
  digitalWrite(M2PWM, 0);   // On initialise la sortie moteur M2PWM =0
  while (a <= v1) {                        // PHASE ACCELERATION 1
    analogWrite(M1PWM, a); // Sortie moteur M1PWM =a     0 au début puis 5, 15 etc...
    a=a+5;                 // Vitesse accélération qui correspond à la vitesse d'incrémentation de la boucle while()
    delay(b);              // Temps d'accélération
  }
  a=v1;


  
   digitalWrite(M2PWM,0);      //vitesse constante 1
   analogWrite(M1PWM,v1);
   delay(d);
   
   while (a >=0) { // phase de décélération 1
     analogWrite(M1PWM, a);
     a = a - 5;
     delay(b);
   }
   a=0;
  
   delay(c);

  /// VITESSE CONSTANTE SENS 2 
   a=0;
   digitalWrite(M1PWM, 0);
   while (a <= v2) { // phase d'accélération 2
     analogWrite(M2PWM, a);
     a=a+5;
     delay(b);
   }
   a=v2;
   
   digitalWrite(M1PWM,0); //vitesse constante 2
   analogWrite(M2PWM,v2);
   delay(d);
    
   while (a >=0) { // phase de décélération 2
     analogWrite(M2PWM, a);
     a = a - 5;
     delay(b);
   }
   a=0;

   delay(c);

  /// OSCILLATIONS PROGRAMME 1 
 
   digitalWrite(M1PWM,0);
   digitalWrite(M2PWM,0);

   while (a <=v3) { // phase d'accélération 1
     analogWrite(M1PWM, a);
     delay(e);
     a=a+10;     
   }
   a=v3;
   
   digitalWrite(M2PWM,0); //vitesse constante 1
   analogWrite(M1PWM,v3);
   delay(g);
   
   while (a >=0) { // phase de décélération 1
     analogWrite(M1PWM, a);
     a = a -10;
     delay(e);
   }
   a=0;

   while(compteur<f){
     
    digitalWrite(M1PWM, 0);
    while (a <= v3) { // phase d'accélération 2
       analogWrite(M2PWM, a);
       delay(e);
       a=a+10;
     }
     a=v3;
     digitalWrite(M1PWM,0); //vitesse constante 2
     analogWrite(M2PWM,v3);
     delay(g);
   
     while (a >=0) { // phase de décélération 2
       analogWrite(M2PWM, a);
       a = a - 10;
       delay(e);
     }
     a=0;
     compteur=compteur+1;
    
     digitalWrite(M2PWM, 0);
     while (a <= v3) { // phase d'accélération 1
       analogWrite(M1PWM, a);
       delay(e);
       a=a+10;
     }
     a=v3;
   
     digitalWrite(M2PWM,0); //vitesse constante 1
     analogWrite(M1PWM,v3);
     delay(g);
    
     while (a >=0) { // phase de décélération 1
       analogWrite(M1PWM, a);
       a = a - 10;
       delay(e);
     }
     a=0;
     compteur=compteur+1;
     
   }
   
   while (a <=v3) { // phase d'accélération 2
     analogWrite(M2PWM, a);
     delay(e);
     a=a+10;
   }
   a=v3;
   
   digitalWrite(M1PWM,0); //vitesse constante 2
   analogWrite(M2PWM,v3);
   delay(g);   
  
   while (a >=0) { // phase de décélération 2
     analogWrite(M2PWM, a);
     a = a - 10;
     delay(e);
   }
   a=0;
   
   delay(c);
   


  ///OSCILLATIONS PROGRAMME 2


  digitalWrite(M2PWM,0); //vitesse constante 1
  analogWrite(M1PWM,v4);
  delay(tdemiosci);

  while(compteur2<f){
    digitalWrite(M1PWM,0); //vitesse constante 2
    analogWrite(M2PWM,v4);
    delay(tosci);
    compteur2=compteur2 +1;

    digitalWrite(M2PWM,0); //vitesse constante 1
    analogWrite(M1PWM,v4);
    delay(tosci);
    compteur2=compteur2+1;
  }

  digitalWrite(M1PWM,0);
  analogWrite(M2PWM,v4);
  delay(tdemiosci);
  analogWrite(M2PWM,0);

  while(1){
    
  }
}

Comme vous pouvez le voir, j'ai fais 2 programmes différents. Le premier programme oscillation 1, je contrôle l'accélération/décélération avec des boucles while(). Le deuxième programme oscillation 2, je ne fais que tourner le moteur dans un sens pendant un certains temps "tosci" puis dans l'autre sens.

Or, quand j'ai présenté ceci à mes tuteurs, ils m'ont dit que c'était beaucoup trop simpliste et que ça ne permettait pas un pilotage externe via la communication série. Si j'ai bien compris, ils voudraient que je pilote la carte Arduino via la comm série et piloter le moteur via un profil de vitesse. En soit, ils voudraient que je rentre une accélération et que la vitesse désirée soit calculé et appliqué au moteur.

Ils m'ont alors proposé un bout de programme que je dois compléter :

J'ai beau me documenter, lire des topics, faire des exercices, je n'y comprend pas grand chose à leur programme. J'utilise ce pdf ( http://www.insyte.website/Arduino/ArduinoEskimonComplet.pdf ) et ce site ( http://eskimon.fr/ ) pour apprendre mais j'ai du mal à faire le lien. Si vous pouvez m'aider à éclaircir ce programme et que je puisse compléter les parties manquantes.

Alan

#include <MsTimer2.h>

String Reception;

unsigned long Delta=10;

float VitesseEnCours=0;
float VitesseDesiree=0;
float AccelMax=0;


typedef struct Oscillation{
	int TSens1; // en s
	float VitesseSens1; // en tr/min
	float AccelSens1; // en tr/min²
	int TSens2; // en s
	float VitesseSens2; // en tr/min
	float AccelSens2;  // en tr/min²
	
	int NbRepetition;
}Oscillation;

Oscillation Sequence[50];
int TailleSequence=0;
int OscillationEnCours=-1;

bool Pause=true;

// Renvoi du signe d'un float
int Sign(float f)
  {
    if (f>0)
    return 1;
   else if (f<0)
     return -1;
   else  if (f==0)
     return 0;
  }


// -------------------------------------- Génération profil de vitesse ---------------------------------------
void CalculVitesse()   // Dès qu'une vitesse est demandée, ce CALLBACK sur interruption la calcule suivant l'accélération demandée
  {
  cli();  
 
   if (fabs(VitesseEnCours-VitesseDesiree)>AccelMax*Delta*0.001)
        VitesseEnCours=VitesseEnCours+AccelMax*Delta*0.001*Sign(VitesseDesiree-VitesseEnCours); // 0.001 pour passer en s
  else
        VitesseEnCours=VitesseDesiree;   


  // Passer de VitesseEnCours qui est un float en sortie PWM et sens moteur

  sei();
  }

//------------------------- Traitement demande sur port série----------------------------------------------------
void Traitement(String Demande)  
{
  
  if (Demande=="VitesseActuelle")
    {
      Serial.println(VitesseEnCours);
    }
   else if (Demande=="Start")  // Met en route ou remet en route après pause
    {
    Pause=false;
   
    Serial.println("OK");
    }	
   else if (Demande=="Stop")  // Arrète tout et réinit le compteur
    {
    Pause=true;
    OscillationEnCours=-1;
  
    Serial.println("OK");
    }	
   else if (Demande=="Pause") // Met en pause
    {
    Pause=true;
  
    Serial.println("OK");
    }
   else if (Demande=="ViderSequence") // Vide la séquence (en fait remet le nb à 0
    {
    Pause=true;
    TailleSequence=0;
  
    Serial.println("OK");
    } 		
  else if (Demande=="Autre")
    {
    
   // Pour l'instant rien
    Serial.println("OK");
    }
  else
    {
    int commaIndex = Demande.indexOf(',');
    if (commaIndex>=0)
      {
        String Action = Demande.substring(0, commaIndex);
       
        if (Action=="SetVitesseAccel")
           {
      
           VitesseDesiree=Demande.substring(commaIndex+1, commaIndex+1-Demande.length()).toFloat();

           // AccelMax=? à faire
           Serial.println("OK");
           }
        else if (Action=="AjouterOscillation")  // A faire 7 paramètres à faire passer ex: "AjouterOscillation,10,50,25,50,-5,25,40"
          {

          //Sequence[TailleSequence].TSens1=...  // Voir exemple au dessus


          TailleSequence++;
          Serial.println("OK");  
          }
       
      }
    else
      Serial.println("NOK");

    
    }
}


// -------------------------------- SETUP -----------------------------------
void setup() {


MsTimer2::set(Delta, CalculVitesse); 
MsTimer2::start(); // Lance CalculVitesse toute les Delta ms
  
  
Serial.begin(115200); // initialise connexion série à 115200 bauds

while (Serial.available())
  {
    char data=(char)Serial.read();
    Reception+=data;
    delay(1);
  }
Reception="";
}


// ------------------------------ LOOP ---------------------------------------

void loop() {


 if (!Pause)
  {
    
 // Gestion des mouvements

 // Gérer les temps d'A/R et leur nb et faire progresser OscillationEnCours

  }
  // Gestion comm RS232
 while (Serial.available())
  {
    char data=(char)Serial.read();
    Reception+=data;
    delay(5);
  }

 if (Reception.length()>0)
  {
   Traitement(Reception);
   
   Reception="";
  }

}

Bonjour et Bienvenue

Prends connaissance de la Règle du Forum et en premier lieu la règle n°1

Tu as choisi le sous-forum dédié au dépot de projets finis.... c'est un peu töt !

Demandes au Modérateur le déplacement du fil au bon endroit (lien Report to Moderator en bas à gauche)
Ici le message encombre la 'vitrine des projets finis'. De plus il n'a qu'une modeste visibilité, il ya peu de passage en ce lieu.

A+

Autant pour moi, j'avais mal interprété le sous forum " Réalisations et projets finis. Je viens d'envoyer un message au modérateur.

Merci de votre réponse,

Alan

Ce code dans le Setup qui a pour objectif j'imagine de vider le buffer série en entrée

while (Serial.available())
  {
    char data=(char)Serial.read();
    Reception+=data;
    delay(1);
  }
Reception="";

n'a pas besoin d'accumuler dans la String Réception ce qu'il lit puisque vous le virez juste après.

  • le delay(1); est une hérésie quand on travaille avec un protocole asynchrone.. faut pas essayer de deviner le temps qu'il faut attendre --> vous pouvez dire à vos tuteurs que leur gestion du port série n'est pas robuste :frowning: elle ne garantit pas la réception d'une commande complète...

  • Ici on vous engagera aussi à ne pas utiliser la classe String sur un UNO

Sinon vous avez regardé le langage de commande qu'ils proposent ? comment calculer les phases moteurs en fonction de la commande ?

Bonjour,

Donc si je comprend bien, j'aurais pu juste mettre ?

while (Serial.available())
  {
    char data=(char)Serial.read();
    delay(1);
  }

Sachant que je débute sur Arduino, je ne peux pas dire à mes tuteurs que leur gestion du port série n'est pas robuste ^^'

Pourquoi ne pas utiliser String pour une UNO ?

Je sais pas si j'ai bien compris votre question mais pour le langage de commande, je pensais le faire soit en Python, soit en Java avec NetBeans.

Si vous voulez comprendre comment bien écouter le port série (ou gérer un flux asynchrone genre keypad) vous pouvez jeter un oeil à mon petit tuto sur le sujet

  • Ici on vous engagera aussi à ne pas utiliser la classe String sur un UNO

Bonjour, Monsieur JML; la question qui me vient à l'esprit est "pourquoi?" (je sais qu'elle est un petit peu hors sujet, mais ce qui est ironique, c'est que j'envisageais de retirer mes constructions basées sur C pour des Strings, par attrait de la nouveauté)
PS -post scroptum, bien sûr : j'ai lu le site mis en PJ de votre tutoriel, et j'ao évité une grosse erreur...

je parle du pourquoi éviter la classe String dans mon tuto en effet

Très bien, je vais donc lire votre tuto cet après midi JML.

Après avoir remercié JML, je reviens au code passé par les tuteurs:
mettre des commandes longues du genre ""VitesseActuelle" est assez cruel (pourquoi pas "VitesseActuelleParLaGraceDuTresVenereRoiAndriamPoImerina", qui saturera le buffer circulaire d'entrée de Serial -32 chars, IIRC-), mange de la place et des cycles CPU :

a) toute personne normale fait des fôtes de frappe: lors de la recherche de panne, avec manip au terminal série, il y a de quoi tourner en bourrique

b) un automate sur PC (python; processing sous java) que vous serez amené à pondre pour vous simplifier la tâche est une fausse bonne idée, car il nécessitera -au minimum - une cohérence entre ce que vous émettez et ce qui est attendu; à la moindre faute de frappe, vous aurez deux programmes à débugguer, et trouver lequel il faut debugguer: c'est une façon déterministe de tourner en bourrique.

Dans tous les cas de figure, vous allez être très ennuyé... et vous maudirez la cruauté de vos tuteurs...

Je vous conseille de faire des commandes d'une lettre, ignorant les majuscules et décodées par un case... switch (a moins qu'il ne faille plus de 26 commandes; seul stop doit être remplacé par H(alt) ou S(tart) par D(émarre)...) le code généré sous arduino sera plus court (et vous pourrez afficher un mémento au démarrage / en cas d'erreur sur le terminal série) .

Si vous décodez au fil de l'eau, par une machine à états, vous n'aurez même pas besoin de buffer.... autre que celui fourni par Arduino pour la reception (et qui vous est caché) -mais là, gérer des corrections est abominable (touche del), disons qu'avec des commandes simplifiées, vous pouvez échapper à des fautes de frap -

Cela s'appelle un interpréteur "un doigt" :slight_smile:
Du genre de ceux que l'on utilise en mise au point pour envoyer des commandes de test depuis un PC quand on n'a qu'une seule main, car l'autre tient une sonde ... ou un sandwich :grin:

Merci pour vos réponses mais je suis toujours bloqué avec mon programme. Je ne sais malheureusement pas quoi faire... Je comprends certaines choses comme la fonction loop() où j'ai mis des commentaires sur le coté

void loop() {


 if (!Pause)
  {
    
    
    
 // Gestion des mouvements

 // Gérer les temps d'A/R et leur nb et faire progresser OscillationEnCours

  }
  // Gestion comm RS232
 while (Serial.available())               
  {
    char data=(char)Serial.read();        // data prend les caractères qu'on rentre dans le moniteur série 
    Reception+=data;                      // Reception = Reception + data   ===> Reception est la somme de tout les caractères ds le moniteur série
    delay(5);
  }

 if (Reception.length()>0)                // Si Reception n'est pas vide, 
  {
   Traitement(Reception);                 // Alors on applique la fonction Traitement(Reception)
   
   Reception="";
  }

}

Lorsque l'on rentre des caractères dans le moniteur, on appelle la fonction Traitement(Reception). Suivant ce que l'on rentre dans le moniteur, la carte nous renvoie différentes réponses (ex : Stop => la carte nous renvoie "OK" )

Cependant, je n'arrive pas du tout à relier le programme de mon tuteur à une application réelle pour faire tourner mon moteur.

Tenez j’ai fait un petit nettoyage de printemps pour se débarrasser de la classe String et pour gérer le port série correctement

#include <MsTimer2.h>

// Gestion du port série et des commandes
const byte tailleMessageMax = 80;

char message[tailleMessageMax + 1]; // +1 car on doit avoir un caractère de fin de chaîne en C, le '\0'
const char marqueurDeFin = '\n'; // une commande se termine par un New Line

unsigned long Delta = 10;
float VitesseEnCours = 0;
float VitesseDesiree = 0;
float AccelMax = 0;

struct Oscillation_t {
  unsigned int TSens1;  // en s
  float VitesseSens1;   // en tr/min
  float AccelSens1;     // en tr/min²
  unsigned int TSens2;  // en s
  float VitesseSens2;   // en tr/min
  float AccelSens2;     // en tr/min²
  int NbRepetition;
};

Oscillation_t Sequence[50];
int TailleSequence = 0;
int OscillationEnCours = -1;

bool systemeEnPause = true;

// Renvoi du signe d'un float
int Sign(float f)
{
  if (f > 0) return 1;
  else if (f < 0) return -1;
  return 0;
}

// -------------------------------------- Gestion "correcte" du port série ---------------------------------------
// ecouterCommande retourne vrai si un nouveau message est disponible

boolean ecouterCommande()
{
  static byte indexMessage = 0; // static pour se souvenir de cette variable entre 2 appels consécutifs. initialisée qu'une seule fois.
  boolean messageEnCours = true; // tant qu'on a pas reçu le caractère 'marqueurDeFin' on n'a pas reçu un message complet

  while (Serial.available() && messageEnCours) {
    int c = Serial.read();
    if (c != -1) {
      switch (c) {
        case '\r': break; // on ignore le '\r'
        case marqueurDeFin:
          message[indexMessage] = '\0'; // on termine la c-string
          indexMessage = 0; // on se remet au début pour la prochaine fois
          messageEnCours = false;
          break;
        default:
          if (indexMessage <= tailleMessageMax - 1) message[indexMessage++] = (char) c; // on stocke le caractère et on passe à la case suivante
          break;
      }
    }
  }
  return !messageEnCours;
}

// -------------------------------------- Génération profil de vitesse ---------------------------------------
void CalculVitesse()   // Dès qu'une vitesse est demandée, ce CALLBACK sur interruption la calcule suivant l'accélération demandée
{
  cli();

  if (fabs(VitesseEnCours - VitesseDesiree) > AccelMax * Delta * 0.001)
    VitesseEnCours = VitesseEnCours + AccelMax * Delta * 0.001 * Sign(VitesseDesiree - VitesseEnCours); // 0.001 pour passer en s
  else
    VitesseEnCours = VitesseDesiree;


  // Passer de VitesseEnCours qui est un float en sortie PWM et sens moteur

  sei();
}

//------------------------- Traitement demande sur port série----------------------------------------------------
void Traitement(char * demande)
{

  if (!strcmp(demande, "VitesseActuelle")) { // https://en.cppreference.com/w/cpp/string/byte/strcmp
    Serial.print(F("Vitesse Actuelle="));
    Serial.println(VitesseEnCours);
    Serial.println(F("OK"));
  }
  else if (!strcmp(demande, "Start")) { // Met en route ou remet en route après Pause
    systemeEnPause = false;
    Serial.println(F("OK"));
  }
  else  if (!strcmp(demande, "Stop")) { // Arrète tout et réinit le compteur
    systemeEnPause = true;
    OscillationEnCours = -1;
    Serial.println(F("OK"));
  }
  else  if (!strcmp(demande, "Pause")) { // Met en Pause
    systemeEnPause = true;
    Serial.println(F("OK"));
  }
  else  if (!strcmp(demande, "ViderSequence")) {// Vide la séquence (en fait remet le nb à 0)
    systemeEnPause = true;
    TailleSequence = 0;
    Serial.println(F("OK"));
  }
  else  if (!strcmp(demande, "Autre")) {
    // Pour l'instant rien
    Serial.println(F("OK"));
  } else {
    // on a une commande complexe avec paramètres Nom_de_commande,p1,p2,p3....
    // on commence par chercher la première virgule
    char * commaIndex = strchr(demande, ','); // https://en.cppreference.com/w/cpp/string/byte/strchr

    // si on a trouvé la virgule le pointeur dans la c-String est non nul, alors on cherche quelle commande on a reçu
    if (commaIndex != NULL) {
      size_t nbCarDansCommande = commaIndex - demande; // calcule combien de caractères à lire pour comparer la commande reçue aux ordres connus

     // commande: "SetVitesseAccel, vitesse,acceleration\n" exemple SetVitesseAccel,120,10 
      if (!strncmp(demande, "SetVitesseAccel", nbCarDansCommande)) { // https://en.cppreference.com/w/cpp/string/byte/strncmp
        VitesseDesiree = atof(commaIndex + 1); // https://en.cppreference.com/w/cpp/string/byte/atof
        // AccelMax=? à faire (envisager d'utiliser strtok https://en.cppreference.com/w/cpp/string/byte/strtok)
        Serial.println(VitesseDesiree); // exemple pour montrer qu'on a bien lu la valeur 
        Serial.println(F("OK"));
      }
      else if (!strncmp(demande, "AjouterOscillation", nbCarDansCommande)) {
        // A faire 7 paramètres à faire passer ex: "AjouterOscillation,10,50,25,50,-5,25,40"
        //Sequence[TailleSequence].TSens1=...  // Voir exemple au dessus (envisager d'utiliser strtok https://en.cppreference.com/w/cpp/string/byte/strtok)
        TailleSequence++;
        Serial.println(F("OK"));
      }
    } else {
      Serial.print(F("Commande: ["));
      Serial.print(demande);
      Serial.println(F("] --> NOK"));
    }
  }
}


// -------------------------------- SETUP -----------------------------------
void setup()
{
  Serial.begin(115200); // initialise connexion série à 115200 bauds
  MsTimer2::set(Delta, CalculVitesse);
  MsTimer2::start(); // Lance CalculVitesse toute les Delta ms
}


// ------------------------------ LOOP ---------------------------------------

void loop() {
  // Gestion comm RS232
  if (ecouterCommande()) Traitement(message);

  // gestion courante
  if (!systemeEnPause) {

    // Gestion des mouvements
    // Gérer les temps d'A/R et leur nb et faire progresser OscillationEnCours

  }
}

Ca utilise le mécanisme de mon tuto pour le port série
J’ai mis les références dans le code sur les fonctions de comparaison de cString. A vous de finir

PS/ tapé sur mon smartphone donc non testé et il se peut qu’il y ait des erreurs de frappe

Avez vous essayé de jouer avec ce programme? Compile -t-il? Que fait il quand on tape dans la console "Start" (sans les "), puis "Stop" ? Est ce que c'est agréable de taper tout ça?

A mon avis, il faut ... essayer de le réécrire.

Sans être un expert (j'ai découvert avec horreur pourquoi éviter les String s, qui me semblaient sexys), je vois quelques maladresses ex:

// Renvoi du signe d'un float
int Sign(float f) // on perd un octet sur un petit 8 bits
  {
    if (f>0)
    return 1;
   else if (f<0)
     return -1;
   else  if (f==0)
     return 0;
  }

se réecrit (plus concis; sur un petit écran, c'est apprécié; utilise le bon nombre d'octets => plus rapide)

int8_t Sign (float f) { // Renvoi du signe d'un float
 if (f > 0.0) {return (1);} 
 if (f < 0.0) {return(-1) ;}
return 0;}

Ca, c'est bénin ;

les Strings peuvent "vous" coller une fuite mémoire (directement ou lors de futures évolutions). JLM a l'air de l'avoir réglé....

on ne voit pas si Delta est une constante, ou doit faire l'objet de modifs ulterieures (ici, il ne change pas et on ne sait pas à quoi il sert : dire que ça fixe la cadence d'invocation d'une fonction -100 fois par secondes- aurait été gentil... J'en ferais une constante et je la commenterais).

Ce programme appelle une bibliotheque MStimer2, qui a l'air d"être bien écrite (appelle une fonction toutes les xxx millisecondes; dans votre cas xxx vaut Delta, 10) et assez bien décrite dans
http://www.mon-club-elec.fr/pmwiki_mon_club_elec/pmwiki.php?n=MAIN.ArduinoInitiationInterruptionsTemporisationTimer2UneSeconde

-le seul bémol est que delay ne bloque pas les interruptions, (officiellement : c'est sa seule qualité) ... (mais c'est la meilleure doc en français que j'aie trouvée) ...

dbrion06:
Avez vous essayé de jouer avec ce programme? Compile -t-il? Que fait il quand on tape dans la console "Start" (sans les "), puis "Stop" ? Est ce que c'est agréable de taper tout ça?
A mon avis, il faut ... essayer de le réécrire.

J'ai conservé le langage de commande car c'est assez simple à changer plus tard, suffit de modifier les commandes et c'est ce que son tuteur propose, peut-être qu'il y a une autre machine qui envoie ces commandes et ce n'est pas un humain

dbrion06:
Sans être un expert je vois quelques maladresses ex:

// Renvoi du signe d'un float

int Sign(float f) // on perd un octet sur un petit 8 bits
  {
    if (f>0)
    return 1;
  else if (f<0)
    return -1;
  else  if (f==0)
    return 0;
  }




se réecrit (plus concis; sur un petit écran, c'est apprécié; utilise le bon nombre d'octets => plus rapide)


int8_t Sign (float f) { // Renvoi du signe d'un float
if (f > 0.0) {return (1);}
if (f < 0.0) {return(-1) ;}
return 0;}

en fait c'est pas trop grave car l'octet en question est sur la pile et consommé immédiatement dans une opération de calcul. Si vous retournez un ou deux octets, il sera de toutes façon convertit en 4 octets dans la formule de calcul qui comporte des nombre décimaux (float)VitesseEnCours = VitesseEnCours + AccelMax * Delta * 0.001 * Sign(VitesseDesiree-VitesseEnCours);il se peut même que l’optimiseur vire totalement la fonction pour la mettre in-line (et vaut mieux garder les else même si on fait return pour que le compilo comprenne la structure et optimise au mieux)

Pour la fonction CalculVitesse() qui est appelée par le timer pour la mise à jour des paramètres moteur, son tuteur a pris soin de mettre cela en section critique. A mon avis ça ne sert à rien puisque le callback (CalculVitesse()) est appelé dans le contexte de l'ISR du timer et qu'une interruption ne peut pas être interrompu de toutes façon par défaut, donc le callback est déjà en section critique. Ce qui serait plus important ce serait de déclarer ces variables volatile

bref, il y aurait bcp à dire... mais pour le moment il faut que @Alan39 comprenne ce qu'il doit faire dans les fonctions d'analyse de l'entrée série ainsi que dans le call back...

Je ne serais pas trop étonné que gcc mette inline cette fonction; j'ai préféré la rendre un peu plus concise (pour les yeux), au detriment de certaines normes (qui imposent un return par fonction; je ne sais pas pourquoi ) et de l'original...Et les optimiseurs optimisent de plus en plus depuis le temps (je ne sais pas si on peut fixer des options d'optimisation (ex : "-g O3", Arduino optimisant en O2 IIRC) et voir du code généré sous l'architecture Arduino ...) .

Est ce que Alan39 sait qu'une fonction peut accepter comme argument l'adresse d'une autre fonction ? (je n'ai jamais su traduire callback).
Tel que vous avez écrit, vous avez un automate à 3 états (init, stockage, prêt à être utilisé) qui accepte des caractères et les met dans un buffer jusqu'à la NL/CR; le buffer serait réduit si on avait des commandes de 1 caractère (et la structure que vous donnez avec le case... switch est suffisamment parfaite pour évoluer un peu vers un automate plus complexe et pourrait être rendue agnostique à la casse sans effort). Cela aurait deux avantages :
a) lors du debuggage, c'est affreux de taper de si longues commandes (unix se limitait à 2 cars);
b) lors de la fabrication éventuelle d'un automate sur PC, la correspondance entre l'arduino et le PC serait plus facile, moins ensible aux lois de Murphy
c) on gagne (quelques|une ) dizaine(s) de bytes

Je n'avais pas vu que VitesseEnCours doit être volatile (main , loop et setup ne la modifient pas directement) : l'optimiseur a toutes les chances de retirer cette variable...); un lien vers https://www.avrfreaks.net/forum/tutcoptimization-and-importance-volatile-gcc (peut être un peu rebarbatif, mais orienté avr ) ou volatile (computer programming) - Wikipedia pourrait être utile .... pour éviter des heures de debugging (je sais detecter des variables à rendre ineffacçables dans des interruptions; le fait que la routine d''interruption appelle une fonction m'avait échappé)

au detriment de certaines normes (qui imposent un return par fonction; je ne sais pas pourquoi ) et de l'original...

l’original n’est pas un modèle, le compilateur émet un warning car il n’arrive pas à savoir que les if else traitent tous les cas possible (l’optimismeur pas passer ensuite pour virer des trucs). Il se dit donc que si pour une raison quelconque aucune condition n’est remplie on arrive en bas de la fonction et on retourne rien ce qui est contraire à la signature, donc oui c’est bien de laisser après le premier if-elle un return pour « tous » les autres cas, le compilo sera content.

Pour la partie pilotage et usage de volatile faudrait rentrer dans le détail car c’est plus compliqué que ça... quand vous utilisez des variables de plus d’un octet dans un contexte d’interruptions, vous êtes en fait pas sûr que vos maths se feront de manière atomique et un octet sur les 4 d’un float par exemple peut changer en même temps que vous faites le calcul si l’interruption arrive au mauvais moment. Il faudrait donc dupliquer en section critique les variables de travail et faire le calcul avec les copies que l’interruption ne pourra pas changer

Bonjour,

J'ai donc repris votre code :

int8_t Sign (float f) { // Renvoi du signe d'un float
 if (f > 0.0) {return (1);} 
 if (f < 0.0) {return(-1) ;}
return 0;}

car si j'ai bien compris, la façon dont il a programmé n'est pas optimale car pas assez rapide et ça n'utilise pas le bon nombre d'octets.

Cependant, je vais être honnête avec vous, je suis perdu et je comprend à peine l'aide que vous m'apportez. A vrai dire, je pense mettre fin à ce sujet car à part vous faire perdre votre temps, je ne comprend pas la moitié de ce que vous m'expliquez :-[

Je suis vraiment un novice d'Arduino et vous rentrez dans des détails que je ne connais absolument pas.

En tout cas, merci à ceux qui ont prit le temps de répondre à mes questions.

Alan

Avez vous essayé de compiler le programme de JML tel quel (il m'a convaincu que son écriture est la meilleure; son automate a de fortes chances d'être très satisfaisant.
Avez vous tapé "Start""Stop" "nimportequoi" -sanss guillemets) dans la console série?
Qu'est ce que ça donne?

On s'est efforcé de donner tout ce qu'il faut pour comprendre pourquoi la variable "vitesseencours" pose problème(s)
Le compilateur d'Arduino est muni d'un optimiseur, excellent et furieusement efficace dans 99% des cas, voire, dans le cas restant, un peu trop zélé:
si le fil d'execution principal utilise une variable qui n'est mise à jour qu'une seule fois, le brave optimiseur en fera ... une constante. C'est le cas de votre programme; la variable Vitessenecours est mise à jour ... dans le fil d'execution temps réel (et pas leprincipal). Pour éviter tout risque de bizarreie et d'evaporation de ladite variable, il est conseillé de la déclarer "volatile"...

MAis ce n'est pas tout (JLM a parlé de section critique, ou, dans le jargon AVR, d'ATOMIC_BLOC (ces mots cles google me ramènent à avr-libc: <util/atomic.h> Atomically and Non-Atomically Executed Code Blocks, qui est très bien)
On peut se poser la question
"que se passe-t-il si, lors d'un afficahge ou d'un calcul portant sur la variable Vitesseencours, cette opération est interrompue (elle l'est toutes les 10 ms par le fil temps réel) "?
La réponse horrifiante est : le résultat sera affreux (mais le fil temps réel continuera a bien fonctionner, dans l'état du code)
La solution est de rendre interruptible les opérations du fil principal (pas temps réel) portant sur vitesseencours, pendant la période la plus courte possible; les copies dans une variable supplémentaires (ou depuis) sont les opérations les plus rapides, donc il faut faire quelque chose comme

nointerrupt();
copie=vitesseencours;
interrupt();

ou

 ATOMIC_BLOCK(ATOMIC_RESTORESTATE ) {
copie=vitesseencours;
}

A mon avis, vous devriez demander à votre tuteur des explications plus détaillées : ceci serait utile à vous et à vos successeurs éventuels, sur des sujets semblables (ces notions évoquées ci dessus à la va-vite ne s'inventent pas, et devraient être enseignées pour éviter que je casse mon clavier). Les commentaires de ce forum et les liens me semblent utiles pour étayer cette demande d'infos supplémentaires (el les codes de JML sont, pour mes yeux , parfaits...
Et prendre le temps de les assimiler (ou demander à vos tuteurs de vous les faire assimiler, ainsi qu'aux générations futures)