Messages récents

Pages: 1 2 3 [4] 5 6 ... 10
31
Débuter / Re : Va et vient avec gare centrale voie unique
« Dernier message par jeff22150 le décembre 10, 2019, 06:03:53 am »
Bonjour.
Merci à msport et dominique.
Si j'ai compris,
- je n'ai pas besoin de l'alimentation 5V : je supprime cette liaison rouge.
- je déclare le pin13 avec pinMonde(13,INPUT_PULLUP)

Ca donne donc le schéma joint (j'aime bien le visuel)

Pour le fait de tout griller j'en suis bien conscient mais mon premier max471 ayant grillé, il me faut attendre la livraison d'un autre.

cordialement
32
Vos projets / Re : Wagon de mesure distance et vitesse (suite 6)
« Dernier message par Conchito le décembre 10, 2019, 01:22:23 am »
Application dédiée en Processing :
Il existe un outil de développement d’applications graphiques : « Processing ». La version Android s’installe facilement sur PC et permet de créer une application dédiée au wagon.

Fonctionnalités de l’application
L’application doit :
•   recevoir les trames Bluetooth
•   afficher éventuellement les trames reçues pour un diagnostic plus facile
•   afficher la distance et la vitesse
•   afficher les unités de mesure de la distance et de la vitesse
•   proposer des boutons logiciels pour les commandes à envoyer au wagon
•   envoyer les commandes au wagon.
•   s’afficher avec une icône significative dans le tableau des applications Android.

Architecture de l’application :
Le logiciel de l’application que j’ai développée est divisé en trois fichiers :
•   Wagon.pde   fichier principal
•   BT_android.pde   fichier qui contient les procédures liées au Bluetooth
•   Boutons.pde   fichier contenant les routines de gestion des boutons

Wagon.pde
On trouve dans ce fichier les déclarations de variables globales propres à l’affichage sur l’écran du périphérique Android.
Vient ensuite la routine settings() qui déclare les dimensions de l’écran.
La routine setup() charge les diverses images nécessaires pour l’application, les polices de caractères requises et appelle les routines d’initialisation de la connexion Bluetooth et d’initialisation de la gestion des boutons logiciels
La routine principale draw() est exécutée de façon répétitive. Après s’être assuré de la connexion du Bluetooth, son rôle consiste à placer et dessiner toutes les entités graphiques sur l’écran, en fonction des variables globales gérées par les routines Bluetooth ou les routines de gestion des boutons logiciels.
Après la mise en place de la couleur de fond, du titre et de l’icône de l’application, l’image du cadran du compteur de vitesse est dessinée sur l’écran, puis on lui superpose :
•   un masque noir rectangulaire pour préparer le fond de l’afficheur 7 segments simulé.
•   une image sombre de tous les segments pour simuler les segments éteints.
•   le texte correspondant à la valeur de la vitesse avec la police 7 segments en jaune vif
•   l’image de l’aiguille tournée d’un angle proportionnel à la vitesse. L’image de l’aiguille utilisée dépend du signe de la vitesse (aiguille verte si vitesse vers l’avant ou positive, aiguille rouge en cas contraire).
•   la chaine de caractères correspondant à la trame reçue.
La routine draw() appelle alors la routine draw_boutons() du fichier ‘Boutons.pde’ qui dessine les boutons logiciels et gère les actions sur ces boutons.

BT_android.pde
Ce fichier fait appel à la librairie Ketai Bluetooth. Il contient :
•   une série de déclarations de variables globales, ainsi que la déclaration globale des objets :
       -   Bluetooth bt qui permet de gérer la connexion Bluetooth.
       -   Ketailist klist qui gère la liste des appareils Bluetooth appairés et permet de sélectionner la connexion au module Bluetooth HC06 du wagon.
•   des routines de CallBack en fonction des évènements déclenchés par l’application, ou la bibliothèque Bluetooth Ketai.
•   la routine setup_BT() qui initialise l’objet bt et le flag isConfiguring indiquant que le Bluetooth est en cours d’initialisation et de configuration.
•   la routine draw_BT() qui crée la liste des appareils appairés et qui, via le CallBack onKetaiListSelection(), déclenche la sélection et la connexion au périphérique choisi (de préférence le module HC06 du wagon). Cette routine ne sera appelée qu’au lancement de l’application grâce au mécanisme du flag isConfiguring qui est remis à zéro, et donc le draw() principal ne rappellera plus cette routine ultérieurement.
•   la routine onBluetoothDataEvent(String who, byte[] data) appelée par la bibliothèque Ketai lorsque le buffer de réception Bluetooth n’est pas vide. Cette routine vérifie le contenu du buffer, et lorsqu’il contient une trame complète,(présence d’un NewLine  ‘\n’), elle vérifie la validité de trame, l’analyse et extrait les entiers distance et vitesse ainsi que les chaines de caractères des unité Ud et Uv, pour affichage par le draw() principal.
•   la routine envoi_octet(byte x) dont le rôle est d’envoyer l’octet x vers le périphérique connecté.

Boutons.pde
Ce fichier assure la gestion de l’affichage des 3 boutons logiciels et la gestion des actions correspondantes. Il contient :
•   les déclarations globales nécessaires telles qu’emplacements, images, tailles et états des boutons.
•   la routine setup_boutons() dont le rôle est de charger les images des boutons.
•   La routine draw_boutons() qui gère les boutons logiciels en deux étapes :
        -   si l’utilisateur appuie sur l’écran (mousePressed), la routine recherche quel est le bouton concerné, puis positionne les flags correspondants et envoie le caractère adéquat, sinon on se contente de laisser les boutons dans leur état actuel.
       -   en fonction des flags positionnés précédemment, la routine  sélectionne et affiche l’image de chacun des trois boutons.
Quand un bouton logiciel est activé, on se contente de dessiner à sa place un carré gris pour simuler l’état appuyé, à l’exception du bouton d’éclairage des feux de fin de convoi qui affiche 2 images différentes suivant l’allumage ou l’extinction des feux de fin de convoi.

Configuration pour Android :
Le répertoire de développement de l’application se présente alors sous la forme suivante :───Wagon
    │   AndroidManifest.xml
    │   Boutons.pde
    │   BT_android.pde
    │   icon-144.png
    │   icon-192.png
    │   icon-32.png
    │   icon-48.png
    │   icon-72.png
    │   icon-96.png
    │   Wagon.pde
    │
    ├───code
    │       sketch.properties
    │
    └───data
            Aiguille_rouge.png
            Aiguille_verte.png
            arialbd.ttf
            BlueCycle.png
            Cadran.png
            DSEG7Classic-BoldItalic.ttf
            GreenBulbButton.png
            RedBulbButton.png
            Wagon.png
            YellowButton.png
Pour pouvoir le mettre dans le post, le répertoire du logiciel a été éclaté en deux archives.
Pour que l’application soit accessible sur le smartphone par une icone différente de l’icone par défaut, on lui attribue une icône qui sera affichée par le système Android dans la page des applications.
Pour cela, une image est créée et dupliquée dans plusieurs fichiers de type .png avec des résolutions différentes. : icon_32x32.png, icon_48x48.png, icon_72x72.png, icon_96x96.png, icon_144x144.png et icon_192x192.png. Un éditeur graphique permet de préparer ces fichiers facilement à partir d’une image de plus grande résolution. Ces fichiers seront placés dans le même répertoire que les fichiers source.
Les fichiers contenant les images nécessaires à l’application seront placés dans le sous-répertoire data.
Le sous-répertoire code ne contient qu’un seul fichier définissant des paramètres de compilation de l’application.

Pour ma part, j’ai opté pour un compteur de vitesse affichant, avec une aiguille, la vitesse et, à l’aide d’un affichage de style « 7 segments », la distance parcourue. L’aiguille change de couleur en fonction du sens de déplacement du wagon. Le cadran comporte l’indication des unités utilisées. Les trames reçues s’affichent en bas de l’écran et les boutons de commande sont répartis autour du cadran.
Sur la capture d’écran jointe, l’aiguille est verte donc marche avant, le wagon a parcouru 224mm et se déplace à une vitesse équivalente de 37km/h.
L’icône rouge indique que les feux de fin de convoi sont allumés, le bouton jaune sert à la remise à zéro de la distance et le bouton bleu permet de changer les unités pour l’affichage. En bas de l’écran on retrouve la trame transmise par le module Bluetooth du wagon.

Mise au point :
Pour la mise au point des routines n’utilisant pas le Bluetooth (position des images, rotations, boutons …) il est possible d'utiliser Processing dans la version pour PC en utilisant la liaison USB série pour valider le décodage des trames et l’envoi de commandes.
Pour valider les graphiques sur smartphone deux solutions :
•   utiliser l’émulateur Android sur PC.
        Cette solution est un peu lourde et ne reflète pas forcément ce qui se passera exactement sur votre smartphone.
•   utiliser un smartphone connecté par une connexion USB.
        Après compilation, Processing télécharge l’application dans le smartphone et démarre son exécution. Il est alors possible de voir l’effet final et d’apporter les corrections de positionnement des éléments graphiques ou autres.

Extensions possibles :
Le projet peut surement être amélioré. Il serait possible de lui adjoindre un capteur de mouvement trois axes pour mesurer le pourcentage d’une rampe, ou comparer la vitesse de rotation d’un gyromètre de lacet à la vitesse de déplacement pour évaluer les rayons de courbure des voies. Une autre possibilité serait d’installer une petite balance à jauges de contrainte pour évaluer l’effort de traction de nos chères motrices…


Ce projet a été pour moi un début en électronique embarquée sur un « petit train ». Il m’a permis de me plonger dans le développement d’une communication Bluetooth entre un wagon et un smartphone, et de faire mes premiers pas dans le développement d’applications Android.
33
Vos projets / Re : Wagon de mesure distance et vitesse (suite 5)
« Dernier message par Conchito le décembre 10, 2019, 12:24:51 am »
Compte tenu du nombre de caractères autorisés dans un post, la descrition des applications Android sera partagée en 2 parties.

Logiciel de réception Androïd :

Terminal Bluetooth :

La première solution, la plus simple, consiste à utiliser une application de type ‘terminal Bluetooth’ disponible sur le Play Store pour recevoir et afficher les trames émises par le wagon. Il en existe plusieurs, par exemple « Bluetooth Terminal HC-05 ».
Une fois l’appareil Android appairé avec le module HC-06, l’application se comporte sensiblement comme le terminal de l’IDE Arduino, et affiche :
•   une fenêtre de réception sur laquelle s’inscrivent les trames reçues
•   une ligne d’émission qui permet d’envoyer des commandes vers le wagon
•   des boutons logiciels auxquels on peut attribuer des commandes à envoyer.
Ceci correspond à la solution la plus facile à mettre en œuvre.

Autres applications possibles :

Il existe une application appelée « Bluetooth Electronics » qui permet de générer des écrans de gestion et d’affichage de données transmises par Bluetooth. Il suffit de configurer graphiquement les boutons, voltmètres ou autres équipements que l’on veut voir apparaitre sur le panneau de contrôle du smartphone, de leur donner un nom et de programmer leur comportement.
Par contre, il faudra modifier les données envoyées et reçues par l’Arduino pour qu’elles correspondent à la syntaxe attendue par « Bluetooth Electronics ».
http://www.keuwl.com/apps/bluetoothelectronics/

Une autre application possible est « MIT App Inventor 2 » pour ceux qui aiment la programmation graphique. Elle permettra de développer une application graphique pour recevoir et afficher les données transmises par le logiciel Arduino du wagon. Le logiciel « MIT App Inventor 2 » édite et compile l’application sur un serveur WEB via un navigateur, puis la télécharge sur le smartphone.
Par contre, il faut un compte Gmail pour accéder à cet outil de développement en ligne créé par Google.
https://appinventor.mit.edu/


Le développement d'une application graphique pour Android en utilisant Processing sera abordée dans le prochain post.

A suivre ...
34
Vos projets / Re : Wagon de mesure distance et vitesse (suite 4)
« Dernier message par Conchito le décembre 09, 2019, 11:48:47 pm »
Les dernières routines du logiciel Arduino :


mise_en_forme() :
Cette routine appelée périodiquement par la routine loop() (période en ms définie par le paramètre AFF_UPDATE) est chargée de la mise en forme des chaines de caractères envoyées à l’afficheur via le bus I2C et envoyées aussi sur la liaison série. Les traitements réalisés sont :
•   extraction du signe et des digits en base 10 de la variable distance de type flottant double et remplissage caractère par caractère de la chaine de caractères dist avec gestion de dépassement de capacité de l’afficheur.
•   même opération pour la variable vitesse de type flottant vers la chaine de caractères vit.
void mise_en_forme() {// mise en forme de chaine de caracteres des resultats distance et vitesse dans dist et vit
  int i;                                // indice pour les boucles
  long data;                            // pour stocker la valeur a afficher
  char *p;                              // pointeur sur la chaine resultat du formatage
  char c;                               // caractere intermediaire (recu ou calcule pour le formatage)
digitalWrite(test_Form_pin, HIGH);      // mise a 1 de la sortie de chronometrage du formatage (au scope)
  // on commence par la distance
        // on remplit la chaine dist avec les 7 chiffres, sans toucher au nom de l'unite deja à la fin de dist ( voir setup() )
        data = long(distance*coef_dist + 0.5);    // la valeur de la distance est arrondie à l'unite
        if (data<0) {                   // si la valeur est négative :
          c='-';                        //      - on stocke le caractere du signe
          data=-data;                   //      - on travaille sur la valeur absolue
        }
        else c=' ';                     // sinon, le caractere de signe est ' '
        p=dist;                         // p pointe sur le debut de la chaine resultat du format
        for (i=0;i<7;i++) *p++=' ';     // effacement de toute la chaine, a la fin p pointe sur la fin
        for (i=0;i<7;i++) {             // on remplit la chaine à partir de la fin (il y a toujours au moins 1 caractere)
          *p--=(data%10)+48;            // avec le reste de la division par 10 + 48 pour obtenir des caracteres
          data=data/10;                 // et on se prepare pour le chiffre suivant
          if (data==0) {                // il n'y a plus de chiffres a afficher
            *p=c;                       // on place le signe
            break;                      // et on sort de la boucle en laissant les espaces avant le signe
          }
        }
        if (data!=0) {                  // data etait superieur à 10^7 (il reste qquechose apres 7 divisions par 10)
          *p=dist;                      // il y a debordement :
          *p++=c;                       // on garde le signe
          *p++=' ';
          for (i=0;i<4;i++) *p++='X';   // et on met quelques X (4)
          *p++=' ';
          *p++=' ';
        }   
  // idem pour la vitesse (2 digits en moins)
        // on remplit la chaine vit avec les 5 chiffres, sans toucher au nom de l'unite deja à la fin de vit
        data = long(vitesse * coef_vit + 0.5);     // la valeur de la vitesse est arrondie à l'unite
        if (data<0) {                   // si la valeur est négative :
          c='-';                        //      - on prepare le caractere du signe
          data=-data;                   //      - on travaille sur la valeur absolue
        }
        else c=' ';                     // sinon, le caractere de signe est ' '
        p=vit;                          // p pointe sur le debut de la chaine resultat du format
        for (i=0;i<5;i++) *p++=' ';     // effacement de toute la chaine, a la fin p pointe sur la fin de
        for (i=0;i<5;i++) {             // on remplit la chaine à partir de la fin
          *p--=(data%10)+48;            // avec le reste de la division par 10 + 48 pour obtenir des caracteres
          data=data/10;                 // et on se prepare pour le chiffre suivant
          if (data==0) {                // il n'y a plus de chiffres a afficher
            *p=c;                       // on place le signe
            break;                      // et on sort de la boucle
          }
        }
        if (data!=0) {                  // data etait superieur à 10^5 (il reste qquechose apres 5 divisions par 10)
          *p=vit;                       // il y a debordement :
          *p++=c;                       // on garde le signe
          *p++=' ';
          for (i=0;i<4;i++) *p++='X';   // et on met quelques X
          *p++=' ';
        }
digitalWrite(test_Form_pin, LOW);   // mise à 0 de la sortie de chronometrage de la mise en forme
}

affichage() :
•   effacement des nombres précédemment affichés par dessin d’un rectangle plein, noir et envoi à l’afficheur des 2 nouvelles chaines à afficher.
•   à la fin des deux chaines de caractères dist et vit, on trouve les noms des unités qui ne sont pas effacés si ce n’est lors d’un changement d’unité.
void affichage() {    // pilotage de l'afficheur AFF_OLED:

digitalWrite(test_Aff_pin, HIGH);   // mise à 1 de la sortie de chronometrage du pilotage de l'afficheur
        // toutes les actions se font dans le buffer avant de declencher l'affichage du buffer
        display.fillRect(0, 10, 128, 20,BLACK);   // on efface la valeur precedente dist par un rectangle noir
        display.fillRect(0, 42, 128, 20,BLACK);   // on efface la valeur precedente vit par un rectangle noir
        display.setCursor(0, 10);       // on se positionne début 2eme ligne
        display.print(dist);            // on ecrit la valeur dans le buffer
        display.setCursor(0, 42);       // on se positionne début 4eme ligne
        display.print(vit);             // on ecrit la valeur dans le buffer
        display.display();              // affichage du buffer
digitalWrite(test_Aff_pin, LOW);   // mise à 0 de la sortie de chronometrage du pilotage de l'afficheur
}

[u]envoi() :[/u]
Cette routine transmet les deux chaines de caractères dist et vit via la liaison série vers le PC via l’USB ou vers le module Bluetooth HC06. La routine envoi() peut également servir en debug pour faire afficher un état ou une variable du logiciel.
[code]void envoi() {                     // communication via la liaison serie
// COMM_Serie:                         
digitalWrite(test_Com_pin, HIGH);  // mise à 1 de la sortie de chronometrage du pilotage de la liaison serie
        Serial.write(dist,10);          // envoi des 10 caracteres de dist  (write() est plus rapide que print)
        Serial.write(",",1);            // envoi du separateur
        Serial.write(vit,10);           // envoi des 10 caracteres de vit
        Serial.write(10);               // envoi de NewLine  \n
digitalWrite(test_Com_pin, LOW);  // mise à 0 de la sortie de chronometrage du pilotage de la liaison serie
}

Vérification du timing :

Dans le code il y a des instructions permettant, de vérifier sur un oscilloscope, le timing du logiciel (occupation de la CPU), via des sortie logiques positionnées à 1 en entrée de la routine, et remise à 0 à la sortie de la routine. Le basculement de la sortie consomme peu de temps (de l’ordre de la µs) et n’a pas d’influence notoire sur les mesures de timing qui sont de l’ordre de grandeur de la milliseconde.
Dans le logiciel, les sorties logiques suivantes sont affectées aux tâches suivantes :
•   D6   routine interruption()
•   D7   routine mise en forme() des chaines de caractères de la trame à envoyer
•   D8   routine envoi() : remplissage du buffer pour envoi par l’UART
•   D9   routine afficheur() : écriture du buffer de l’afficheur via la bibliothèque graphique
Ces sorties étant reliées chacune à une voie de l'oscilloscope, on obtient les constatations suivantes (voir diagrammes joints) :

La routine d’interruption (lecture des capteurs et calculs) s’exécute bien toutes les 2 ms et dure 0,55ms (D6). Elle consomme 25% du temps CPU.

La mise en forme des chaines de caractères à partir des valeurs numériques de la distance et la vitesse prend 0,13 ms (D7). Le remplissage des buffers de la liaison série prend.0,16ms (D8).

Lorsque une interruption tombe pendant l’exécution de la tache de mise en forme, celle-ci dure 0,68 ms.

Tandis que les interruptions tombent toutes les 2ms (D6), toutes les 100ms les chaines de caractères sont mises en forme (D7), puis le buffer de la liaison série est rempli (D8), puis les écritures sur l’affichage sont effectuées (D9). L’envoi des trames (Tx), déclenché par l’écriture du buffer, se fait en parallèle avec l’écriture sur l’afficheur grâce à l’UART qui les émet en temps masqué. L’envoi dure 23ms.
L’écriture dans les buffers de l’affichage prend 26,5ms dans le timing. La durée réelle n’est que de 19,5ms en temps CPU, mais l’écriture est interrompue 14 fois par l’interruption de calcul.

Ces mesures indiquent que c’est la gestion des buffers de l’afficheur qui prend le plus de temps. Le programme n’utilise environ que 50% du temps CPU. On pourrait diminuer la période d’envoi et d’affichage des trames.


A suivre, avec l'application Android   ....
35
Vos projets / Re : Wagon de mesure distance et vitesse (suite 3)
« Dernier message par Conchito le décembre 09, 2019, 11:28:11 pm »
Voici la première partie de la description du logiciel Arduino.

Implantation globale:
L’ensemble des modules contient facilement à l’intérieur du wagon. Afin de minimiser un peu le nombre de fils, l’Arduino est monté sur une carte prototype pastillée, sur laquelle viennent se raccorder l’alimentation, les 4 fils des capteurs, le bus I2C vers l’afficheur, le module Bluetooth HC06 et les diodes des feux de fin de convoi. Pour la programmation de l’Arduino, le module support se soulève et donne accès au connecteur micro-USB, en n’oubliant pas de déconnecter le module HC06.

Schéma de câblage :
Le schéma d'interconnexion des périphériques est fournit en fichier joint.

Logiciel Arduino :
L'architecture du logiciel est fournie en fichier joint.
La taille des posts étant limitée, la description du logiciel sera complétée dans le post suivant.

Déclarations et initialisation :
Les instructions #include permettent d’ajouter les fichiers des déclarations des bibliothèques. Les bibliothèques utilisées sont :
•   FlexiTimer2   gère la configuration des registres pour la gestion des tâches répétitives sous interruption timer.
•   SPI   requise pour la bibliothèque graphique Adafruit qui gère l’afficheur.
•   Wire   bibliothèque I2C pour communiquer avec l’afficheur.
•   Adafruit_GFX   bibliothèque graphique Adafruit pour écrire sur l’afficheur.
•   Adafruit_SSD1306   bibliothèque spécifique à la gestion de l’afficheur
Les instructions #define définissent des mnémoniques pour divers paramètres utilisés dans le programme.
Un objet de la classe Adafruit_SSD1306 s’appelant display est instancié en variable globale pour manipuler l’afficheur.
Diverses variables globales sont créées pour permettre les échanges de données entre les diverses routines du programme. Il s’agit principalement de variables pour récupérer les données de la routine d’interruption.
// Programme pour wagon odometre,tachymetre
//
// V1.0 S.G. création du logiciel
// Calcul distance, vitesse
// Affichage OLED, serial BlueTooth
//        r reset distance et vitesse
//        a allume led feux arrieres
//        e eteint led feux arrieres
//        u changement d'unite pour l'affichage
// Clignotement led temoin de presence d'IT
//
#include <FlexiTimer2.h>      // configuration automatisee des IT du timer 2
               
// Declarations pour affichage OLED SSD1306
#include <SPI.h>              // utilisee par la bibliotheque Adafruit pilotant l'afficheur
#include <Wire.h>             // bibliotheque I2C
#include <Adafruit_GFX.h>     // bibliotheque graphique Adafruit
#include <Adafruit_SSD1306.h> // bibliotheque afficheur
// Declarations limitees car l'afficheur OLED SSD1306 est connecte en I2C (SDA, SCL pins)
#define SCREEN_WIDTH 128      // OLED largeur de l'afficheur en pixels
#define SCREEN_HEIGHT 64      // OLED hauteur de l'afficheur en pixels
#define OLED_RESET     -1     // n° broche relié au reset de l'afficheur (-1 si reset commun avec l'arduino)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // instanciation de l'afficheur

#define PIN_X 0                         // analog pin pour mesurer x (0 à 1023 pour 0 à 5V)
#define PIN_Y 1                         // analog pin pour mesurer y (0 à 1023 pour 0 à 5V)
#define X_OFFSET 512                    // valeur du 0 pour le capteur x  (2,5 V)
#define Y_OFFSET 512                    // valeur du 0 pour le capteur y  (2,5 V)

const double _2PI = 2 * 3.1415926535;   // 2 Pi pour les calculs en radians
const double CSTE_ROUE = (11./4.);      // (mm/rad) diamètre roue/2 -> rayon  puis /2 car 2 paires d'aimants par tour de roue
const double _echantil = 0.002;         // periode d'echantillonnage pour le filtre
const double _Wn = 2.*3.1415926525 * 3.;  // rad/s   ici Fc = 3Hz pour le filtre de calcul de la vitesse
const double _2Ksi = 2. * 1.;           // ksi =1  (filtre super amorti).
const double COEF_DIST=87./1000.;       // passage de mm à km avec echelle HO
const double COEF_VIT=3.6/1000.*87.;    // passage de mm/s à km/h avec echelle HO

// les variables suivantes sont globales car l'interruption en a besoin a chaque calcul
double X1 = 0, X2 = 0;                  // variables d'etat pour le filtre 2eme ordre de calcul de la vitesse
bool init_dist = true;                  // remet à 0 la distance affichée quand vrai
double offset_dist = 0.;                // decalage de zero appliqué a la distance affichée
double coef_dist = 1.;                  // coef pour le changement d'unite de distance
double coef_vit = 1.;                   // coef pour le changement d'unite de vitesse
float angle_1 = 0;                      // sauvegarde du calcul d'angle precedent pour detection des passages +/- pi
float tours = 0;                        // sauvegarde du nombre de tours deja faits pour calcul de la distance totale
double distance;                        // distance parcourue (accessible partout)
float vitesse;                          // vitesse apres filtrage (accessible partout)
int compteur=0;                         // compte les IT, utilisé pour faire clignoter la led témoin de "vie" du soft et des IT
                                        // repasse à zero tout seul au bout de 65536 IT (131s)

// variables globales pour générer les trames à envoyer
char  dist[11]="        mm";            // chaines utilisées pour formater l'affichage
char   vit[11]="      mm/s";            // 10 caracteres affiches en taille 2 + le caractere de fin '\0'
int  unit_mm = 0;                       // memorise la selection du type d'unites

// donnees globales pour la gestion de la periode d'envoi
long aff_date;                          // date du dernier raffraichissement de l'ecran OLED
#define AFF_UPDATE  100                 // periode de rafraichissement de l'affichage en ms

#define led_pin 13                      // led à faire clignoter (temoin de "vie" des IT
#define lanterne_pin 5                  // feux arrière du wagon
#define test_IT_pin 6                   // sortie digitale pour chronometrer les IT avec un oscilloscope
#define test_Form_pin 7                 // sortie digitale pour chronometrer le formatage
#define test_Com_pin 8                  // sortie digitale pour chronometrer l'affichage
#define test_Aff_pin 9                  // sortie digitale pour chronometrer la liaison PC

// variables intermédiaires du calcul declarees globales pour pouvoir les utiliser en debug via la liaison série
int x, y;                               // mesures lues (cos et sin)
double angle;                           // angle magnetique de l'essieu (2 paires de poles)
double dx1_dt, dx2_dt;                  // variables intermédiaires pour le 2eme ordre de calcul de la vitesse

setup() :
La routine setup() réalise toutes les initialisations de la liaison série (vitesse de transmission), de l’afficheur (type et adresse I2C), des interruptions (nom de la routine d’interruption, période d’interruptions, autorisation des interruptions) et des sens de fonctionnement des broches de l’Arduino.
void setup()  {
  Serial.begin(9600);               // initialize serial connection to PC
  while (!Serial) { ; }             // wait for serial port to connect. Needed for native USB port only
 
  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {   // Initialisation de l'objet afficheur a faire en premier car
                                                      // besoin de memoire contigue pour l'allocation dynamique
                                                      // du buffer d'affichage de 1024 octets
    Serial.println(F("SSD1306 allocation failed"));   // Init ratee
    for (;;);                                         // on s'arrete, boucle infinie
  }

  // affichages varies
  display.display();                    // affichage du logo de la librairie (splash.h) mis dans le buffer par l'init
  delay(2000);                          // pause de 2 seconde pour admirer le logo ;-)
  display.clearDisplay();               // effacement du buffer de l'afficheur
  display.setTextSize(2);               // taile 2 pour message lisible
  display.setTextWrap(true);            // autorise le retour à la ligne automatique
  display.setTextColor(WHITE);          // dessine le texte en blanc
  display.setCursor(0, 0);              // ecriture a partir du coin en haut à gauche
  display.println(F("Setup OK!" ));     // message de bienvenue sur l'ecran
  display.display();                    // affichage du buffer
  Serial.println(F("Setup OK" ));       // message de bienvenue sur la com serie
  delay(1000);                          // pause pour voir le message
  display.clearDisplay();               // effacement du buffer
  display.drawPixel(0, 0, WHITE);       // affiche un pixel en haut à gauche pour ne pas laisser l'ecran vide
  display.display();                    // affichage du buffer
  display.clearDisplay();               // efface le buffer pour le trace suivant
  display.setTextSize(1);               // remplit le buffer avec le titre de chaque valeur qui sera affichee
  display.setCursor(0, 0);              // en haut à gauche
  display.print(F("distance"));
  display.setCursor(0, 32);             // au milieu à gauche
  display.print(F("vitesse"));
  display.setTextSize(2);               // gros texte pour les valeurs mesurées

  aff_date = millis();                  // init date de reference pour les affichages OLED, PC

  // initialisation interruptions cadencees
  FlexiTimer2::set(2, 0.001, interruption);    // un tick toutes les 2*1ms pour executer interruption()
  FlexiTimer2::start();                 // on demarre les IT

  // initialisation des broches de sortie
  pinMode(led_pin, OUTPUT);             // led clignotante
  pinMode(lanterne_pin, OUTPUT);        // led feu arriere wagon
  pinMode(test_IT_pin, OUTPUT);         // sortie chronometrage IT
  pinMode(test_Form_pin, OUTPUT);       // sortie chronometrage Formatage
  pinMode(test_Aff_pin, OUTPUT);        // sortie chronometrage Affichage
  pinMode(test_Com_pin, OUTPUT);        // sortie chronometrage Communication serie
}

loop() :
C’est la routine principale, appelée automatiquement en boucle par le système d’exploitation de l’Arduino. Ici elle se contente de vérifier que le temps écoulé depuis le dernier envoi et affichage est inférieur au temps de répétition (100 ms). Si ce n’est pas le cas, les routines envoi() et affichage() sont de nouveau appelées.
void loop() {  // la seule action de loop est de cadencer l'affichage des resultats
  if (millis() > aff_date)  {           // il est temps d'afficher
    aff_date += AFF_UPDATE;             // date de la prochaine action
    mise_en_forme();                    // mise en forme
    envoi();                // envoi sur la liaison serie
    affichage();                        // affichage
  }
}

serialEvent() :
Cette routine est appelée par le système d’exploitation de l’Arduino entre chaque appel à la routine loop() lorsqu’au moins un caractère a été reçu par la liaison série.
Ici, lorsqu’elle est appelée, on commence par vérifier qu’il y a bien au moins un caractère dans le buffer de réception. Puis, après lecture du 1er caractère du buffer, on exécute la commande correspondant à ce caractère. S’il y a d’autres caractères dans le buffer, ils seront lus au prochain appel de serialEvent().
Les commandes acceptées sont :
•   r   pour remise à zéro de l’odomètre. Un flag est positionné à 1, et la remise à zéro sera effectuée par le prochain passage dans la routine interruption().
•   a   allumage des feux de fin de convoi. La sortie logique de l’Arduino correspondante est positionnée à 1.
•   e   extinction des feux de fin de convoi. La sortie logique de l’Arduino correspondante est positionnée à 0.
•   u   changement d’unité d’affichage. Le choix des unités se fait par une liste cyclique de trois combinaisons : mm et mm/s, ou mm et km/h ramenés de l’échelle HO à l’échelle 1, ou m et km/h à l’échelle 1. Le changement d’unité se fait en changeant les coefficients pour calculer la valeur à afficher et en corrigeant le nom de l’unité en fin des chaines de caractères destinées à l’afficheur.
void serialEvent() {                    // fonction appelee par le systeme si il y a quelquechose de recu sur
                                        // la liaison serie (ici on ne fait que quelques commutations de flags).
  char c;                               // caractere intermediaire recu
 
  if (Serial.available()>0)             // y a-t-il qquechose recue venant de la liaison serie
        c=Serial.read();                // lecture du 1er caractere du buffer de reception
  switch(c) {                           // aiguillage en fonction du caractere recu
    case 'r' :                          // raz distance a la prochaine IT
      init_dist = true;                 
      break;
    case 'A' :                          // allumage feu arriere
    case 'a' :
      digitalWrite(lanterne_pin, HIGH);
      break;
    case 'e' :                          // extinction feu arriere
      digitalWrite(lanterne_pin, LOW);
      break;
    case 'u' :                          // changement d'unite d'affichage.
                                        // les calculs sont toujours en mm et mm/s 
      switch (unit_mm++) {              // phases du cycle des unites d'affichage avec incrementation
        case 0:                         // on passe de mm/s a km/h on reste en m
          coef_vit = COEF_VIT;          // mise à jour du coef d'affichage de la vitesse
          vit[6]='k';                   // on remplace le m et le s de 'mm/s' pour obtenir 'km/h'
          vit[9]='h';
          break;
        case 1:                         // on passe de mm a m
          coef_dist=COEF_DIST;          // mise à jour du coef d'affichage de la distance
          dist[9]=' ';                  // on efface un m pour passer de 'mm' a 'm '
          break;
        case 2:                         // on revient aux unites de calcul
          coef_dist = 1.;               // les coefs reviennent à 1.
          coef_vit = 1.;
          dist[9]='m';                  // et les chaines de caracteres sont retablies           
          vit[6]='m';
          vit[9]='s';
          break;               
       }
      unit_mm%=3;                       // seulement 3 cas d'affichage (si unit_mm vaut 3 on revient a 0)
      //     init_dist = true;          // raz distance a la prochaine IT 
      break;   
  }   // fin du swich(c)
}   // fin de serialEvent

interruption() :
Suite à l’initialisation des registres du processeur par setup() via la bibliothèque FlexiTimer2, cette routine est appelée toutes les 2 ms. Les traitements réalisés sont :
•   lecture des mesures des capteurs à effet Hall et suppression de l’offset.
•   calcul de l’angle de l’essieu par la fonction système atan2().
•   calcul du nombre total de tours de l’essieu en incrémentant ou décrémentant le compteur de demi-tours en fonction de la différence de la position angulaire de l’essieu entre deux calculs.
•   calcul de la distance parcouru en mm par multiplication de l’angle total (angle essieu + nombre de demi-tours) par le rayon de la roue.
•   calcul de la vitesse par implémentation des équations du 2nd ordre.
•   clignotement de la LED embarquée sur la carte Arduino pour indiquer que la routine d’interruption est toujours appelée : un compteur est incrémenté à chaque appel de la routine interruption(). Le bit 8 du compteur est utilisé pour piloter la broche de sortie liée à la LED. Ce bit change donc d’état tous les 256 appels de la routine interruption(). La LED clignote à une période de 256*2*2ms = 1,024 s soit presque 1Hz.
void interruption()  {
    digitalWrite(test_IT_pin, HIGH);              // positionnement sortie de chronometrage interruption
// mesure de l'angle "magnetique" = angle_essieu * 2 (car 2 paires de poles)
    x=analogRead(PIN_X)-X_OFFSET;                 // mesure cos et sin de l'angle magnetique
    y=analogRead(PIN_Y)-Y_OFFSET;                 // peut importe le gradiant de mesure, tant qu'il est identique sur X et Y
    angle = atan2(y, x);                          // extraction de l'angle (calcul ratiometrique)
// decompte du nombre de tours
    if ((angle - angle_1) > 4 ) tours -= _2PI;    // l'angle vient de faire un saut a -Pi
    if ((angle - angle_1) < -4 ) tours += _2PI;   // l'angle vient de faire un saut a +Pi
    angle_1 = angle;                              // memorisation de l'angle pour IT suivante
    distance = (angle + tours) * CSTE_ROUE;       // distance = angle * Rayon / nbre paires de poles
    if (init_dist) {                              // raz distance
      X1 = 0; X2 = 0;                             // raz variables d'etat du filtre
      tours=0;                                    // raz nbre de tours
      offset_dist = angle_1*CSTE_ROUE;            // reglage de l'offset pour avoir distance = 0
      init_dist = false;                          // raz flag pour ne pas repasser ici
    }
    distance -= offset_dist;                      // compensation de la position initiale de l'essieu
   
// filtre derivee (2eme ordre)
    dx1_dt = (distance - X2 - _2Ksi * X1) * _Wn;  // equation d'evolution de l'etat X1
    dx2_dt = X1 * _Wn;                            // equation d'evolution de l'etat X2
    X1 += dx1_dt * _echantil;                     // integration X1
    X2 += dx2_dt * _echantil;                     // integration X2
    vitesse = dx2_dt;                             // X2 recopie la distance à travers le filtre
                                                  //     ==> sa derivee est la vitesse filtree
// pour faire clignoter la led temoin de "vie" du systeme                                                                                                                 
    digitalWrite(led_pin, !(compteur++ & 0x100)); // le bit 2^9 du compteur change toutes les 256 IT
                                                  // soit 256*2ms = 512ms la led clignote a 1,024 Hz
    digitalWrite(test_IT_pin, LOW);               // positionnement sortie de chronometrage de l'interruption
}

A suivre pour la description des dernières routines du logiciel Arduino ...
36
Discussions ouvertes / Re : Utilisation d'une tablette en Wi-Fi
« Dernier message par Dominique le décembre 09, 2019, 09:49:34 pm »
Probablement dans la doc des produits.
37
Débuter / Re : Va et vient avec gare centrale voie unique
« Dernier message par Dominique le décembre 09, 2019, 09:38:48 pm »
Effectivement, il ne faut pas relier la pin 13 au 5v mais la déclarer INPUT-PULLUP.

A part cela, au moindre court-circuit, tout grille ! Il faut une limitation de courant entre l’alim et les rails, d’où la nécessité du Max471 et du soft adéquate.
38
Vos projets / Re : Wagon de mesure distance et vitesse (suite 2)
« Dernier message par Conchito le décembre 09, 2019, 08:52:29 pm »
Dans le post précédent nous avons obtenu la distance parcourue, il ne "reste" plus qu'à calculer la vitesse et réaliser les affichages.

Calcul de la vitesse :
A partir de la distance parcourue, la vitesse s’obtient par dérivation. Si Dn est la distance actuelle, est Dn-1 la distance calculée au pas précédent, la vitesse V peut se calculer par (Dn- Dn-1)/(tn-tn-1). Ici  tn-tn-1 = 0,002 s. La différence est très sensible aux erreurs et il convient de filtrer le résultat.
Pour calculer une vitesse filtrée, j’ai utilisé un filtre du 2nd ordre constitué d’un filtre du 1er ordre rebouclé via un gain et un intégrateur. Pour implémenter ce filtre, j’ai utilisé deux variables d’état X1 et X2 (voir le synoptique en fichier joint).

wn est la pulsation propre du filtre (2pi·fc)
Ksi est le facteur d’amortissement du filtre
X1n = X1n-1 + (tn – tn-1) · (E – X2n-1 - 2·Ksi·X1n-1)
S = wn · X1n-1
X2n = X2n-1 + (tn – tn-1) ·S

Le filtre du 2nd ordre ayant un gain de 1, la variable X2 est l’image filtrée de l’entrée E. La sortie S étant prise en amont de l’intégrateur de X2, S sera l’image de la dérivée de X2 qui est l’image de l’entrée filtrée, S est donc la dérivée filtrée de l’entrée E.
Si E est la distance parcourue, S est la vitesse filtrée du wagon.

Processeur et périphériques :
Le processeur utilisé est un Arduino Nano qui pilote :
•   un écran SSD1306 pour afficher les données mesurées
•   un module Bluetooth HC06 pour transmettre, sans fil, les mesures, via la liaison série, vers un équipement portable.
•   les LEDs des feux de fin de convoi.

L'utilisation d'une émulation logicielle de la liaison série (soft serial) pour communiquer avec le module Bluetooth HC06 pour conserver la liaison série pour le débug avec le PC consomme trop de temps CPU. Par contre le périphérique série (UART) travaille en parallèle à la CPU sans consommer de temps CPU. Pour garder de la marge, on utilise la liaison série standard du processeur connectée au module HC06. Mais, comme cette liaison sert également au téléchargement du logiciel, pour ne pas perturber le téléchargement, il faut déconnecter le module HC06 pendant le télé-versement du code dans le microcontrôleur.

Implantation de l’afficheur :
Afin de diminuer le nombre de fils de câblage, j’ai utilisé un afficheur OLED avec un bus I2C. Voulant avoir des caractères suffisamment lisibles, j’ai choisi un écran de de 0,96ˮ.
https://www.ebay.fr/itm/0-96inch-IIC-I2C-Serial-White-OLED-Display-Module-128X64-SSD1306-12864-For-STM32/253761819458
La hauteur utile de l’afficheur correspond assez bien à la hauteur du wagon, mais son encombrement mécanique total oblige à fraiser le plancher du wagon pour loger la partie inférieure de l’afficheur

Module Bluetooth HC06 :
La liaison série de l’Arduino est relié à un module Bluetooth de type HC06. Le module HC06 est paramétrable (nom, vitesse …) via des commandes AT (voir les nombreux tutoriels).
https://www.ebay.fr/itm/Wireless-Serial-4-Pin-Bluetooth-RF-Transceiver-Module-HC-06-RS232-With-backplane/200924726178
Attention, les données échangées par le HC06 sont en 3,3V alors que l’Arduino est en 5V. Il faut prévoir un pont diviseur entre la sortie Tx de l’Arduino et l’entrée Rx du HC06. Par contre, le HC06 accepte une alimentation de 3,3V à 6V.

Feux de fin de convoi :
Deux LEDs rouges de 3mm sont collées dans des perçages à l’arrière du wagon. Deux petits manchons de laiton de 3mm intérieur viennent compléter la décoration. Les LEDs sont alimentées par une sortie de l’Arduino et commandées par la liaison série.


A suivre ....
39
Vos projets / Re : Wagon de mesure distance et vitesse
« Dernier message par CATPLUS le décembre 09, 2019, 08:36:11 pm »
40
Débuter / Re : Re : Va et vient avec gare centrale voie unique
« Dernier message par msport le décembre 09, 2019, 08:26:45 pm »

ce sera bon !


pour le message cité.

Mais ici la résistance de pullup externe (qui ne figure pas) manque entre le 5V et l'entrée de l'Arduino = grillage de l'entrée.
Elle n'est utile qu'en cas de fausses détections sinon elle ne sert à rien dans la mesure où on cherche en général à avoir la meilleure sensibilité.
Pages: 1 2 3 [4] 5 6 ... 10