Il est aujourd’hui possible d’émuler un périphérique USB HID avec les carte Arduino UNO grâce a au framework LUFA. Néanmoins cela nécessite de flasher la carte et donc un programmateur externe et cela conduit également a la perte du bootloader Arduino.
Des informations sur le framework LUFA ICI et un exemple d’utilisation avec une Arduino Uno ICI
La nouvelle carte Arduino Leonardo possède également une gestion USB HID. Celle-ci est intégrée dans l’IDE. Nous allons l’utiliser pour communique avec serveur Raspberry Pi fonctionnant sous Raspbian. Cela permet de créer des périphériques HID sans devoir flasher le firmeware comme c’est le cas pour la librairie LUFA et d’utilise l’IDE de DEV Arduino.
Un thread intéressant à propos de LUFA et l’Arduino Leonardo :
It’s disappointing to see Arduino move away from LUFA in only one generation of hardware
revisions, but I understand that they need to more tightly integrate the USB code with their custom platform to make it more useful to the masses, and portable to the new expanded range of Arduino architectures.
On peut donc penser qu’il y aura de plus en plus de projet USB grâce à la nouvelle génération de carte Arduino Leonardo. Il existe déjà quelque exemple dans l’IDE 1.01 (dans le menu Fichier -> Exemple -> Leonardo) pour la gestion d’un clavier et d’une souris.
Je vais utiliser un des exemple “clavier” et la lib libusb pour essayer de communiquer entre mon raspberry pi et mon arduino Leonardo. Voilà le code Leonardo qui se contente de faire clignoter une LED s’il reçoit qq chose sur le port USB :
/* Blink LED when receive something from USB */ //Define the pin number const int _OutPinLedTest = 7; const int PIN_BOUTON_ON = 4; const int PIN_BOUTON_OFF = 3; int _ledState=0; void setup() { //defined input button pinMode(_OutPinLedTest,OUTPUT); pinMode(PIN_BOUTON_ON,INPUT); pinMode(PIN_BOUTON_OFF,INPUT); //Active resistor as pull up digitalWrite(PIN_BOUTON_ON,HIGH); digitalWrite(PIN_BOUTON_OFF,HIGH); // open the serial port: Serial.begin(9600); // initialize control over the keyboard: Keyboard.begin(); } void flashPin(int pin, int times, int wait) { for (int i = 0; i < times; i++) { digitalWrite(pin, HIGH); delay(wait); digitalWrite(pin, LOW); if (i + 1 < times) { delay(wait); } } } void switchLedStatus(int pin) { if(_ledState == 0) { digitalWrite(pin, HIGH); _ledState = 1; delay(100); }else{ digitalWrite(pin, LOW); _ledState = 0; delay(100); } } void loop() { // check for incoming serial data: if (Serial.available() > 0) { // read incoming serial data: char inChar = Serial.read(); // Type the next ASCII value from what you received: flashPin(_OutPinLedTest, 10, 600); //Keyboard.write(inChar+1); } //Read button status int aInputDigitalValue = digitalRead(PIN_BOUTON_ON); //Reset the counter if button is press if (aInputDigitalValue == LOW) { flashPin(_OutPinLedTest, 10, 600); } aInputDigitalValue = digitalRead(PIN_BOUTON_OFF); //Reset the counter if button is press if (aInputDigitalValue == LOW) { switchLedStatus(_OutPinLedTest); } }
Cote Raspberry Pi avant de connecter la carte :
pi@raspberrypi ~ $ lsusb Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 001 Device 002: ID 0424:9512 Standard Microsystems Corp. Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. Bus 001 Device 007: ID 05e3:0608 Genesys Logic, Inc. USB-2.0 4-Port HUB Bus 001 Device 008: ID 04f2:1013 Chicony Electronics Co., Ltd
Après connexion avec la Carte Leonardo (sur un HUB USB externe) :
pi@raspberrypi ~ $ lsusb Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 001 Device 002: ID 0424:9512 Standard Microsystems Corp. Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. Bus 001 Device 007: ID 05e3:0608 Genesys Logic, Inc. USB-2.0 4-Port HUB Bus 001 Device 008: ID 04f2:1013 Chicony Electronics Co., Ltd Bus 001 Device 010: ID 2341:8036 Arduino SA
La carte Leonardo est détecte ! Voyons voir dans le détail :
Bus 001 Device 010: ID 2341:8036 Arduino SA Couldn't open device, some information will be missing Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 0 (Defined at Interface level) bDeviceSubClass 0 bDeviceProtocol 0 bMaxPacketSize0 64 idVendor 0x2341 Arduino SA idProduct 0x8036 bcdDevice 1.00 iManufacturer 1 iProduct 2 iSerial 0 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 100 bNumInterfaces 3 bConfigurationValue 1 iConfiguration 0 bmAttributes 0x80 (Bus Powered) MaxPower 500mA Interface Association: bLength 8 bDescriptorType 11 bFirstInterface 0 bInterfaceCount 2 bFunctionClass 2 Communications bFunctionSubClass 2 Abstract (modem) bFunctionProtocol 1 AT-commands (v.25ter) iFunction 0 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 1 bInterfaceClass 2 Communications bInterfaceSubClass 2 Abstract (modem) bInterfaceProtocol 0 None iInterface 0 CDC Header: bcdCDC 1.10 CDC Call Management: bmCapabilities 0x01 call management bDataInterface 1 CDC ACM: bmCapabilities 0x06 sends break line coding and serial state CDC Union: bMasterInterface 0 bSlaveInterface 1 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x0010 1x 16 bytes bInterval 64 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 1 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 10 CDC Data bInterfaceSubClass 0 Unused bInterfaceProtocol 0 iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x02 EP 2 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x83 EP 3 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 0 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 2 bAlternateSetting 0 bNumEndpoints 1 bInterfaceClass 3 Human Interface Device bInterfaceSubClass 0 No Subclass bInterfaceProtocol 0 None iInterface 0 HID Device Descriptor: bLength 9 bDescriptorType 33 bcdHID 1.01 bCountryCode 0 Not supported bNumDescriptors 1 bDescriptorType 34 Report wDescriptorLength 101 Report Descriptors: ** UNAVAILABLE ** Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x84 EP 4 IN bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 1
Pour mieux comprendre à quoi correspondent ces informations vous pouvez jeter à la présentation de la norme USB disponible ICI. Le schéma suivant permet de voir les différents “composants” d’un périphérique USB :
Et si on adapte a l’Arduino Leonardo :
Si ma mémoire est bonne (la dernière fois que j’ai utilisé la lib libusb été à l’époque où j’utilisai les microcontroleur PIC avec une communication Linux USB – PIC 18f4550) il nous faut ces 2 informations :
idVendor 0x2341 Arduino SA idProduct 0x8036
Cote Raspbian
J’ai trouvé un vieux code C++ que j’utilisai pour communiquer avec les PIC 18f4550 qui fonctionne avec la lib libusb. On doit donc commencer par l’installer :
pi@raspberrypi ~/USB_Leonardo $ sudo apt-get install libusb-1.0-0-dev
Ensuite….un petit tour sur la doc de la lib disponible ICI pour vérifier les dernières mise a jour/info. Ensuite j’ai mis à jour mon vieux programme (ca n’a pas bcp change en deux…) pour que l’utilisateur puisse changer d’interface et de configuration sans devoir tout recompiler. Voilà le programme :
#include <errno.h> #include <signal.h> #include <string.h> #include <stdio.h> //C++ migration #include <iostream> #include <stdlib.h> #include <libusb-1.0/libusb.h> #define VERSION "0.1.0" #define VENDOR_ID 9025 //CWA #define LINUX #define PRODUCT_ID 32822 // HID Class-Specific Requests values. See section 7.2 of the HID specifications #define HID_GET_REPORT 0x01 #define HID_GET_IDLE 0x02 #define HID_GET_PROTOCOL 0x03 #define HID_SET_REPORT 0x09 #define HID_SET_IDLE 0x0A #define HID_SET_PROTOCOL 0x0B #define HID_REPORT_TYPE_INPUT 0x01 #define HID_REPORT_TYPE_OUTPUT 0x02 #define HID_REPORT_TYPE_FEATURE 0x03 #define CTRL_IN LIBUSB_ENDPOINT_IN|LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE #define CTRL_OUT LIBUSB_ENDPOINT_OUT|LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE const static int PACKET_CTRL_LEN=2; const static int PACKET_INT_LEN=2; const static int INTERFACE=2; const static int ENDPOINT_INT_IN=0x81; /* endpoint 0x81 address for IN */ const static int ENDPOINT_INT_OUT=2; /* endpoint 1 address for OUT */ const static int TIMEOUT=5000; /* timeout in ms */ void bad(const char *why) { fprintf(stderr,"Fatal error> %s\n",why); exit(17); } static struct libusb_device_handle *devh = NULL; static int find_lvr_hidusb(void) { devh = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID); return devh ? 0 : -EIO; } int main(void) { //Store the interface number to use int aInterface; std::cout << "Please enter an interface ID (integer value): "; std::cin >> aInterface; std::cout << "The interface used will be : " << aInterface <<std::endl; //Store the configuration number to use int aConfiguration; std::cout << "Please enter an configuration ID (integer value): "; std::cin >> aConfiguration; std::cout << "The configuration used will be : " << aConfiguration <<std::endl; int r = 1; unsigned char *data = new unsigned char[1]; //data to write data[0]=0x00; //some dummy values //for (int compt=0; compt<63;compt++) //data[compt]=0x00; //some dummy values int actual; //used to find out how many bytes were written int actual2; //used to find out how many bytes were written r = libusb_init(NULL); if (r < 0) { fprintf(stderr, "Failed to initialise libusb\n"); exit(1); } printf("Successfully initialise libusb\n"); libusb_set_debug(NULL,4); r = find_lvr_hidusb(); if (r < 0) { fprintf(stderr, "Could not find/open LVR Generic HID device\n"); goto out; } printf("Successfully find the LVR Generic HID device\n"); #ifdef LINUX printf("LINUX - we need to see if a kernel driver is active on the interface.\nIf yes we have to detach it to be able to claim the interface\n"); r = libusb_kernel_driver_active(devh, aInterface); if (r==0) { printf("No Kernel Driver active on this interface\n"); } else { printf("Kernel Driver already active on this interface - we have to detach it to be able to claim the interface\n"); r = libusb_detach_kernel_driver(devh, aInterface); if (r < 0) { fprintf(stderr, "libusb_detach_kernel_driver error %d\n", r); goto out; } printf("Kernel Driver detach\n"); } #endif /*r = libusb_reset_device(devh); if (r < 0) { fprintf(stderr, "libusb_set_configuration error %d\n", r); goto out; } printf("Successfully reset usb \n");*/ r = libusb_set_configuration(devh, aConfiguration); if (r < 0) { fprintf(stderr, "libusb_set_configuration error %d\n", r); goto out; } printf("Successfully set usb configuration 1\n"); r = libusb_claim_interface(devh, aInterface); if (r < 0) { fprintf(stderr, "libusb_claim_interface error %d\n", r); goto out; } printf("Successfully claimed interface\n"); r = libusb_bulk_transfer(devh, ENDPOINT_INT_OUT, data, 1, &actual2, 0); if(r == 0 && actual2 == 1) //we wrote the 4 bytes successfully { fprintf(stderr, "Successfully perform bulk transfer \n"); } else { fprintf(stderr, "libusb_bulk_transfer error %d\n", r); goto out; } //r = libusb_bulk_transfer(devh, ENDPOINT_INT_OUT, data, 64, &actual, 0); r = libusb_interrupt_transfer(devh, ENDPOINT_INT_OUT, data, 1, &actual, 0); if(r == 0 && actual == 1) //we wrote the 4 bytes successfully { fprintf(stderr, "Successfully perform interupt transfer \n"); } else { fprintf(stderr, "libusb_interrupt_transfer error %d\n", r); goto out; } libusb_release_interface(devh, 0); out: // libusb_reset_device(devh); libusb_close(devh); libusb_exit(NULL); return r >= 0 ? r : -r; }
Voilà ce que ça donne au final :
pi@raspberrypi ~/USB_Leonardo $ sudo ./test Please enter an interface ID (integer value): 1 The interface used will be : 1 Please enter an configuration ID (integer value): 1 The configuration used will be : 1 Successfully initialise libusb Successfully find the LVR Generic HID device LINUX - we need to see if a kernel driver is active on the interface. If yes we have to detach it to be able to claim the interface No Kernel Driver active on this interface Successfully set usb configuration 1 Successfully claimed interface Successfully perform bulk transfer [timestamp] [threadID] facility level [function call] <message> -------------------------------------------------------------------------------- [ 0.000000] [000007ca] libusbx: error [submit_bulk_transfer] submiturb failed error -1 errno=22 libusb_interrupt_transfer error -1
et une led qui clignote 😉
Pingback: Site Web Jquery Mobile | Djynet
Pingback: Merge ! | Djynet