Forum Programmation.c [flux USB] select ne fonctionne pas.

Posté par . Licence CC by-sa.
Tags : aucun
0
19
déc.
2017

Bonjour,

Je récupère un flux USB brute (sans aucun protocole) via un programme en C.
Le programme fonctionne correctement sans timeout.
Pour ajouter un timeout j'utilise la fonction "select()". Mais cela ne fonctionne pas, pourtant :
- La fonction "open()" fonctionne correctement et retourne 4
- Le timeout de select fonctionne correctement.
- Aucune erreur en retour de select (constamment un timeout)
- Malgré un flux USB entrant, "select()" ne détecte aucun caractère …

Je suis sous Ubuntu 16.04

Je vous envoie le programme.
J'ai déjà un peu d'expérience en C. Mais aucune sous linux, alors tout commentaire sera le bienvenue !

Merci !

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>

#define USB_RX_SIZE 0xFF

int main(int argc, char **argv)
{
    /* --- INITIALISATION --- */

    // ouverture du fichier
    FILE* usbfile = fopen("/dev/ttyUSB0", "r+");
    if(usbfile == NULL)
    {
        printf("fichier tty inexistant\n");
        return 0;
    }

    // tableau de récupération des données
    uint8_t usbRx[USB_RX_SIZE];
    int i;
    for(i = 0; i<USB_RX_SIZE; i++)
    {
        usbRx[i] = 0;
    }

    /* --- EMISSION --- */

    for(i=0; i<strlen(argv[1]); i++)
    {
        fputc(argv[1][i], usbfile);
    }

    fputc('\r', usbfile);
    fclose(usbfile);

    // init de select() pour le TO
    fd_set set;
    struct timeval timeoutValue;
    int rv_select;
    int usbfile_select = open("/dev/ttyUSB0", O_RDWR);
    FD_ZERO(&set);
    FD_SET(usbfile_select, &set);
    timeoutValue.tv_sec = 1;
    timeoutValue.tv_usec = 0;   

    /* --- RECEPTION --- */

    int idxRx = 0;
    int timeout = 0;
    do
    {
        rv_select = select(usbfile_select+1, &set, NULL, &set, &timeoutValue);
        timeoutValue.tv_sec = 1;
        timeoutValue.tv_usec = 0;
        printf("%d\n", rv_select);
        if(rv_select == -1)
        {
            printf("select error\n");
        }
        else if(rv_select == 0)
        {
            timeout = 1;
        }
        else
        {   
            printf("data to read\n");
            fread(&usbRx[idxRx], 1, 1, usbfile);
            //read(usbfile_select, &usbRx[idxRx], 1);
            idxRx++;
        }
    }while( (usbRx[idxRx-1] != '\n') && (idxRx < USB_RX_SIZE) && (timeout == 0) );

    if(idxRx == USB_RX_SIZE)
    {
        printf("buffer full\n");
    }
    else if(timeout == 1)
    {
        printf("timeout expire\n");
    }
    else if(usbRx[idxRx-1] == '\n')
    {
        usbRx[idxRx] = '\0';
        printf("%s\n", usbRx);
    }
    else
    {
        printf("error?\n");
    }

    return 0;
}
  • # double ouverture du fichier ?

    Posté par . Évalué à 0.

    Ça m’étonne qu'on puisse ouvrir simultanément un même fichier avec fopen() et open().

    Es-tu sûr que le open() a bien fonctionné et n'a pas renvoyé -1 ?
    ça expliquerai pourquoi la suite se passe mal.
    vu que usbfile_select+1 vaudrait 0, le select fonctionnerait en mode "tempo"

    Après, si c'est bien ça, je n'ai pas d'idée sur comment faire le timeout sur un fichier ouvert avec fopen().

    • [^] # Re: double ouverture du fichier ?

      Posté par . Évalué à 1.

      C'est ce que je me suis dit. C'est pourquoi je fait un
      fopen()

      fclose()

      open()

      J'ai bien vérifié le retour des fonctions et aucune erreur …

  • # Port serie

    Posté par (page perso) . Évalué à 3.

    Hum, de ce que je vois tu n'est pas du tout en train de lire un "flux usb" mais un port serie, sur bus USB certes mais ce n'est rien d'autre qu'un port serie normal (enfin modulo que ton adapteur serie<=>usb soit de bonne facture).

    Ne devrais tu pas configurer ton baud rate & compagnie avant tout du coup ?

    Pour le select as tu essayé d'augmenter le timeout ça pourrait etre pas mal aussi

    • [^] # Re: Port serie

      Posté par (page perso) . Évalué à 1.

    • [^] # Re: Port serie

      Posté par . Évalué à 1.

      Oui tu as bien raison c'est une com série via le port USB. Je me suis mal exprimé !

      Ceci dit si j'utilise fread() pour récupérer les données caractères après caractères ça fonctionne, alors ce n'est pas un problème de configuration. Par contre si le flux série s'arrête prématurément fread() va bloquer le programme, alors que select() permet d'utiliser un timeout.

      Je me demande si select() fonctionne sur un fichier de type ttyUSBx. Pourtant j'ai vu un exemple sur le net très proche de mon appli, c'était sur un fichier ttySx

      Le timeout d'une seconde est correct, du moins lorsque j'exécute le programme il y a bien une seconde d'attente avant de m'indiquer l'erreur timeout.

      Je regarderais tes liens ci-dessous ce soir … Merci !

      • [^] # Re: Port serie

        Posté par (page perso) . Évalué à 1.

        Je me demande si select() fonctionne sur un fichier de type ttyUSBx. Pourtant j'ai vu un exemple sur le net très proche de mon appli, c'était sur un fichier ttySx

        pas de raison, c'est un appel système géré par le kernel, cela va fonctionner quelque soit la source du flux.

        Le timeout d'une seconde est correct, du moins lorsque j'exécute le programme il y a bien une seconde d'attente avant de m'indiquer l'erreur timeout.

        Certes, mais est-ce que dans cet interval très court ton port série a vraiment eu le temps de bufferiser des données ?

  • # pourquoi select en particulier?

    Posté par . Évalué à 2.

    Une question, toute bête, mais pourquoi tu t'emmerdes avec select()?

    Ce n'est que m'on avis personnel, mais je trouve les codes qui utilisent select imbuvables, poll me semble tellement plus lisible, plus intuitif:

    • tu initialise un tableau qui contiens autant d'instances de pollfd que de fd à surveiller, ces structures contenant: le fd en question, un bitfield uniquement lu (donc, pas besoin de le réécrire après un appel, contrairement à select de mémoire) qui contiens les évènements à surveiller, un autre uniquement accédé en écriture. Accessoirement, le timeout est juste un entier en millisecondes, je trouve ça tellement plus simple.
    • tu le passes à poll.
    • tu récupères le nombre d'évènements et tu itères sur ton tableau pour voir quelles structures ont leur revent pas à 0
    • le tour est joué.

    Au niveau avantages:

    • pas besoin de rafraîchir les tableaux qui contiennent les files à surveiller
    • une seule structure pour tous les types d'évènements
    • pas de limitation hard-codée par le système (donc, pas de gâchis d'espace ni de limitations dans le nombre de descripteurs à surveiller)
    • pas de macros (donc des messages d'erreur plus simples à comprendre et en cadeau bonus une type safety un peu moins crade)
    • données mémoire plus compacte
    • on peut désactiver la surveillance sur un fd en lui affectant une valeur négative, tandis que select requiert uniquement des fd valides.

    Les inconvénients… je n'en connais pas. Ah, si, la doc indique que c'est un peu plus récent (POSIX.1-2001, contre une apparition de select dans 4.4BSD) mais franchement, ça fait quand même plus de 15 ans, ça devrait le faire sur la plupart des systèmes.

    Bon, tu fais ce que tu veux, mais ton bug est très probablement lié au fait que, justement, select ait une interface dégueulasse:

            rv_select = select(usbfile_select+1, &set, NULL, &set, &timeoutValue);

    Tu utilises 2 fois la même structure, du coup, select, ben, il écrit 2 fois dedans…

  • # Usage du select

    Posté par . Évalué à 1.

    Bonjour,

    select modifie les fd_set et les timeout, c'est pourquoi il faut les réinitialiser dans la boucle avant chaque appel.

    Pour une meilleur portabilité préférez pselect ou son équivalent ppoll.

    À noter : la résolution des timeouts est differente selon l'interface : poll en millisec, select en microsec, pselect et ppoll en nanosec.

    • [^] # Re: Usage du select

      Posté par . Évalué à 4.

      Pour une meilleur portabilité préférez pselect ou son équivalent ppoll.

      Dans le man:

      ppoll() is Linux-specific

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.