Site : tvaira.free.fr
Il est conseillé de lire ou de s’aider de ce document avant de poursuive.
Avant :
$ lsusb > lsusb-avant.txt
$ lsmod > lsmod-avant.txt
$ dmesg > dmesg-avant.txt
Brancher maintenant un périphérique USB.
Après :
$ lsusb > lsusb-apres.txt
$ lsmod > lsmod-apres.txt
$ dmesg > dmesg-apres.txt
Analyse :
$ diff lsusb-avant.txt lsusb-apres.txt
> Bus 002 Device 011: ID 0e8d:3329 MediaTek Inc.
$ diff dmesg-avant.txt dmesg-apres.txt
> [204555.128418] usb 2-1.4: new full-speed USB device number 10 using ehci-pci
> [204555.223664] usb 2-1.4: New USB device found, idVendor=0e8d, idProduct=3329
> [204555.223669] usb 2-1.4: New USB device strings: Mfr=3, Product=4, SerialNumber=0
> [204555.223672] usb 2-1.4: Product: GPS Receiver
> [204555.223674] usb 2-1.4: Manufacturer: MTK
> [204555.257427] cdc_acm 2-1.4:1.1: ttyACM0: USB ACM device
> [204555.257994] usbcore: registered new interface driver cdc_acm
> [204555.257997] cdc_acm: USB Abstract Control Model driver for USB modems and ISDN adapters
$ diff lsmod-avant.txt lsmod-apres.txt
> cdc_acm 26991 2
$ modinfo cdc_acm
filename: /lib/modules/3.8.0-44-generic/kernel/drivers/usb/class/cdc-acm.ko
alias: char-major-166-*
license: GPL
description: USB Abstract Control Model driver for USB modems and ISDN adapters
author: ...
srcversion: AD91845189110F8BEC40793
...
alias: usb:v0E8Dp3329d*dc*dsc*dp*ic*isc*ip*in*
alias: usb:v0E8Dp0003d*dc*dsc*dp*ic*isc*ip*in*
...
$ ls -l /dev/ttyACM0
crw-rw---- 1 root dialout 166, 0 sept. 18 16:24 /dev/ttyACM0
cdc_acm
(cf. lsmod et modinfo)cdc_acm
) a été chargé par le système (cf. dmesg et lsmod)ttyACM0
dans /dev
(cf.)Attention : le périphérique ne sera accessible en lecture/écriture que par l’utilisateur propriétaire root
et les membres du groupe dialout
.
Par défaut, le système applique la politique des droits d’accès rw-rw----
pour ce type de périphérique USB :
$ ls -l /dev/ttyACM0
crw-rw---- 1 root dialout 166, 0 sept. 18 16:24 /dev/ttyACM0
Ici, le périphérique sera accessible en lecture/écriture par l’utilisateur propriétaire root
et les membres du groupe dialout
. Tous les autres (other) utilisateurs auront aucun accès.
Pour modifier cette situation, vous avez les possibilités suivantes :
$ sudo chmod 666 /dev/ttyACM0
dialout
: $ sudo adduser toto dialout
Pour une gestion automatique, il faudra passer par udev
qui est maintenant le service qui prend en charge le répertoire /dev
.
$ ls -l /dev/ttyACM0
crw-rw---- 1 root dialout 166, 0 sept. 18 16:24 /dev/ttyACM0
$ sudo vim /etc/udev/rules.d/51-ttyusb.rules
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="3329", MODE="0666"
$ ls -l /dev/ | grep ACM
crw-rw-rw- 1 root dialout 166, 0 sept. 18 17:08 ttyACM0
Il est aussi possible de créer un lien symbolique au choix :
$ sudo vim /etc/udev/rules.d/51-ttyusb.rules
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="3329", MODE="0666", SYMLINK+="gps"
$ ls -l /dev/ | grep "\(ACM\|gps\)"
lrwxrwxrwx 1 root root 7 sept. 18 17:10 gps -> ttyACM0
crw-rw-rw- 1 root dialout 166, 0 sept. 18 17:10 ttyACM0
Ou de fixer le nom du fichier spécial :
$ sudo vim /etc/udev/rules.d/51-ttyusb.rules
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="3329", MODE="0666", NAME="ttyUSB1",
SYMLINK+="gps"
$ ls -l /dev/ | grep "\(gps\|USB\)"
lrwxrwxrwx 1 root root 7 sept. 18 17:12 gps -> ttyUSB1
crw-rw-rw- 1 root dialout 166, 0 sept. 18 17:12 ttyUSB1
On peut tester la réception de phrases NMEA 0183 avec la commande screen
(Ctrl-a k
pour sortir) ou avec picocom
(Ctrl-a Ctrl-x
pour sortir) :
$ screen /dev/ttyACM0 115200
$GPRMC,000936.799,V,0000.0000,N,00000.0000,E,,,060180,,*10
$GPVTG,,T,,M,,N,,K*4E
$GPGGA,000937.799,0000.0000,N,00000.0000,E,0,00,,,M,,,,0000*04
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPGSV,1,1,00*79
$GPRMC,000937.799,V,0000.0000,N,00000.0000,E,,,060180,,*11
$GPVTG,,T,,M,,N,,K*4E
$GPGGA,000938.799,0000.0000,N,00000.0000,E,0,00,,,M,,,,0000*0B
...
$ picocom -b 115200 /dev/ttyACM0
picocom v1.4
port is : /dev/ttyACM0
flowcontrol : none
baudrate is : 115200
parity is : none
databits are : 8
escape is : C-a
noinit is : no
noreset is : no
nolock is : no
send_cmd is : ascii_xfr -s -v -l10
receive_cmd is : rz -vv
Terminal ready
$GPGGA,064509.842,4405.2015,N,00457.8701,E,0,00,,,M,,,,0000*05
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPGSV,3,1,12,29,80,028,,31,61,278,,25,55,092,,21,29,178,*73
$GPGSV,3,2,12,26,28,296,,12,17,102,,02,14,041,,14,06,218,*74
$GPGSV,3,3,12,05,06,076,,20,03,123,,23,02,333,,16,02,290,*77
$GPRMC,064509.842,V,4405.2015,N,00457.8701,E,,,041015,,*1E
$GPVTG,,T,,M,,N,,K*4E
...
Remarque : 115200 est le débit de la “ligne” en bits/s.
Ou avec le logiciel cutecom
:
// Lit une trame NMEA 183 sur le port série virtuel
// Attention : vérifiez le nom du fichier de périphérique et la configuration du port série
/*************************************************************
* gcc -O2 -o nom_executable nom_source
**************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <sys/fcntl.h>
// On fixe la taille maximum à 128 octets
#define MAX 128
//#define DEBUG
int main(void)
{
int fd; // descripteur de fichier
char nomPeripherique[16] = "/dev/ttyACM0"; // nom du fichier special
struct termios termios_p; // parametres du port
char* buffer = (char *)NULL; // un buffer
char c = 0; // un caractère
int fini = 0; // fin de reception d'une trame
int i = 0, n = 0;
int debutTrame = 0; // debut de reception d'une trame
/* Ouverture de la liaison serie */
fd = open(nomPeripherique, O_RDWR | O_NOCTTY | O_NONBLOCK);
if ( fd == -1 )
{
perror("open"); // message d'erreur système
//printf("Erreur d'ouverture du peripherique !\n"); // message d'erreur client
exit(1);
}
fflush(NULL);
//printf("Peripherique ouvert avec succes !\n");
/* Lecture des parametres courants */
tcgetattr(fd, &termios_p);
/* On ignore les BREAK et les caracteres avec erreurs de parite */
termios_p.c_iflag = IGNBRK | IGNPAR;
/* Pas de mode de sortie particulier */
termios_p.c_oflag = 0;
/* Liaison a 9600 bps, 8 bits de donnees, pas de parite, lecture possible */
termios_p.c_cflag = B9600 | CS8 | CREAD | CLOCAL;
termios_p.c_cflag &= ~PARODD ;
termios_p.c_cflag &= ~PARENB;
/* Desactive le mode canonique */
termios_p.c_lflag = 0;
/* Caracteres immediatement disponibles */
termios_p.c_cc[VMIN] = 1;
termios_p.c_cc[VTIME] = 0;
/* Sauvegarde des nouveaux parametres */
if(tcsetattr(fd,TCSANOW,&termios_p)== -1)
{
perror("tcsetattr");
close(fd);
exit(1);
}
//printf("Sauvegarde de la nouvelle configuration avec succes !\n");
/* Lecture de MAX octets */
buffer = (char *)malloc((MAX+1) * sizeof(char)); // on alloue un buffer
i = 0;
debutTrame = 0;
do
{
n = read(fd, &c, 1);
#ifdef DEBUG
printf("read : %d -> 0x%02X ", n, (unsigned char)c);
#endif
// est-ce qu'on a lu 1 caractère ?
if(n == 1)
{
// est-ce le début d'une trame ?
if(c == '$')
{
debutTrame = 1;
}
// est-ce qu'on est entrain de lire une trame ?
if(debutTrame == 1)
{
// on copie le caractère lu dans le buffer
*(buffer + i) = c;
i++;
}
// est-ce que c'est la fin de la trame qu'on est entrain de lire ?
if (debutTrame == 1 && c == '\n')
{
fini = 1;
debutTrame = 0;
}
}
/*else
{
if(n == -1)
{
perror("read"); // message erreur système
//printf("Erreur de lecture\n"); // message client
//fini = 1;
}
else
{
printf("Aucune lecture !\n"); // message warning
}
}*/
}
while(i <= MAX && fini == 0);
*(buffer + i) = 0x00; // fin de chaîne (attention à i > MAX !)
printf("Trame lue :\n%s\n", buffer);
/* Fermeture du port série */
close (fd);
//printf("Fermeture du peripherique avec succes !\n");
free(buffer); // on libère la mémoire allouée
return 0;
}
$ gcc -O2 test-port-com-usb.c
$ ./a.out
Trame lue :
$GPZDA,001448.799,06,01,1980,,.0000,E,0,00,0,00,,,M,,,,0000*01