Projet

Général

Profil

Projet interface Python Usb à partir d'un module FT245 » Historique » Révision 9

Révision 8 (Anonyme, 29/06/2013 19:52) → Révision 9/27 (Anonyme, 29/06/2013 23:14)

h1. Projet interface Python Usb à partir d'un module FT245 

 _Statut du wiki : *En cours de rédaction...*_ 

 {{>toc}} 

 h2. Introduction 

 L'objectif de ce projet est de réaliser une bibliothèque de fonctions multiplateforme en Python permettant de communiquer avec un périphérique en USB via la puce FTDI 245RL. Cette bibliothèque doit pouvoir être utilisée avec le même code Python sur les principaux systèmes d'exploitations du marché (Windows, Linux, Mac OS X et Android), en fournissant une couche d'abstraction des accès aux différents pilotes correspondants. 

 Ce projet a été réalisé durant le premier semestre 2013, dans le cadre de notre première année d'étude à l'ISIMA. Il a été suivi par M. Jacques Laffont et M. Clément Jacob, que nous souhaitons tous deux remercier de nous avoir proposé ce projet et de nous avoir aidé à le réaliser. 

 h2. Systèmes supportés 

 La bibliothèque que nous avons réalisée est conçue pour fonctionner sur les systèmes suivants : 

 * Windows XP, Vista, 7 et 8 (32 et 64 bits) 
 * Android (version 3.1 et ultérieures), sur les terminaux compatibles avec le mode USB Host (également appelé USB On The Go : permet l'utilisation de périphériques USB externes) 
 * Linux (32 et 64 bits) 
 * Mac OX X (version 10.5 et ultérieures) 

 Toutefois, en raison du matériel à notre disposition, nous n'avons pu tester la bibliothèque que sur Windows 7 (32 et 64 bits), Linux (32 bits), Android 4.1.2 et Mac OS X 10.8. 

 Les contraintes de support de cette bibliothèque sont directement liées à celles des pilotes FTDI sous-jacents.  
 Ainsi, pour Android par exemple, l'"API USB Host":http://developer.android.com/guide/topics/connectivity/usb/host.html, permettant de gérer et d’accéder à des périphériques sur une tablette ou un smartphone Android sans privilèges adminitrateur, n'est présente qu'à partir de la version 3.1 de ce système. FTDI fournit un pilote utilisant cette API, ainsi qu'un autre pilote pour les versions précédentes. Toutefois, ce second pilote nécessite des privilèges administrateur (root), ce qui n'est pas possible par défaut sur les terminaux du marché. Nous avons donc choisi d'utiliser le pilote "moderne", utilisant l'API, ce qui restreint quelques peu la compatibilité de la bibliothèque. 
 De même, les versions de Mac OS supportées sont celles compatibles par le pilote FTDI associé. 


 h2. Structure externe et documentation des fonctions de la bibliothèque 

 Du point de vue de son utilisateur, la bibliothèque possède une structure très simple : toutes les méthodes utiles sont regroupées dans une seule et unique classe, portant le même nom que la bibliothèque elle-même (_Xftdi_). Une seconde classe existe toutefois : la classe _XftdiError_. Il s'agit d'une classe d'exception qui peut être levée par toutes les méthodes de la bibliothèque lorsque le code de retour d'une des fonctions du pilote FTDI indique une erreur. Cette exception possède un attribut _errorcode_ qui contient le code de retour de la fonction du pilote ayant échoué, ainsi qu'un attribut _message_ qui peut contenir une description textuelle de l'erreur. 

 La structure de la bibliothèque, du point de vue d'un développeur l'utilisant, peut ainsi être résumé par le schéma suivant : 

 p=. !xftdi-uml-externe.png! 

 p=. _Aspect extérieur de la bibliothèque_ 

 La liste ci-dessous documente succinctement les méthodes publiques de la bibliothèque. Ces méthodes portent souvent le même nom que les fonctions du pilote qu'elles utilisent. Pour une documentation plus détaillée sur une de ces méthodes, nous vous recommandons de vous reporter à son code source (docstring), ainsi qu'à la "documentation des fonctions du pilote FTDI":http://www.ftdichip.com/Support/Documents/ProgramGuides/D2XX_Programmer%27s_Guide%28FT_000071%29.pdf. 

 * *deviceCount()* : Retourne le nombre de périphériques FTDI connectés 
    
 * *listDevices()* : Retourne la liste des numéros de série des périphériques FTDI connectés 

 * *isAvailable()* : Retourne Vrai si au moins un périphérique FTDI est connecté. 

 * *isOpen()* : Retourne Vrai si un périphérique FTDI est ouvert. 
        
 * *openByIndex(_index_)* : Ouvre un périphérique FTDI identifié par son index. Il est recommandé de vérifier que l'ouverture a fonctionné en appelant la méthode _isOpen()_ après un _open[...]()_ 

 * *openBySerialNumber(_serialNumber_)* : Ouvre un périphérique FTDI identifié par son numéro de série. 
        
 * *write(_data_)* : Envoi de données vers le périphérique ouvert. Les données sont une chaînes de caractères non signés. La méthode retourne le nombre d'octets effectivement envoyés. 
    
 * *read(_size_)* : Reçoit un paquet de données de taille maximale indiquée en paramètre (_size_) depuis le périphérique précédemment ouvert. Les données sont retournées sous la forme d'une chaînes de caractères non signés.  
    
 * *getQueueStatus()* : Retourne le nombre d'octets disponibles en lecture. Un appel à la méthode _read()_ demandant au plus ce nombre d'octets permet de retourner les données immédiatement (sans blocage). 

 * *flush(_input=True_, _output=True_)* : Efface les données en attente dans les buffers de lecture (si input est Vrai) et/ou d'écriture (si output est Vrai). Retourne Vrai si l'opération a réussi, Faux sinon. 
    
 * *resetDevice()* : Envoi un ordre de réinitialisation du périphérique. Retourne Vrai si l'opération a réussi, Faux sinon. 
    
 * *closeDevice()* : Ferme la communication avec un périphérique. 

 h2. Structure interne de la bibliothèque 

 La structure présentée précédemment n'est pas réellement la structure complète de la bibliothèque, mais plutôt une "façade" présentée à son utilisateur, grâce à un jeu d'importation. Cela permet d'avoir une interface unique quelque soit la plateforme utilisée. 

 La structure réelle de la bibliothèque est plus proche du diagramme ci-dessous, qui présente les trois principales classes de la bibliothèque et la classe d'exception, ainsi que l'arborescence des fichiers sources de la bibliothèque et la correspondance entre chaque classe et le fichier dans lequel elle est implémentée (association par rectangle de couleur identique) : 

 p=. !xftdi-uml-interne-arbo.png! 

 p=. _Structure interne du code et arborescence des fichiers de la bibliothèque_ 

 En réalité, la bibliothèque est donc composée d'une classe de base, abstraite, nommée _XftdiBase_, qui décrit sans les implémenter l'ensemble des méthodes fournies par la bibliothèque. 

 Ces méthodes sont ensuite implémentées par deux classes filles : _XftdiPC_ et _XftdiAndroid_. La seconde est conçue pour les terminaux Android, et utilise le pilote Java D2XX fourni par FTDI sous la forme d'un Jar, via Pyjnius qui permet l'appel de ces méthodes Java depuis Python. Ce pilote est lui-même basé sur l'"API USB Host":http://developer.android.com/guide/topics/connectivity/usb/host.html fourni par Google au sein de son système d'exploitation à partir de sa version 3.1. Un second pilote existe pour les versions antérieures de ce système, mais il requiert le plus souvent des privilèges administrateurs sur le terminal (droits root), ce qui est souvent interdit par les constructeurs sous peine d'annulation de la garantie. Pour cette raison, nous avons choisi de ne pas utiliser ce pilote. 

 La première fonctionne indifféremment sur Windows, Linux ou Mac OS X. En effet, celle-ci est basée sur les pilotes natifs fournis par FTDI (_.dll_, _.so_ ou _.dylib_) qui fournissent tous la même API, et qui peuvent tous être utilisés avec ctypes et un code Python identique. Seul le chargement du pilote correspondant à la plateforme utilisée doit être traité de manière différente selon cette plateforme, ce qui est fait dans le fichier _ftdidrv.py_. 

 Le choix de l'une de ces deux classes est automatiquement effectué lors de l'importation de la bibliothèque grâce au code écrit dans le fichier spécial ___init__.py_. Ainsi, ce même fichier permet également de masquer cette structure plus complexe en la réduisant à celle présentée dans la section précédente. La classe de base permet quant à elle de garantir une interface commune quelle que soit la plateforme cible. 

 h2. Utilisation de la bibliothèque 

 h3. Utilisation basique 

 Cette abstraction de la structure interne de la bibliothèque permet de simplifier grandement son utilisation. Ainsi, le Le script suivant montre un exemple d'utilisation basique mais fonctionnel de la bibliothèque Xftdi : 
 <pre><code class="python"> 
 # Importation de la bibliothèque 
 from xftdi import Xftdi 

 # Instanciation de la classe 
 xftdi = Xftdi() 

 # Obtention de la liste des numéros de série 
 # des périphériques compatibles connectés 
 serials = xftdi.listDevices() 

 # Si il y a au moins un périphérique disponible 
 if len(serials) > 0: 
     # Ouvre le premier périphérique trouvé 
     # à partir de son numéro de série 
     xftdi.openBySerialNumber(serials[0]) 

     # Vérifie que l'ouverture a fonctionné 
     if xftdi.isOpen(): 
         # Envoi d'un message à ce périphérique 
         xftdi.write("Hello World") 
         # Lecture de sa réponse 
         reponse = xftdi.read(50) 
         # Fermeture du périphérique 
         xftdi.closeDevice() 

 </code></pre> 
 Pour que ce script fonctionne, il est bien évidemment nécessaire de placer le dossier de la bibliothèque (xftdi) dans le même dossier que le script lui-même, ou de modifier le path (_sys.path_) de sorte à ce que la librairie soit contenue dans un des dossiers énumérés dans cette variable d'environnement. 

 h4. Suppression des éléments inutiles en fonction de la plateforme ciblée 

 Comme nous l'avons montré dans la section précédente, la La bibliothèque est divisée conçu en deux blocs principaux : un premier destiné à fonctionner sur PC, un second pour les terminaux Android. Lors de la réalisation d'une application utilisant Xftdi et ciblant l'une ou l'autre de ces familles de plateformes, plateforme, il est possible de supprimer la partie complémentaire de la bibliothèque dans un but d'optimisation de l'espace disque occupé. 

 Pour cela, il suffit de supprimer le sous-dossier _pc_ du dossier _xftdi_ si votre application est destinée à des terminaux Android, ou le sous-dossier _android_ si votre application est destinée à Windows, Linux ou Mac. Cette manipulation est surtout utile dans le premier cas, où vous pouvez économiser près de 3 Mo pour une application destinée à des terminaux encore souvent limités en capacité de stockage. 

 Concernant la partie PC, il Il est également possible d'agir plus finement en supprimant les pilotes natifs non utilisés selon les systèmes d'exploitations PC ciblés. Par exemple, lorsque vous créer le package pour Windows, vous pouvez supprimer sans problème les pilotes pour Linux et Mac OS X présents dans les sous dossiers du dossier _xftdi/pc/native&#95;fdti&#95;drivers_ 

 h3. Modification de l’emplacement des drivers natifs 

 Les drivers FTDI natifs concernant la partie PC sont disposés par défaut dans des sous-dossiers précis de la bibliothèque. Les chemins de ces pilotes sont incrits "en dur" dans le code de la bibliothèque, relativement à l'emplacement de cette dernière. 

 Si, pour une raison quelconque, vous souhaitez modifier l'emplacement de ces pilotes, vous devez mettre à jour le code en indiquant le nouvel emplacement de chacun de ces pilotes *dans le fichier _constants.py_*, ainsi que le chemin du pilote Linux dans le fichier _build-ftdidrv.sh_ du dossier _ressources_. 

 h3. Particularité du fichier ftdidrv.py 

 Le fichier _ftdidrv.py_ (partie PC) a la particularité d'être généré automatiquement à l'aide du script bash _build-ftdidrv.sh_ (dossier _ressources_). *Il est donc important de ne pas le modifier manuellement*, car ces modifications seront écrasées lors de la prochaine exécution du script. 

 Ce fichier fournit une première couche d’abstraction des pilotes natifs fournis par FTDI. Il contient une classe FtdiDriver qui est chargée de charger le pilote PC adapté au système d'exploitation, et qui encapsule l'appel à chacune des fonctions de ce dernier en vérifiant le code de retour à l'aide d'un décorateur, et en levant une exception _FtdiError_ si nécessaire. 

 Toutes les fonctions exportées par le pilote sont présentes grâce à la génération automatique à partir d'une commande Bash : strings. Cette commande permet de lister les chaines de caractères présentes dans un binaire. Grâce à un filtre simple, il est possible d'obtenir la liste des fonctions disponibles (elles commencent toutes par FT_ ). A partir de cette liste, le script génère le code Python correspondant pour chacune des fonctions. 

 Si vous souhaitez modifier la première partie de ce fichier source (importations, déclaration de la classe, constructeur et méthode _loadDriver()_), vous devez le faire non pas dans le fichier lui même, mais dans le script bash que vous devez ensuite exécuter à nouveau pour mettre à jour le code Python. 

 h2. Création d'une application Kivy pour Android 

 Ce paragraphe détaille détail uniquement les étapes de création d'une application Kivy pour Android utilisant la bibliothèque xftdi. Pour des information plus générales concernant la création d'une application Kivy pour Android, veuillez vous reportez sur la documentation de Kivy. 

 * Créez une distribution Kivy : ./distribute.sh -m "pyjnius kivy" 
 * Copiez le fichier d2xx.jar dans le sous-dossier libs de la distribution 
 * Dans le sous-dossier src, créer le chemin suivant : fr/isima/xftdi/android. Copiez y le fichier DriverAdaptor.java 
 * Astuce : modif pour non fullscreen 
 * build 
 * adb install 

 h3. Modification de l’emplacement des drivers natifs 

 h3. Génération du code 

 h2. Application de démonstration : FTDI Speed Test 



 h2. Limitations et problèmes connus 

 h3. Linux 

 +Conflit avec le module ftdi_sio :+ Si ce dernier est chargé, le périphérique ne peut plus être ouvert avec l'application. (cf. README http://www.ftdichip.com/Drivers/D2XX/Linux/ReadMe-linux.txt, http://www.tincantools.com/wiki/OpenOCD_Troubleshooting:_Device_Not_Opened_%28Linux%29). Une solution simple consiste à blacklister ce module en ajoutant dans /etc/modprobe.d/blacklist (créer la fichier s'il n'existe pas) la ligne suivante : blacklist ftdi_sio. 

 +Permissions d'accès au périphériques :+ Si vous ne parvenez pas à ouvrir le prériphérique (exception DEVICE_NOT_FOUND, éventuellement accompagné du message libusb couldn't open USB device /dev/bus/usb/001/015: Permission denied. 
 libusb requires write access to USB device nodes. 
 Vous devez modifiez les règles de udev afin que le périphériques soit accessible à tous les utilisateurs Unix de l'application. 
 Pour cela, lancer la commande suivante dans un terminal administrateur :  
 <pre> 
 echo 'ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", MODE="0666"' >> /etc/udev/rules.d/80-ftdiusb.rules 
 </pre> 
 Ici, la ligne permet de donner les droits de lecture/écriture à tout le monde. D'autres options permettent de créer un groupe associé au périphérique par exemple. Pour plus d'informations, veuillez vous reporter sur le manuel de udev 

 +Application Android tuée quand on branche l'usb :+ vérifier la présence d'une fonction on_pause : http://kivy.org/docs/api-kivy.app.html#pause-mode 

 h2. Conclusion 

 h2. Ressources et liens utiles 

 * "Page de téléchargement des pilotes FTDI":http://www.ftdichip.com/Drivers/D2XX.htm 
 * "Documentation des pilotes FTDI pour les programmeurs":http://www.ftdichip.com/Support/Documents/ProgramGuides/D2XX_Programmer%27s_Guide%28FT_000071%29.pdf 
 * "Documentation de l'API USB Host d'Android":http://developer.android.com/guide/topics/connectivity/usb/host.html 
 * "Ticket de bug déposé sur le GitHub de Pyjnius concernant les paramètres de sortie":http://github.com/kivy/pyjnius/issues/58