Lecture /dev/ttyUSB0 en C

Fermé
ABouras Messages postés 26 Date d'inscription vendredi 16 novembre 2012 Statut Membre Dernière intervention 7 décembre 2012 - 20 nov. 2012 à 16:19
totof31 Messages postés 163 Date d'inscription lundi 29 octobre 2012 Statut Membre Dernière intervention 30 avril 2013 - 21 nov. 2012 à 15:00
Bonjour!

Je voudrais lire des trames de données envoyées par un GPS par protocole XBee. La clé USB XStick reçoit les données suivantes :
CHARS : 15931    SENTENCES = 0    CHECKSUM : 58
Heading : 55    Tilt: -46    Roll:2
CHARS : ....

et ainsi de suite ... J'arrive à les lire en tapant dans le terminal la commande :
$ screen /dev/ttyUSB0

Mais j'aimerais pouvoir afficher ces coordonnées de la même manière, mais avec un programme écrit en C. Voici ce que j'ai fais pour l'instant :
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include "serial_port.h"

void read_Serial_Port(const char* DEVICE_PORT)
{
	int file;
	struct termios options;
	unsigned int nCountMax = 60;
	bool b;
	
	file = open(DEVICE_PORT, O_RDONLY | O_NOCTTY | O_NDELAY);
	
	if(file == -1){perror("Unable to open the serial port\n");}
	printf("Serial port open successful !\n");
	  
	tcgetattr(file, &options); 			
	cfsetispeed(&options, B9600); 					
	cfsetospeed(&options, B9600); 					
	options.c_cflag |= (CLOCAL | CREAD); 			  
	options.c_cflag |= PARENB; 						//No parity					
	options.c_cflag |= PARODD; 						
	options.c_cflag &= ~CSTOPB; 					
	options.c_cflag &= ~CSIZE; 						
	options.c_cflag |= CS8; 						//8 bits					
	options.c_iflag |= (INPCK | ISTRIP); 			
	tcsetattr(file, TCSANOW, &options); 	     
	fcntl(file, F_SETFL, FNDELAY);			
	 
	printf("Reading serial port ...\n\n"); 
	b = readMessage(file, nCountMax);
	if (b == 0){printf("Error while reading serial port\n");}
	else printf("Serial port read successful\n");
	close(file);
	printf("Serial port closed\n");
};

bool readMessage(int file, unsigned int nCountMax)
{
    int nbCharToRead;
    int i;
    
    if (file != 0)
    {
	    i = 0;  
	    char message[100];
	    char data[100];
		while (i<nCountMax && data != ".")
		{
			printf("\n%d\n",read(file,data,1));
		    if (read(file,data,1) == -1)
		    {
			    printf("reception error\n");
			    printf("errno = %d\n", errno);
			    return false;
		    }
		    else
		    {   
			    message[i] = *data;
			    printf("%c", message[i]);
			    i++;
		    }
		}
	    message[i] = 0;
	    return true;
    }
};


Mais ça ne fonctionne pas, il me sors "reception error" correspondant et le code errno est 11, c'est à dire "Try again" ...

Pouvez-vous m'aider svp ?

Merci d'avance !
A voir également:

11 réponses

ABouras Messages postés 26 Date d'inscription vendredi 16 novembre 2012 Statut Membre Dernière intervention 7 décembre 2012
20 nov. 2012 à 17:25
Mon programme est le suivant :
bool readMessage(int file, unsigned int nCountMax)
{
    int i;
    size_t nbytes;
	ssize_t bytes_read;
 
    if (file != -1)
    {
	    i = 0;  
	    char message[100];
	    char data[100];
		while (i<nCountMax && data != ".")
		{
		    if (read(file, data, 1) == -1)
		    {
			    printf("reception error\n");
			    printf("code errno = %d\n", errno);
			    return false;
		    }
		    else
		    {   
				nbytes = sizeof(data);
				bytes_read = read(file, data, nbytes);
			    message[i] = *data;
			    printf("%c", message[i]);
			    i++;
		    }
		}
	    message[i] = 0;
	    return true;
    }
};


Cette fois-ci, plus d'erreurs, mais les caractères affichés sont étranges :
$$$$QUC
U$C
$$$$JQMJ'	J$Cz(HSQ'Q'y
UKUNiQUMJ


