IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Windows Forms Discussion :

Besoin de conseils - Interface de test bus série - Arduino [Débutant]


Sujet :

Windows Forms

  1. #1
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2017
    Messages
    4
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Drôme (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2017
    Messages : 4
    Points : 3
    Points
    3
    Par défaut Besoin de conseils - Interface de test bus série - Arduino
    Bonjour à tous,
    Alors d'abord merci de m'avoir adopté !

    Je souhaiterais avoir quelques conseils sur la conception d'une interface en C#.
    Je m'explique, je réalise cette interface pour regrouper tout les tests que j’effectue sur une carte Arduino (équipée d'un module quelconque, disons un module GSM) car ça devient un peu fatiguant d'utiliser le moniteur série d'Arduino. En gros mon interface me permettrait d'envoyer des commandes (du style "AT+IPR=115200") à l'aide de boutons et de recevoir une réponse du microcontrôleur dans une textbox. Après avoir souffert quelques jours sur la partie communication série en C# mon programme fonctionne, enfin... J'arrive à :

    -Envoyer une commande à mon Arduino sur le bus série depuis mon winform.
    -Réceptionner cette commande avec un programme préalablement téléversé sur mon Arduino.
    -Renvoyer cette commande sur le bus série pour faire un simple echo (pour tester) avec le même programme préalablement téléversé.
    -Récupérer l'echo sur le winform (l'afficher dans une textbox).

    J'ai un peu de mal avec le C# mais pour le coup j'ai pas trop le choix du langage...
    En fait j'aurais voulu savoir si quelqu'un connaissait un moyen propre de faire d'un textbox une sorte de console de logging, qui pourrait donner quelque chose comme ceci :

    >Attente d'une commande...
    >Envoi de la commande : AT+IPR=115200
    >Le module GSM à répondu : OK
    >Attente d'une commande...

    Je n'arrive pas à conserver le contenu de la textbox, à chaque fois que je clique sur mon bouton elle se vide.
    Pourtant mon test textBox1.AppendText("coucou"); ligne 36 ne clear pas la textbox (AppendText ajoute du texte non ?).
    J'ai l'impression de m'embrouiller dans le code je ne sais pas très précisément qu'est-ce qui fait quoi et quand...

    J'ai également rendu thread-safe tout mon petit truc mais j'ai l'impression de l'avoir fait de 2 manières différentes à deux endroits différents, si quelqu'un peut également m'éclaircir sur ce point ce serait super.

    A terme, quelle organisation vais-je devoir adopter au niveau de ma classe Form1 s'il y a 36 commandes pour 4 modules différents ?
    J'ai peur que ce soit un peu chiant d'avoir 36 boutons, même si c'est juste une interface de tests...

    Merci à tous pour votre patience !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
     
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.IO.Ports;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
     
    namespace SerialTestInterface
    {
        public partial class Form1 : Form
        {
            SerialPort Serial;
            public Form1()
            {
                InitializeComponent();
            }
     
            private void Form1_Load(object sender, EventArgs e)
            {
                //New SerialPort nammed Serial
                Serial = new SerialPort("COM3", 9600, Parity.None, 8, StopBits.One);
                //Event Handler that specify a method to use when the event append
                this.Serial.DataReceived += new SerialDataReceivedEventHandler(this.Serial1_DataReceived);
                this.Serial.Open();
     
            }
     
            //Here we send "AT" to Serial1
            private void button1_Click(object sender, EventArgs e)
            {
                this.Serial.Write("AT" + Environment.NewLine);
                //textBox1.AppendText("coucou");
            }
     
            private void button2_Click(object sender, EventArgs e)
            {
                this.Serial.Write("GSMAT" + Environment.NewLine);
            }
     
            //This method is called when the event append
            private void Serial1_DataReceived(object sender, SerialDataReceivedEventArgs e)
            {
                var InputData = this.Serial.ReadLine();
                var Text = InputData;
                System.Diagnostics.Debug.Write(InputData + Environment.NewLine);
                try {
                    textBox1.AppendText(Text);
     
                }
                catch (InvalidOperationException exception)
                {
                    System.Diagnostics.Debug.Write(exception.Message);
                    Action t = () =>
                    {
                        AppendTextBox1(InputData);
                        textBox1_TextChanged(sender, e);
                    };
                    textBox1.BeginInvoke(t);
                }
            }
     
            private void textBox1_TextChanged(object sender, EventArgs e){
                var InputData = this.Serial.ReadLine();
                var Text = InputData;
     
     
            }
     
            // This delegate enables asynchronous calls for setting
            // the text property on a TextBox control.
            delegate void AppendTextBox1CallBack(string text);
     
            public void AppendTextBox1(string txt)
            {
     
                // InvokeRequired required compares the thread ID of the
                // calling thread to the thread ID of the creating thread.
                // If these threads are different, it returns true.
                if (this.textBox1.InvokeRequired)
                {
                    AppendTextBox1CallBack d = new AppendTextBox1CallBack(AppendTextBox1);
                    this.Invoke(d, new object[] { txt });
                }
                else
                {
                    this.textBox1.Text = txt;
                }
            }
     
     
        }
    }

  2. #2
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    pour concaténer des string il y a +
    donc untxtb.text += environment.newline + "nouveau texte en dessous";

    il faut que le textbox soit multiligne à true tant qu'à faire ^^

    un listbox aurait pu convenir aussi (avec .items.add)

    après sur l'interface je pense que le mieux serait d'avoir un fichier contenant la structure des tests
    tel type de module
    - telle commande
    - autre commande
    ...
    et que l'interface se construise en fonction de ce fichier de conf (avec éventuellement un bouton par commande sur le type de module sélectionné (donc pas tous les boutons de tous les types de modules à un instant T))

    après si le but est de tester toutes les commandes d'un type de module pourquoi faire une interface avec un bouton par commande ? autant envoyer toutes les commandes à la suite et écrire sur l'interface si tout est ok ou ce qui n'est pas ok

    selon la finalité on pourra mieux t'aiguiller
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  3. #3
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2017
    Messages
    4
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Drôme (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2017
    Messages : 4
    Points : 3
    Points
    3
    Par défaut
    Merci Pol63 je vais surement pouvoir avancer un peu.

    Le multiligne est a True.

    J'ai remplacé
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    textBox1.AppendText(InputData);
    par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    textBox1.Text += Environment.NewLine + InputData;
    C'est vrai que cela semble plus adapté, mais ma textBox n'affiche toujours qu'une fois l'echo quand je clique plusieurs fois sur le bouton (je pense qu'il vide la textbox et qu'il réécrit la même chose mais visuellement ça ne bouge pas).
    Il y a autre chose qui bloque la concaténation ?

    EDIT : je n'avais pas remplacé "this.textBox1.Text = txt;" par "this.textBox1.Text += txt + Environment.NewLine;" maintenant ça fonctionne à ce niveau !

    Pour ce qui est de l'interface pour le moment ce n'est pas le plus grave, mais pour donner une idée j'ai 4 composants qui vont devoir interagir alors je ne pense pas que je vais pouvoir faire une interface par composants même si l'idée est bonne.

    Sinon tu as raison, je pourrais envoyer une série de commande au clique sur le bouton et ce serait visuellement plus léger !
    Je pourrais le faire pour les phases d'initialisation des composants, pour les commandes basiques.

    Mais par la suite je pense que le mieux serait une zone de saisie de commande et la j'avoue ne pas trop savoir comment m'y prendre...
    J'imagine un petit protocole, je pourrais par exemple envoyer "GSMAT" et faire lire les 3 premiers caractères à l'Arduino pour qu'il sache qui est le destinataire, ainsi "GSMAT" serait la commande "AT" destinée au GSM, "GPStruc" serait la commande "truc" destinée au GPS etc.

    Par ailleurs, est-ce que
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    delegate void AppendTextBox1CallBack(string text);
            public void AppendTextBox1(string txt)
            {
                if (this.textBox1.InvokeRequired)
                {
                    AppendTextBox1CallBack d = new AppendTextBox1CallBack(AppendTextBox1);
                    this.Invoke(d, new object[] { txt });
                }
                else
                {
                    this.textBox1.Text = txt;
                }
            }
    sert à quelque chose ?

    J'ai l'impression d'avoir déjà traité le multi-threading avec
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    Action t = () =>
    {
         AppendTextBox1(InputData);
         textBox1_TextChanged(sender, e);
    };
    textBox1.BeginInvoke(t);
    Ici j'ai mis "textBox1_TextChanged(sender, e);" mais cette méthode est vide, ça n'a pas de sens
    Je ne devrais pas plutôt mettre autre chose ?
    Là encore je ne sais pas trop ce que j'ai bidouillé mais ça a finit par fonctionner...

    Quoi-que c'est peut-être ce qui fait que la textbox n'affiche pas plusieurs lignes.
    Car la console me sort ceci :

    Exception levée*: 'System.InvalidOperationException' dans System.Windows.Forms.dll
    Opération inter-threads non valide*: le contrôle 'textBox1' a fait l'objet d'un accès à partir d'un thread autre que celui sur lequel il a été créé.AT


    On vois AT à la fin, donc ça passe, mais ça passe en mode "inter-threads non valide" donc j'imagine que c'est toujours pas thread-safe...
    Quelqu'un sait comment rendre mon code thread-safe proprement ?

    Merci encore !

  4. #4
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    la 1ère méthode est sur la bonne base

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public void AppendTextBox1(string txt)
            {
                if (this.textBox1.InvokeRequired)
                {
                    AppendTextBox1CallBack d = new AppendTextBox1CallBack(AppendTextBox1);
                    this.Invoke(d, new object[] { txt });
                }
                else
                {
                    this.textBox1.Text += "\r\n" + txt;
                }
            }
    cette méthode fait que si on est pas sur le thread principal (qui a le droit de modifier l'interface) on demande au thread principal (via .invoke) d'appeller cette même méthode
    et là on ajoute au textbox (j'ai mis une concaténation à la place du =)

    du coup partout dans le code si tu veux ajouter du texte sur le textbox tu peux appeler cette méthode en lui donnant le texte à ajouter, et c'est elle qui s'occupe de vérifier s'il faut le mécanisme cross thread
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  5. #5
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2017
    Messages
    4
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Drôme (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2017
    Messages : 4
    Points : 3
    Points
    3
    Par défaut
    Merci encore,

    Si j'ai bien compris :

    J'ai une méthode Serial1_DataReceived qui est appelée quand on reçoit quelque chose sur le bus série.
    Cette méthode va lire le bus série et afficher les données dans ma textbox.

    On essaye afficher les données dans le try, si ça ne fonctionne pas on soulève InvalidOperationException et on appelle AppendTextBox1 dans le catch.

    AppendTextBox1 va rendre l'affichage des données possible avec un Invoke, qui va comparer les ID des threads.
    Si ils sont différents on utilise un delegate nommé AppendTextBox1CallBack qui va rappeler AppendTextBox1.
    On gros ça fait un retry ? C'est ça le principe de l'invoke et du delegate ?

    J'imagine que j'ai à peu près compris ?

    Voici mon code actuel (la partie intéressante).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
     
            private void Serial1_DataReceived(object sender, SerialDataReceivedEventArgs e){
     
                var InputData = this.Serial.ReadLine();
                InputData = this.Serial.ReadLine();
                System.Diagnostics.Debug.Write(InputData + Environment.NewLine);
                try {
                    textBox1.Text += InputData + Environment.NewLine;
                }
                catch (InvalidOperationException exception)
                {
                    AppendTextBox1(InputData);
                }
            }
     
            private void textBox1_TextChanged(object sender, EventArgs e){
     
                textBox1.SelectAll();
                textBox1.ScrollToCaret();
            }
     
            delegate void AppendTextBox1CallBack(string text);
     
            public void AppendTextBox1(string txt)
            {
                if (this.textBox1.InvokeRequired)
                {
                    AppendTextBox1CallBack d = new AppendTextBox1CallBack(AppendTextBox1);
                    this.Invoke(d, new object[] { txt });
                }
                else
                {
                    this.textBox1.Text += txt + Environment.NewLine;
                }
            }
    C'est assez propre ou bien il manque des choses ?
    La console m'indique toujours Exception levée*: 'System.InvalidOperationException' dans System.Windows.Forms.dll pour chaque lignes qui s'affichent. Et à la fin de la séquence d'initialisation du GSM la console indique Le thread 0x2e0 s'est arrêté avec le code 0 (0x0).
    Une capture d'écran pour mieux visualiser.

    J'ai rajouté des boutons qui ont le même fonctionnement.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
            private void button2_Click(object sender, EventArgs e)
            {
                this.Serial.Write("GPS-INIT\n" + Environment.NewLine);
            }
    Tout fonctionne à une exception près.
    On vois sur la capture d'écran qu'au premier clique sur le bouton "Initialize GSM" ça ne fonctionne pas(1).
    Au second clique tout est parfait (2).

    Le code arduino qui génère ce message est le suivant (les parties importantes uniquement).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    String receiver = message.substring(0, 3); //Soit "GSM" dans la chaine "GSM-INIT"
    String command = message.substring(3); //Soit "-INIT"
         //Je triche un peu, je n'envoie pas les commande depuis l'interface C#
         if(receiver == "GSM"){
            S.println("Flip&Click will talk to the GSM");
            delay(200);
            if(command =="-INIT\n"){
              S.println("Flip&Click will initialize the GSM");
              while (!gprs.init()); //Une libraire gère l'initialisation.
              S.println("OK");
              S.print("Setting up network...");
              //reste de la séquence d'initialisation du GSM.
         }else {
          S.println("No receiver called " + receiver + " live on this flip&click board, sorry !"); // On vient ici au premier clique sur "Initialize GSM".
         }
    Pourquoi ça ne fonctionne jamais au premier clique ?
    On vois bien que la variable receiver est vide dans le log de l'interface (trait rouge) et que la suite " live on this flip&click board, sorry !" ne s'affiche pas non plus.

    Une idée ?

    Nom : probleme.PNG
Affichages : 328
Taille : 88,6 Ko

  6. #6
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    Citation Envoyé par Niconnexion Voir le message
    On essaye afficher les données dans le try, si ça ne fonctionne pas on soulève InvalidOperationException et on appelle AppendTextBox1 dans le catch.

    AppendTextBox1 va rendre l'affichage des données possible avec un Invoke, qui va comparer les ID des threads.
    Si ils sont différents on utilise un delegate nommé AppendTextBox1CallBack qui va rappeler AppendTextBox1.
    On gros ça fait un retry ? C'est ça le principe de l'invoke et du delegate ?
    non, ca c'est la partie du code à retirer
    datareceived est forcément sur un autre thread, donc c'est sur que modifier le textbox va planter, donc ca ne sert à rien d'essayer
    donc tu enlèves le try catch et tu mets juste appendtextbox, qui comme je disais te permettra de modifier le textbox de n'importe où

    le principe de l'invoke et du delegate n'a rien à voir avec du retry, un delegate est un pointeur vers une méthode, et invoke permet de l'appeler (invoke sur un control permet de l'ajouter sur la pile de son thread)
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  7. #7
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2017
    Messages
    4
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Drôme (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2017
    Messages : 4
    Points : 3
    Points
    3
    Par défaut
    Merci pour cette explication Pol63
    L'ensemble fonctionne bien désormais !

    PS : Merci aussi pour les cours et tutoriels pour apprendre Arduino

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 8
    Dernier message: 23/11/2016, 02h50
  2. Interface Web, besoin de conseil !
    Par Skarlix dans le forum Webdesign & Ergonomie
    Réponses: 3
    Dernier message: 29/08/2005, 09h13
  3. [C#] [ADO.NET] Besoin de conseil
    Par djsbens dans le forum Accès aux données
    Réponses: 8
    Dernier message: 01/04/2005, 15h04
  4. Réponses: 3
    Dernier message: 24/12/2004, 12h21
  5. Réponses: 1
    Dernier message: 06/01/2003, 07h55

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo