Partage
  • Partager sur Facebook
  • Partager sur Twitter

Communication Raspberry Pi vers Arduino Uno

Problème d'écriture dans le buffer

Sujet résolu
    8 juillet 2019 à 14:37:48

    Bonjour à tous,

    Beaucoup de sujets traitent la connexion série mais malgré tout je ne trouve pas la solution à mon problème.

    Je souhaite à l'aide d'une carte arduino Uno récupérer différentes valeurs de capteurs puis envoyer ces valeurs vers mon RPi pour traiter les données. La liaison série (via câble USB) fonctionnement correctement dans le sens Uno vers RPi, j'arrive à lire le buffer de sorti de l'Arduino depuis le RPi.

    Mon problème est qu'à présent je souhaite envoyer un message à l'Arduino depuis mon RPi pour lui demander de lire ses entrées et de renvoyer ces données au RPi. Toutefois quand j'écris un message dans le buffer de sorti du RPi j'ai l'impression que ce message n'est pas toujours (de manière irrégulière) écrit dans le buffer.

    Si quelqu'un a déjà eu un problème similaire faites moi signe, ça fait déjà plusieurs jours que je rame...

    Voici mon code (très simple côté arduino), qui consiste à allumer une LED quand le RPi envoie le caractère '1': 

    int ledPin = 7;
    boolean state;
    
    void setup() {
            Serial.begin(9600);     // opens serial port, sets data rate to 9600 bps
            pinMode(ledPin, OUTPUT);
    
            
    }
    
    void loop() {
    char c;
            // send data only when you receive data:
           /* if (Serial.read()==1) {
              state = HIGH;
              digitalWrite(ledPin, state);
              Serial.println("ok");
              
            }*/
    
    
            c= (char)Serial.read();
            //Serial.println(Serial.read());
            if (c == '1')
            {
              digitalWrite(ledPin, HIGH);
              Serial.println("ok");
             
            }
            else 
            {
              Serial.println("pas 1");
              
                digitalWrite(ledPin, LOW);
            }
            
            
    }

    Voici mon code java qui envoie le caractère '1' puis lit le buffer de l'arduino :

    import com.pi4j.io.serial.*;
    
    import java.io.*;
    
    public class PiCom implements Runnable
    {
    	public Serial serial;
    	public BufferedReader bufRead;
    	public BufferedWriter bufWrite;
    	OutputStream out;
    	public PrintWriter prWriter;
    	
    	public PiCom()  throws Exception
    	{
    		SerialConfig config;
    		
    		config = new SerialConfig();
    
            // set default serial settings (device, baud rate, flow control, etc)
    
            config.device("/dev/ttyACM0") //port série sur le Pi
                  .baud(Baud._9600)
                  .dataBits(DataBits._8)
                  .parity(Parity.NONE)
                  .stopBits(StopBits._1)
                  .flowControl(FlowControl.NONE);
            
            serial = SerialFactory.createInstance();
            serial.open(config);
            out = this.serial.getOutputStream();
            bufWrite = new BufferedWriter (new OutputStreamWriter(out));
            prWriter = new PrintWriter(bufWrite);
        
    	 	bufRead = new BufferedReader(new InputStreamReader(this.serial.getInputStream()));
    	 	
    	}
    	
    	public void run()
    	{
              while (true){
    			
    			System.out.println("a");
    			picom.prWriter.write('1');
    			System.out.println("b");
    			picom.prWriter.flush();
    			System.out.println(picom.bufRead.readLine());
    			i += 1;
    			
    			if (i == 15){
    				break;
    			}
    		}
    		System.exit(0);
    }

    Voici ce que j'obtiens en lecture sur le RPi :

    a
    b
    pas 1
    a
    b
    pas 1
    a
    b
    pas 1
    a
    b
    pas 1
    a
    b
    pas 1
    a
    b
    pas 1
    a
    b
    pas 1
    a
    b
    pas 1
    a
    b
    pas 1
    a
    b
    pas 1
    a
    b
    pas 1
    a
    b
    pas 1
    a
    b
    pas 1
    a
    b
    pas 1
    a
    b
    pas 1
    

    Et la LED qui clignote mais pas de manière constante et ce qui est étrange c'est l'absence de 'ok' alors que la LED clignote.

    C'est assez long, j'espère avoir été clair mais si vous avez ne serait-ce qu'un bout de piste ça serait génial.

    Merci


    • Partager sur Facebook
    • Partager sur Twitter
      9 juillet 2019 à 15:24:22

      le problème (très probablement) est que cette liaison est half duplex (de memoire) ce qui veux dire qu'il n'y en a qu'un qui peut ecrire à la fois et il faut que RPI le face au bon momment.

      Si tu test juste de placer la arduino en lecture seule ça devrait passer, elle devrait bien recevoir des trucs. mais dans ton cas il doivent tous les deux ecrire en même temps ce qui fait que personne ne reçoi rien de valide.

      -
      Edité par ox223252 9 juillet 2019 à 15:32:43

      • Partager sur Facebook
      • Partager sur Twitter

      la connaissance est une chose qui ne nous appauvrit pas quand on la partage.

      Mon GitHub

        9 juillet 2019 à 21:13:43

        C'est une simple liaison série asynchrone avec RX et Tx. Pour quelle raison serait-elle half-duplex?

        pour le reste, j'avoue ne pas trop comprendre d'où vient le problème. Il faut un peu de matériel de mesure pour trouver des pistes. Sans oscillo ou analyseur logique, on peut rester bloquer très très longtemps.

        • Partager sur Facebook
        • Partager sur Twitter
          10 juillet 2019 à 8:02:45

          Je dis half duplux car ce n'est pas du RS232, ça se rapproche plus du RS485 (dans le codage des données).

          Un USB est formé de quatres fils (dans le cas qu'i nous interresse)

          • VCC / GND qui  ne sont là que pour l'allim 
          • D+ / D- pour les données sauf que ces deux forment une paire torsadée et utilisent le principe de la transmission différentielle afin de garantir une certaine immunité aux bruits parasites de l’environnement physique du périphérique ou de son câble. 
          Donc (D-) = -(D+), autrement dit on à la même donnée sur D+ et D- (à l'inversion près). Au final c'est comme si on avait trois fils VCC, GND, data avec data utilisé altenativement en RX et TX.

          -
          Edité par ox223252 10 juillet 2019 à 8:04:28

          • Partager sur Facebook
          • Partager sur Twitter

          la connaissance est une chose qui ne nous appauvrit pas quand on la partage.

          Mon GitHub

            10 juillet 2019 à 10:10:58

            Bonjour,

            On ne peut pas vraiment parlé de liaison full-duplex pour L'Arduino car le microcontrôleur (Atemega328p) utilise un seul registre pour l'émission et la réception.

            Je pense que le problème vient du code que tu utilises sur l'Arduino, la fonction Serial.read() n'est pas bloquante et renvoie -1 quand aucun caractère n'a été reçu.

            Pour connaitre le nombre de caractères reçu tu peux utiliser Serial.available()

            int ledPin = 7;
            boolean state;
             
            void setup() {
                    Serial.begin(9600);     // opens serial port, sets data rate to 9600 bps
                    pinMode(ledPin, OUTPUT);
             
                     
            }
             
            void loop() {
            char c;
                    // send data only when you receive data:
                   /* if (Serial.read()==1) {
                      state = HIGH;
                      digitalWrite(ledPin, state);
                      Serial.println("ok");
                       
                    }*/
             
                 if(Serial.available()>0)
                 {
                    c= (char)Serial.read();
                    //Serial.println(Serial.read());
                    if (c == '1')
                    {
                      digitalWrite(ledPin, HIGH);
                      Serial.println("ok");
                      
                    }
                    else
                    {
                      Serial.println("pas 1");
                       
                        digitalWrite(ledPin, LOW);
                    }
                }
                     
                     
            }



            • Partager sur Facebook
            • Partager sur Twitter
              10 juillet 2019 à 11:32:22

              @ox223252:

              l'USB est transparent dans cette histoire. L'adaptateur USB / série (contrôleur FT232, CPxxx, etc) est capable de transmettre sur Rx et Tx simultanément, donc full duplex côté liaison asynchrone.

              De l'autre côté, c'est un bus avec multiplexage temporel des paquets de données. Mais côté asynchrone on n'a pas à s'en préoccuper, c'est le contrôleur qui rend ça transparent.

              @pigeorge:  "le microcontrôleur (Atemega328p) utilise un seul registre pour l'émission et la réception"

              ???. Çà serait bien la première fois que j'entends parler d'un UART (USART en l’occurrence) qui posséderait un buffer commun pour l'émission et la réception. Regarde la doc, page 144, et tu verras que l'UART possède bien une structure classique avec buffer Rx et Tx séparés (heureusement).

              PS: il suffit d'ailleurs de regarder les "features" de l'USART pour noter, je cite: "Full duplex operation (independent serial receive and transmit registers)". Je ne sais pas où tu as trouvé cette information de buffer commun, mais à mon avis il va falloir l'éliminer de tes références.

              -
              Edité par zoup 10 juillet 2019 à 11:36:55

              • Partager sur Facebook
              • Partager sur Twitter
                10 juillet 2019 à 13:18:44

                Bonjour,

                Merci à tous pour vos réponses.

                J'ai réussi à régler mon problème, un des problèmes venait en effet du fait que la fonction serial.read() n'est pas bloquante. Pour ce faire j'ai utilisé un while qui contrôle que mon caratère lut soit différent de "-1" (qui correspond à ce que l'arduino lit quand le buffer est vide). Un autre problème venait du fait qu'apparemment avant le premier envoie de mon caractère le buffer n'est pas ce qui crée des "interférences". Pour régler ce problème je vide le buffer avec un flush sur java et j'attends que l'arduino me confirme que le buffer est bien vide en bloquant dans le setup avec un while tant que le buffer n'est pas vide.

                Merci à tous.

                Cette manière de faire n'est toutefois pas très "propre" donc si jamais vous avez des solutions alternatives je suis preneur.

                Pour ceux que ça intéresse voilà les codes :

                Arduino :

                int ledPin = 7;
                boolean state;
                
                
                void setup() {
                        Serial.begin(9600);     // opens serial port, sets data rate to 9600 bps
                        pinMode(ledPin, OUTPUT);
                
                        delay(100);
                        
                        while(Serial.read() != -1);  // tant que le buffer n'est pas vide j'attends
                        Serial.println("START");
                        
                }
                int i =0;
                
                void loop() {
                signed char c;
                
                        // send data only when you receive data:
                       /* if (Serial.read()==1) {
                          state = HIGH;
                          digitalWrite(ledPin, state);
                          Serial.println("ok");
                          
                        }*/
                
                
                
                        while((c= (signed char)Serial.read()) == -1);   //tant que l'arduino ne recoit rien j'attends
                       // Serial.println(Serial.read());
                        if (c == '1')
                        {
                           i++;
                          digitalWrite(ledPin, HIGH);
                          Serial.print("i="); Serial.println(i); 
                        }    
                        else  
                        {
                
                          digitalWrite(ledPin, LOW);
                          Serial.println(c);
                        } 
                
                            if (i == 10) digitalWrite(ledPin, LOW);
                        
                }

                Code java :

                int i = 0;
                		char c = '1';
                		
                		picom.prWriter.flush();
                		
                		while(!picom.bufRead.readLine().equals("START"));
                		
                		System.out.println("Start recu");
                		
                		while (true){
                			
                			un = (int) c;
                			//System.out.println("a");
                			picom.prWriter.write(un);
                			picom.prWriter.flush();
                			
                			//picom.prWriter.print(un);
                			System.out.println("b"+ un);
                			
                			System.out.println(picom.bufRead.readLine());
                			//picom.prWriter.flush();
                			i += 1;
                			Thread.sleep(1000);
                			
                			
                			if (i == 10){
                				System.exit(0);
                				break;
                				
                			}
                		}
                		System.exit(0);



                -
                Edité par HutP 10 juillet 2019 à 13:33:56

                • Partager sur Facebook
                • Partager sur Twitter
                  10 juillet 2019 à 14:01:15

                  Je pense que l'utilisation de Serial.available proposée par pigeorge est la bonne solution.

                  Il faudrait que les données attendues soient encapsulées dans une trame, avec un marqueur de début, le nombre d'octets à suivre et éventuellement un checksum. A chaque fois que des données sont reçues, on attend le marqueur de début et si c'est le cas on ajoute dans un buffer les caractères reçus. On analyse si on a trame complète et si c'est le cas on en extrait les données utiles.

                  On vire la trame du buffer et on continue le cycle pour la réception d'autres trames.

                  • Partager sur Facebook
                  • Partager sur Twitter
                    10 juillet 2019 à 14:32:56

                    D'accord je vais regarder avec un serial.available.

                    Je ne suis pas sûr de comprendre :

                    zoup a écrit:

                    Il faudrait que les données attendues soient encapsulées dans une trame, avec un marqueur de début, le nombre d'octets à suivre et éventuellement un checksum. A chaque fois que des données sont reçues, on attend le marqueur de début et si c'est le cas on ajoute dans un buffer les caractères reçus. On analyse si on a trame complète et si c'est le cas on en extrait les données utiles.


                    De quel côté faudrait-il que je le fasse, côté arduino ?

                    Le but est de vérifier que la trame reçue correspond bien à la trame envoyée c'est ça ? J'avoue ne pas du tout savoir comment fonctionne le principe de marqueur.

                    zoup a écrit:

                    On vire la trame du buffer et on continue le cycle pour la réception d'autres trames.

                    Qu'entendez-vous par là ? Il est possible de vider le buffer depuis arduino ?

                    J'ai entendu parler d'un mode XON/XOFF, cela a-t'il un intérêt dans ce que je veux réaliser ?

                    -
                    Edité par HutP 10 juillet 2019 à 14:36:10

                    • Partager sur Facebook
                    • Partager sur Twitter
                      10 juillet 2019 à 15:01:41

                      HutP a écrit:

                      De quel côté faudrait-il que je le fasse, côté arduino ?

                      Oui, Serial.available, c'est du code Arduino.

                      "Le but est de vérifier que la trame reçu correspond bien à la trame envoyée c'est ça ? J'avoue ne pas du tout savoir comment fonctionne le principe de marqueur."

                      Lorsque tu reçois un flux continu d'octets, il faut bien savoir à quel moment commence une trame. C'est le principe des transmission asynchrone par exemple avec le bit start, ou la condition de start avec l'I2C.

                      "Qu'entendez-vous par là ? Il est possible de vider le buffer depuis arduino ?"

                      Le buffer, c'est un simple tableau que tu gères toi-même, à ne pas confondre avec le buffer de réception de l'UART et qui est un composant matériel du µcontrôleur. Donc tu stockes tes caractères dans un tableau au fur et à mesure de leur réception et lorsque tu remarques un début de trame, tu sais que ce qui suit est une trame.

                      Lorsque la trame est complète, tu la sors du tableau, ce qui n'est que de la gestion de tableau.

                      ce qu'il faut comprendre, c'est que lorsqu'une trame a été envoyée, si la fonction de lecture n'est pas bloquante, le récepteur ne va pas forcément pouvoir la lire en une seule fois: il est possible que tu ne récupères que quelques caractères de la trame, mais il est aussi possible de récupérer les derniers caractères de la ou des trames précédentes.

                      Il faut donc une synchronisation, d'où le marquage de début de trame qui peut simplement constituer en un caractère qui n’apparaîtra jamais dans les données utiles. Par exemple, si tes données  sont codées en ASCII, un simple caractère 0xFF peut faire l'affaire, ou bien le caractère STX (start of text), où n'importe quel autre code qui te conviendra.

                      Autant l'émission est simple à gérer, autant la réception n'est pas triviale.



                      -
                      Edité par zoup 10 juillet 2019 à 15:08:36

                      • Partager sur Facebook
                      • Partager sur Twitter
                        10 juillet 2019 à 15:48:27

                        D'accord merci pour vos réponses.

                        Je clos le sujet.

                        • Partager sur Facebook
                        • Partager sur Twitter

                        Communication Raspberry Pi vers Arduino Uno

                        × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                        × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                        • Editeur
                        • Markdown