les dollars $$$$ sont représentés par des carrés contenant 4 chiffres, bref .... je rappel que je voudrais avoir :
CHARS : 15931    SENTENCES = 0    CHECKSUM : 58
Heading : 55    Tilt: -46    Roll:2
CHARS : .....


J'ai essayé en mettant %c, %d, %x dans le printf, mais évidemment aucun ne fonctionne correctement ...

Merci !
0
totof31 Messages postés 163 Date d'inscription lundi 29 octobre 2012 Statut Membre Dernière intervention 30 avril 2013 74
20 nov. 2012 à 17:46
Salut,

Je ne comprends pas très bien :
bytes_read = read(file, data, nbytes);
retourne bytes_read caractères (au maximum nbytes)

Avec :
message[i] = *data;
printf("%c", message[i]);

tu ne lis et n'affiches qu'un seul caractère !

Il faut regarder la valeur retournée par bytes_read. Il ne faut donc pas faire :
if (read(file, data, 1) == -1)
mais :
bytes_read = read(file, data, nbytes); // A UN SEUL ENDROIT
if (bytes_read...
else...
0
totof31 Messages postés 163 Date d'inscription lundi 29 octobre 2012 Statut Membre Dernière intervention 30 avril 2013 74
20 nov. 2012 à 17:58
Autre chose : tu ne dois pas faire
data != "."
data est le nom d'un tableau (pointeur à l'adresse du tableau), on ne peut pas le comparer à un caractère.
0
ABouras Messages postés 26 Date d'inscription vendredi 16 novembre 2012 Statut Membre Dernière intervention 7 décembre 2012
21 nov. 2012 à 09:38
Ok, maintenant j'ai le code suivant :

#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include "serial_port.h"

void read_Serial_Port(const char* DEVICE_PORT)
{
    int file;
    struct termios options;
    unsigned int nCountMax = 60;
    bool b;

    file = open(DEVICE_PORT, O_RDONLY | O_NOCTTY | O_NDELAY);

    if(file == -1){perror("Unable to open the serial port\n");}
    printf("Serial port open successful !\n");

    tcgetattr(file, &options);          
    cfsetispeed(&options, B9600);                   
    cfsetospeed(&options, B9600);                   
    options.c_cflag |= (CLOCAL | CREAD);              
    options.c_cflag |= PARENB;                                          
    options.c_cflag |= PARODD;                                          
    options.c_cflag &= ~CSIZE;                      
    options.c_cflag |= CS8;                                             


    printf("Reading serial port ...\n\n"); 
    b = readMessage(file, nCountMax);

    if (b == 0){printf("Error while reading serial port\n");}
    else printf("\nSerial port read successful\n");

    close(file);
    printf("Serial port closed\n");
};

bool readMessage(int file, unsigned int nCountMax)
{
    int i;
    size_t nbytes;
    ssize_t bytes_read;

    if (file != -1)
    {
        i = 0;  
        char message[100] = {0};
        char data[1] = "";
        nbytes = sizeof(data);

        while (i<nCountMax)
        {
            bytes_read = read(file, data, nbytes);
            if (bytes_read < 0)
            {
                printf("read failed, errno code = %d [%s]\n", errno, strerror(errno));
                return false;
            }
            else
            {   
                message[i] = *data;
                printf("%c", message[i]);
                i++;
            }
        }
        message[i] = 0;
        return true;
    }
}


Et j'obtient le code errno 11 [Resource temporarily unavailable] ... pourquoi ?

Merci.
0
totof31 Messages postés 163 Date d'inscription lundi 29 octobre 2012 Statut Membre Dernière intervention 30 avril 2013 74
21 nov. 2012 à 10:08
Je ne me focaliserais pas trop sur le cas d'erreur pour l'instant. Attention également à ton test :
- si bytes_read est nul, tu affiches quand même un caractère...
- si bytes_read est > à 1, tu n'affiches qu'un seul caractère alors que tu en as dépilés plusieurs

Avec un minimum de changements, je te suggère d'essayer le code suivant :
bytes_read = read(file, data, 1);
if (bytes_read > 0)
{   
    message[i] = *data;
    printf("%c", message[i]);
    i++;
}


et laisse tomber errno pour l'instant. Tu pourras toujours sortir avec un ctrl-C si ça ne marche pas.

Si ça marche, tu pourras améliorer en faisant un truc du genre :
bytes_read = read(file, data, nbytes);
if (bytes_read > 0)
{
   for (j=0;j<bytes_read;j++) printf("%c", data[j]);
   i+=bytes_read;
...


Autres choses :
- tu ne donnes pas le code qui appelle read_Serial_Port
- tu fais bien un tcgetattr pour modifier les paramètres de com, mais tu dois ensuite enregistrer les valeurs modifiées avec tcsetattr (est-ce bien nécessaire d'ailleurs, d'où viennent ces valeurs/ce code ? Tu utilises un périphérique USB qui émule un port série, il n'est pas toujours nécessaire dans ce cas de changer la vitesse de com qui est potentiellement fictive)
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
ABouras Messages postés 26 Date d'inscription vendredi 16 novembre 2012 Statut Membre Dernière intervention 7 décembre 2012
Modifié par ABouras le 21/11/2012 à 10:23
Merci pour ta réponse, en faisant la première modif que tu me proposes :
bool readMessage(int file, unsigned int nCountMax) 
{ 
    int i; 
    size_t nbytes; 
 ssize_t bytes_read; 
     
    if (file != -1) 
    { 
     i = 0;   
     char message[100] = {0}; 
     char data[1] = ""; 
     nbytes = sizeof(data); 
      
  while (i<nCountMax) 
  { 
   bytes_read = read(file, data, 1); 
      if (bytes_read >0) 
      {    
       message[i] = *data; 
       printf("%c", message[i]); 
       i++; 
      } 
  } 
     message[i] = 0; 
     return true; 
    } 
}

J'obtiens sur le prompteur ceci :
Start read_Serial_Port function ...
Serial port open successful !
Reading serial port ...

CJ*$	CzQC*H
Q$U$u[-CJQ$U$[CJ
CJQ$U$0

S
SCJMQ$U$
Serial port read successful
Serial port closed


Mais les caractères sont incompréhensibles ...
0
totof31 Messages postés 163 Date d'inscription lundi 29 octobre 2012 Statut Membre Dernière intervention 30 avril 2013 74
21 nov. 2012 à 10:32
Alors essaie tout de même de jouer sur la vitesse de com :
- fait bien un tcsetattr()
- et si ça ne marche toujours pas, essaie d'autres vitesses standards
0
totof31 Messages postés 163 Date d'inscription lundi 29 octobre 2012 Statut Membre Dernière intervention 30 avril 2013 74
21 nov. 2012 à 10:33
pardon, je viens de voir le tcsetattr
0
totof31 Messages postés 163 Date d'inscription lundi 29 octobre 2012 Statut Membre Dernière intervention 30 avril 2013 74
21 nov. 2012 à 10:38
Du coup, peux-tu essayer en commentant le tcsetattr ? Quand tu fais screen /dev/... tu ne changes pas les paramètres par défaut...
Peux-tu aussi donner le code d'appel de read_serial_port STP ?!
0
ABouras Messages postés 26 Date d'inscription vendredi 16 novembre 2012 Statut Membre Dernière intervention 7 décembre 2012
21 nov. 2012 à 10:42
Non toujours pas, quelque soit la vitesse en Baud, je reçois ces caractères bizzares.

Autre chose, tu me demandais l'utilisation de la fonction read_Serial_Port(const char* DEVICE_PORT), j'ai juste fais un main() :
int main()
{
	const char* DEVICE_PORT = "/dev/ttyUSB0";
	
	printf("Start read_Serial_Port function ...\n");
   	read_Serial_Port(DEVICE_PORT);
}


De plus, je remarque qu'en faisant cat /dev/ttyUSB0, j'obtiens les même caractères bizarres, mais si je fais un screen /dev/ttyUSB0, j'obtiens les bonnes données ... Comment faire pour afficher correctement mes données comme avec la commande screen au lieu de cat ?
0
ABouras Messages postés 26 Date d'inscription vendredi 16 novembre 2012 Statut Membre Dernière intervention 7 décembre 2012
Modifié par ABouras le 21/11/2012 à 11:11
C'est bon ça marche !!!!!!!!!!!!!!!!!! merci beaucoup !!!!!!!!!

Dernière chose, j'aimerais recevoir les données :
CHARS : 15931    SENTENCES = 0    CHECKSUM : 58   
Heading : 55    Tilt: -46    Roll:2   
CHARS : .....

de manière régulière, bloc par bloc. Là je reçois genre une seule fois, 60 caractères d'un coup (j'ai mis nCountMax = 60 dans la fonction pour essayer) :
: -47 Roll: 1   
CHARS : 3CHARS : 3809  SENTENCES : 0  CHECKSU


puis si je réexecute :
t: -46 Roll: 1   
CHARS : CHECKSUM : 53   
Heading: 53 Tilt: -46


Bref ... comment faire pour recevoir les données régulièrement ? Il faudrait que j'affiche les bloc en commençant par CHARS, puis que le bloc s'arrête sur le nombre de Roll, ensuite il recommence sur CHARS, ainsi de suite ...

Ensuite j'essayerai d'écrire ces données dans un fichier texte proprement de cette manière :
CHARS : 15931    SENTENCES = 0    CHECKSUM : 58   
Heading : 55    Tilt: -46    Roll:2  
CHARS : 15931    SENTENCES = 1    CHECKSUM : 59   
Heading : 55    Tilt: -46    Roll:1  
CHARS : 15931    SENTENCES = 0    CHECKSUM : 58   
Heading : 54    Tilt: -45    Roll:1  
...
0
totof31 Messages postés 163 Date d'inscription lundi 29 octobre 2012 Statut Membre Dernière intervention 30 avril 2013 74
21 nov. 2012 à 11:19
Cool !
Qu'as-tu fait pour que ça marche finalement ?
Pour afficher les blocs il faut repérer les séquences qui t'intéressent. Il y a plusieurs façons de faire, essaie de regarder les fonctions strstr ou strtok ou sscanf...
0
ABouras Messages postés 26 Date d'inscription vendredi 16 novembre 2012 Statut Membre Dernière intervention 7 décembre 2012
21 nov. 2012 à 11:30
Ok je vais regarder ça ... Voici mon code :
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include "serial_port.h"

void read_Serial_Port(const char* DEVICE_PORT)
{
	int file;
	struct termios options;
	unsigned int nCountMax = 60;
	bool b;
	
	file = open(DEVICE_PORT, O_RDONLY | O_NOCTTY | O_NDELAY);
	
	if(file == -1){perror("Unable to open the serial port\n");}
	printf("Serial port open successful !\n");
	  
	tcgetattr(file, &options); 			
	cfsetispeed(&options, B9600); 					
	cfsetospeed(&options, B9600); 					
	options.c_cflag |= (CLOCAL | CREAD); 			  
	options.c_cflag |= PARENB; 					
	options.c_cflag |= PARODD; 						 					
	options.c_cflag &= ~CSIZE; 						
	options.c_cflag |= CS8;			
	 
	printf("Reading serial port ...\n\n"); 
	b = readMessage(file, nCountMax);
	
	if (b == 0){printf("Error while reading serial port\n");}
	else printf("\nSerial port read successful\n");
	
	close(file);
	printf("Serial port closed\n");
};

bool readMessage(int file, unsigned int nCountMax)
{
    int i,j;
	ssize_t bytes_read;
    
    if (file != -1)
    {
	    i = 0;
	    char data[1] = "";
	    
		while (i<nCountMax)
		{
			bytes_read = read(file, data, sizeof(data));
			if (bytes_read > 0)
			{
			   	printf("%c", data[0]);
			   	i += bytes_read;
			}
		}
	    return true;
    }
};


D'ailleurs il n'y a pas une fonction pour compter le nombre de caractère reçu en un seul paquet ? parce que moi pour l'instant j'utilise à la main nCountMax = 60 ce qui m'affiche exactement 60 caratères, mais j'ai pris cette valeur pif juste pour tester ...
0
totof31 Messages postés 163 Date d'inscription lundi 29 octobre 2012 Statut Membre Dernière intervention 30 avril 2013 74
21 nov. 2012 à 15:00
Si, c'est justement la fonction read : elle retourne au maximum sizeof(data), mais ce qui dans ton cas vaut 1. Essaie :
char data[1000];
int j;

bytes_read = read(file, data, sizeof(data));
if (bytes_read > 0)
{
for (j=0; j < bytes_read; j++)
printf("%c", data[j]);
}

Ton problème, c'est que des données sont envoyées en permanence, donc pas facile de savoir si tu as tout reçu ou pas, ou si tu commences à recevoir de nouvelles données, etc.
Si le rythme d'envoi des données est suffisamment lent, tu peux éventuellement utiliser un compteur qui s'incrémente à chaque fois que bytes_read est <= 0. Au delà d'une certaine valeur, tu peux ainsi savoir que tu as atteint la fin d'un bloc
0