CleUSB : Différence entre versions

De troyesGEII
Aller à : navigation, rechercher
({{Vert|Fonctionnement du microcontrôleur}})
Ligne 501 : Ligne 501 :
 
{| class="wikitable"
 
{| class="wikitable"
 
|-
 
|-
! classe Com
+
! Partie communication avec l'interface
 
|-
 
|-
| classe gérant les protocoles de communication avec l'interface graphique
+
|
 
<small>
 
<small>
 
<source lang=c>
 
<source lang=c>
Ligne 520 : Ligne 520 :
 
<small>
 
<small>
 
<source lang=c>
 
<source lang=c>
 +
void change() {
 +
  byte* r;
 +
  Serial.readBytesUntil('\n', r, 15);
 +
  byte num = r[0] - '0';
 +
  //on stocke le numéro du couple(converti automatiquement en ascii par la liaison série)
 +
  // après l'avoir converti en valeur numérique
  
    void change() {
+
  char id[16];
      byte* r;
+
  for (int i = 0; i < 16; i++) id[i] = 0;
      Serial.readBytesUntil('\n', r, 15);
+
  while (Serial.available() == 0);
      byte num = r[0] - '0';
+
  Serial.readBytesUntil('\n', id, 15);
      //on stocke le numéro du couple(converti automatiquement en ascii par la liaison série) après l'avoir converti en valeur numérique
+
  //on stocke dans id les caractères suivant jusqu'à '\n' ou 15 caractères
  
      char id[16];
+
  char mp[16];
      for (int i = 0; i < 16; i++) id[i] = 0;
+
  for (int i = 0; i < 16; i++) mp[i] = 0;
      while (Serial.available() == 0);
+
  while (Serial.available() == 0);
      Serial.readBytesUntil('\n', id, 15);
+
  Serial.readBytesUntil('\n', mp, 15);
      //on stocke dans id les caractères suivant jusqu'à '\n' ou 15 caractères
 
  
      char mp[16];
+
  char i = 0;
      for (int i = 0; i < 16; i++) mp[i] = 0;
+
  char adresse = 0x10 + num * 0x20;
      while (Serial.available() == 0);
+
  do {
      Serial.readBytesUntil('\n', mp, 15);
+
    if (EEPROM.read(i + adresse) != id[i]) EEPROM.write( i + adresse, id[i]);
      //même principe avec mp
+
    i++;
 +
  } while (id[i] != 0);
 +
  if (EEPROM.read(i + adresse) != 0) EEPROM.write( i + adresse, 0);
  
      //on stocke id et mp dans l'EEPROM à l'adresse du couple de numéro indiqué
+
  i = 0;
      char i = 0;
+
  adresse += 0x10;
      char adresse = 0x10 + num * 0x20;
+
  do {
      do {
+
    if (EEPROM.read(i + adresse) != mp[i]) EEPROM.write( i + adresse, mp[i]);
        if (EEPROM.read(i + adresse) != id[i]) EEPROM.write( i + adresse, id[i]);
+
    i++;
        i++;
+
  } while (mp[i] != 0);
      } while (id[i] != 0);
+
  if (EEPROM.read(i + adresse) != 0) EEPROM.write( i + adresse, 0);
      if (EEPROM.read(i + adresse) != 0) EEPROM.write( i + adresse, 0);
+
}
 
 
      i = 0;
 
      adresse += 0x10;
 
      do {
 
        if (EEPROM.read(i + adresse) != mp[i]) EEPROM.write( i + adresse, mp[i]);
 
        i++;
 
      } while (mp[i] != 0);
 
      if (EEPROM.read(i + adresse) != 0) EEPROM.write( i + adresse, 0);
 
    }
 
 
</source>
 
</source>
 
</small>
 
</small>
 +
 
La fonction liste retourne à l'interface graphique la liste des couples identifiant/mot de passe stockés
 
La fonction liste retourne à l'interface graphique la liste des couples identifiant/mot de passe stockés
 +
 
<small>
 
<small>
 
<source lang=c>
 
<source lang=c>
 +
void liste() {
 +
  Serial.print(3);
 +
  Serial.print('\n');
  
    void liste() {
+
  char adresse = 0x10;
      Serial.print(3);
+
  for (int n = 0; n < 4; n++) {
      Serial.print('\n');
+
    Serial.print(n + "\n");
 
+
    char i = 0;
      char adresse = 0x10;
+
    do {
      for (int n = 0; n < 4; n++) {
+
      char c = EEPROM.read(adresse + i);
        Serial.print(n + "\n");
+
      Serial.print(c);
        char i=0;
+
      i++;
        do {
+
    } while (EEPROM.read(adresse + i) != 0);
          char c = EEPROM.read(adresse + i);
+
    adresse += 0x10;
          Serial.print(c);
+
    i = 0;
          i++;
+
    do {
        } while (EEPROM.read(adresse + i) != 0);
+
      char c = EEPROM.read(adresse + i);
        adresse += 0x10;
+
      Serial.print(c);
        i=0;
+
      i++;
        do {
+
    } while (EEPROM.read(adresse + i) != 0);
          char c = EEPROM.read(adresse + i);
+
    adresse += 0x10;
          Serial.print(c);
+
  }
          i++;
+
}
        } while (EEPROM.read(adresse + i) != 0);
 
        adresse += 0x10;
 
      }
 
 
 
    }
 
 
</source>
 
</source>
 
</small>
 
</small>
 +
 
La fonction suivante lit la demande de l'interface graphique et lance la fonction correspondant.<br />
 
La fonction suivante lit la demande de l'interface graphique et lance la fonction correspondant.<br />
 
Placer la définition de serialEvent ne marche peut être pas dans une classe, nous testerons avec cette écriture lorsque la carte sera réalisée, si cela ne fonctionne pas,<br />
 
Placer la définition de serialEvent ne marche peut être pas dans une classe, nous testerons avec cette écriture lorsque la carte sera réalisée, si cela ne fonctionne pas,<br />
 
nous placerons la définition de serialEvent à l’extérieur et appellerons cette fonction ou directement les fonctions concernées si un evenement survient sur la liaison série.
 
nous placerons la définition de serialEvent à l’extérieur et appellerons cette fonction ou directement les fonctions concernées si un evenement survient sur la liaison série.
 +
 
<small>  
 
<small>  
 
<source lang=c>
 
<source lang=c>
 
+
void serialEvent() {
    void serialEvent() {
+
  byte* r;
      byte* r;
+
  Serial.readBytesUntil('\n', r, 15);
      Serial.readBytesUntil('\n', r, 15);
+
  if (r[0] = 1) liste();
      if (r[0] = '1') liste();
+
  else if (r[0] = 2) change();
      else if (r[0] = '2') change();
+
}
    }
 
 
 
};
 
 
</source>
 
</source>
 
</small>
 
</small>
Ligne 608 : Ligne 605 :
 
{| class="wikitable"
 
{| class="wikitable"
 
|-
 
|-
! classe HID
+
! Partie HID
 
|-
 
|-
| classe gèrant les couples identifiants/mot de passe
+
| partie du programme gérant les couples identifiants/mots de passe
<small>
 
<source lang=c>
 
class HID {
 
 
 
  public:
 
    HID() {
 
 
 
    }
 
</source>
 
</small>
 
 
Le texte envoyé par l'ARDUINO LEONARDO est basé sur un clavier "qwerty" et le signal envoyé correspond à la position de la touche sur le clavier.<br />
 
Le texte envoyé par l'ARDUINO LEONARDO est basé sur un clavier "qwerty" et le signal envoyé correspond à la position de la touche sur le clavier.<br />
 
La fonction translate permet de remplacer le caractère demandé par l'emplacement de ce caractère sur un clavier "qwerty". <br />
 
La fonction translate permet de remplacer le caractère demandé par l'emplacement de ce caractère sur un clavier "qwerty". <br />
 
Il est également possible de modifier la classe Keyboard d'ARDUINO afin qu'elle utilise un clavier "azerty"
 
Il est également possible de modifier la classe Keyboard d'ARDUINO afin qu'elle utilise un clavier "azerty"
 +
 
<small>
 
<small>
 
<source lang=c>
 
<source lang=c>
 
+
char translate(char c) {
    char translate(char c) {
+
  switch (c) {
      switch (c) {
+
    case '1':
        case '1':
+
      c = '!';
          c = '!';
+
      break;
          break;
+
    case '2':
        case '2':
+
      c = '@';
          c = '@';
+
      break;
          break;
+
    case '3':
        case '3':
+
      c = '#';
          c = '#';
+
      break;
          break;
+
    case '4':
        case '4':
+
      c = '$';
          c = '$';
+
      break;
          break;
+
    case '5':
        case '5':
+
      c = '%';
          c = '%';
+
      break;
          break;
+
    case '6':
        case '6':
+
      c = '^';
          c = '^';
+
      break;
          break;
+
    case '7':
        case '7':
+
      c = '&';
          c = '&';
+
      break;
          break;
+
    case '8':
        case '8':
+
      c = '*';
          c = '*';
+
      break;
          break;
+
    case '9':
        case '9':
+
      c = '(';
          c = '(';
+
      break;
          break;
+
    case '0':
        case '0':
+
      c = ')';
          c = ')';
+
      break;
          break;
+
    case '°':
        case '°':
+
      c = '_';
          c = '_';
+
      break;
          break;
+
    case '&':
        case '&':
+
      c = '1';
          c = '1';
+
      break;
          break;
+
    case 'é':
        case 'é':
+
      c = '2';
          c = '2';
+
      break;
          break;
+
    case '"':
        case '"':
+
      c = '3';
          c = '3';
+
      break;
          break;
+
    case '(':
        case '(':
+
      c = '5';
          c = '5';
+
      break;
          break;
+
    case '-':
        case '-':
+
      c = '6';
          c = '6';
+
      break;
          break;
+
    case 'è':
        case 'è':
+
      c = '7';
          c = '7';
+
      break;
          break;
+
    case '_':
        case '_':
+
      c = '8';
          c = '8';
+
      break;
          break;
+
    case 'ç':
        case 'ç':
+
      c = '9';
          c = '9';
+
      break;
          break;
+
    case 'à':
        case 'à':
+
      c = '0';
          c = '0';
+
      break;
          break;
+
    case ')':
        case ')':
+
      c = '-';
          c = '-';
+
      break;
          break;
+
    case 'a':
        case 'a':
+
      c = 'q';
          c = 'q';
+
      break;
          break;
+
    case 'A':
        case 'A':
+
      c = 'Q';
          c = 'Q';
+
      break;
          break;
+
    case 'z':
        case 'z':
+
      c = 'w';
          c = 'w';
+
      break;
          break;
+
    case 'Z':
        case 'Z':
+
      c = 'W';
          c = 'W';
+
      break;
          break;
+
    case '^':
        case '^':
+
      c = '[';
          c = '[';
+
      break;
          break;
+
    case '$':
        case '$':
+
      c = ']';
          c = ']';
+
      break;
          break;
+
    case '£':
        case '£':
+
      c = '}';
          c = '}';
+
      break;
          break;
+
    case 'q':
        case 'q':
+
      c = 'a';
          c = 'a';
+
      break;
          break;
+
    case 'Q':
        case 'Q':
+
      c = 'A';
          c = 'A';
+
      break;
          break;
+
    case 'm':
        case 'm':
+
      c = ';';
          c = ';';
+
      break;
          break;
+
    case 'M':
        case 'M':
+
      c = ':';
          c = ':';
+
      break;
          break;
+
    case 'µ':
        case 'µ':
+
      c = '|';
          c = '|';
+
      break;
          break;
+
    case 'w':
        case 'w':
+
      c = 'z';
          c = 'z';
+
      break;
          break;
+
    case 'W':
        case 'W':
+
      c = 'Z';
          c = 'Z';
+
      break;
          break;
+
    case ',':
        case ',':
+
      c = 'm';
          c = 'm';
+
      break;
          break;
+
    case '?':
        case '?':
+
      c = 'M';
          c = 'M';
+
      break;
          break;
+
    case ';':
        case ';':
+
      c = ',';
          c = ',';
+
      break;
          break;
+
    case '.':
        case '.':
+
      c = '<';
          c = '<';
+
      break;
          break;
+
    case ':':
        case ':':
+
      c = '.';
          c = '.';
+
      break;
          break;
+
    case '/':
        case '/':
+
      c = '>';
          c = '>';
+
      break;
          break;
+
    case '!':
        case '!':
+
      c = '/';
          c = '/';
+
      break;
          break;
+
    default:
        default:
+
      break;
          break;
+
  }
      }
+
  return c;
      return c;
+
}
 
 
    }
 
 
</source>
 
</source>
 
</small>
 
</small>
 +
 
La fonction login saisie le couple identifiant/mot de passe correspondant au nombre sélectionné en paramètre.
 
La fonction login saisie le couple identifiant/mot de passe correspondant au nombre sélectionné en paramètre.
 +
 
<small>
 
<small>
 
<source lang=c>
 
<source lang=c>
 
+
void login(char num) {
    void login(char num) {
+
  Keyboard.begin();
      Keyboard.begin();
+
  char adresse = 0x10 + 0x20 * num;
      char adresse = 0x10 + 0x20 * num;
+
  char i = 0;
      char i = 0;
+
  do {
      do {
+
    char c = EEPROM.read(adresse + i);
        char c = EEPROM.read(adresse + i);
+
    Keyboard.print(translate(c));
        Keyboard.print(translate(c));
+
    i++;
        i++;
+
  } while (EEPROM.read(adresse + i) != 0);
      } while (EEPROM.read(adresse + i) != 0);
+
  Keyboard.write(KEY_TAB);
      Keyboard.write(KEY_TAB);
+
  i = 0;
      i = 0;
+
  adresse += 0x10;
      adresse += 0x10;
+
  do {
      do {
+
    char c = EEPROM.read(adresse + i);
        char c = EEPROM.read(adresse + i);
+
    Keyboard.print(translate(c));
        Keyboard.print(translate(c));
+
    i++;
        i++;
+
  } while (EEPROM.read(adresse + i) != 0);
      } while (EEPROM.read(adresse + i) != 0);
+
  Keyboard.println("");//==write(KEY_RETURN)
      Keyboard.println("");//==write(KEY_RETURN)
+
  Keyboard.end();
      Keyboard.end();
+
}
    }
 
 
 
 
 
};
 
 
</source>
 
</source>
 
</small>
 
</small>
Ligne 804 : Ligne 788 :
 
char bp[4] = {1 << PD5, 1 << PD4, 1 << PD7, 1 << PD6};
 
char bp[4] = {1 << PD5, 1 << PD4, 1 << PD7, 1 << PD6};
 
char led[4] = {1 << PF4, 1 << PF5, 1 << PF6, 1 << PF7};
 
char led[4] = {1 << PF4, 1 << PF5, 1 << PF6, 1 << PF7};
Com c;
 
HID h;
 
  
 
void setup() {
 
void setup() {
   c = Com();
+
   Serial.begin(9600);
  h = HID();
+
 
 
   DDRD = 0;
 
   DDRD = 0;
 
   PORTD = 0xf0;
 
   PORTD = 0xf0;
Ligne 815 : Ligne 797 :
 
   PORTF = 0xf0;
 
   PORTF = 0xf0;
 
}
 
}
 +
  
 
void loop() {
 
void loop() {
Ligne 820 : Ligne 803 :
 
     if ((PIND & bp[i]) == 0) {
 
     if ((PIND & bp[i]) == 0) {
 
       PORTF ^= led[i];
 
       PORTF ^= led[i];
       h.login(i);
+
       login(i);
 
       delay(100);
 
       delay(100);
 
       PORTF ^= led[i];
 
       PORTF ^= led[i];

Version du 16 janvier 2016 à 17:02

L'objectif de ce projet est de réaliser une clé USB capable de régler le volume de l'ordinateur sur lequel elle est branchée. Dans un deuxième temps, la clé devra pouvoir servir de télécommande ou stocker et saisir des mots de passes. Pour cela nous allons réaliser successivement plusieurs clés, la première servira uniquement à envoyer des informations à l'ordinateur afin d'ajuster le volume.


Conception de la première carte

Notre projet est basé sur un ATMEGA32U4, nous avons donc repris le schéma de l'ARDUINO LEONARDO, en ne conservant que la partie utile à notre clé USB. Ainsi nous pouvons supprimer les entrées et sorties ARDUINO. La carte sera toujours alimentée par USB donc la borne de connexion pour alimentation et le système de régulation de tension peuvent être supprimés. L'objectif étant de miniaturiser au maximum la carte, nous avons décidé de supprimer également les inductances et condensateurs destinés à l'amélioration de la qualité des mesures.
Nous allons ajouter sur le port D maintenant inutilisé 4 boutons et 4 LEDs


Voici la première version de la clé USB:

Schéma USB1.png
Routage
Perçage
placement composants top
Placement composants bottom
Nomenclature
quantité valeur package
4 (led) PLCC2
4 330R R1206
4 (bouton poussoir) SWITCH-6*6.5
2 1M R1206
2 22R R1206
1 (ISP) MA03-2
1 100nF C1206
1 10kR R1206
1 1uF C1206
1 (ATMEGA32U4) TQFP44-PAD
1 quartz 16MHz
1 (USB mâle A)

Bootloader

Afin de téléverser les programmes sans utiliser le connecteur ISP et d’exécuter le programme à la mise sous tension du microcontrôleur, nous allons installer un bootloader sur l'ATMEGA32U4. Pour cela, avant de le souder sur la carte, nous plaçons le microcontrôleur dans un boitier d'adaptation et utilisons l'outil "graver la séquence d'initialisation" du logiciel ARDUINO. Dans la première version de la carte nous avons conservé l'ISP comme sécurité si le bootloader ne fonctionne pas.

Problèmes rencontrés

Le connecteur ISP de la carte sur laquelle est soudé ce boitier d'adaptation, ne sont pas reliées aux bonnes broches du microcontrôleur. Nous allons donc créer une nouvelle carte d'adaptation sur laquelle nous souderons le boitier. Les broches à connecter sont:

Nom Broche ATMEGA32U4
MISO 11
MOSI 10
SCLK 9
/RESET 13
VCC 14,34
GND 15,23,35,43
Quartz 16,17

Le boitier n'étant pas au format standard, il faut recréer le composant sur Eagle.

voici la carte d'adaptation que nous avons réalisé.

Schéma adaptation USB.png
Board adaptation USB.png
Nomenclature
quantité valeur package
1 (ISP) MA03-2
1 1MR R1206
1 (quartz 16MHz)
1 (adaptateur ATMEGA32U4)

Réglage du volume

Modification des programmes ARDUINO

Pour ajuster le volume d'un ordinateur, il existe 3 caractères spéciaux nommés XF86volume_up, XF86volume_down et XF86volume_mute. Pour pouvoir envoyer ces caractères, il faut modifier les fichiers ARDUINO HID.cpp et USBAPI.h qui gèrent la partie "périphérique USB" de l'ARDUINO LEONARDO. Voici un exemple que nous avons trouvé qui ajoute à l'interface homme-machine la classe Remote permettant l'usage de fonctions de base d'une télécommande.

USBAPI.h
On déclare la classe Remote_ après les classes Keyboard_ et Mouse_ :

//================================================================================
//================================================================================
//	Remote 
 
#define REMOTE_CLEAR 0
#define VOLUME_UP 1
#define VOLUME_DOWN 2
#define VOLUME_MUTE 4
#define REMOTE_PLAY 8
#define REMOTE_PAUSE 16
#define REMOTE_STOP 32
#define REMOTE_NEXT 64
#define REMOTE_PREVIOUS 128
#define REMOTE_FAST_FORWARD 256
#define REMOTE_REWIND 512
 
class Remote_
{
private:
public:
	Remote_(void);
	void begin(void);
	void end(void);
 
	// Volume
	void increase(void);
	void decrease(void);
	void mute(void);
 
	// Playback
	void play(void);
	void pause(void);
	void stop(void);
 
	// Track Controls
	void next(void);
	void previous(void);
	void forward(void);
	void rewind(void);
 
	// Send an empty report to prevent repeated actions
	void clear(void);
};
extern Remote_ Remote;

HID.cpp
On déclare Remote l'instance de _Remote au début du programme:

Mouse_ Mouse;
Keyboard_ Keyboard;
Remote_ Remote;

On place l'impémentation de Remote à la fin du fichier,

après l'implémentation de Mouse et Keyboard, et avant les #endif finaux:

//================================================================================
//================================================================================
//	Remote
 
Remote_::Remote_(void)
{
}
 
void Remote_::begin(void) 
{
}
 
void Remote_::end(void) 
{
}
 
void Remote_::increase(void)
{
	u8 m[2];
	m[0] = VOLUME_UP;
	m[1] = 0;
	HID_SendReport(4,m,2);
}
 
void Remote_::decrease(void)
{
	u8 m[2];
	m[0] = VOLUME_DOWN;
	m[1] = 0;
	HID_SendReport(4,m,2);
}
 
void Remote_::mute(void)
{
	u8 m[2];
	m[0] = VOLUME_MUTE;
	m[1] = 0;
	HID_SendReport(4,m,2);
}
 
void Remote_::play(void)
{
	u8 m[2];
	m[0] = REMOTE_PLAY;
	m[1] = 0;
	HID_SendReport(4,m,2);
}
 
void Remote_::pause(void)
{
	u8 m[2];
	m[0] = REMOTE_PAUSE;
	m[1] = 0;
	HID_SendReport(4,m,2);
}
 
void Remote_::stop(void)
{
	u8 m[2];
	m[0] = REMOTE_STOP;
	m[1] = 0;
	HID_SendReport(4,m,2);
}
 
void Remote_::next(void)
{
	u8 m[2];
	m[0] = REMOTE_NEXT;
	m[1] = 0;
	HID_SendReport(4,m,2);
}
 
void Remote_::previous(void)
{
	u8 m[2];
	m[0] = REMOTE_PREVIOUS;
	m[1] = 0;
	HID_SendReport(4,m,2);
}
 
void Remote_::forward(void)
{
	u8 m[2];
	m[0] = 0;
	m[1] = REMOTE_FAST_FORWARD >> 8;
	HID_SendReport(4,m,2);
}
 
void Remote_::rewind(void)
{
	u8 m[2];
	m[0] = 0;
	m[1] = REMOTE_REWIND >> 8;
	HID_SendReport(4,m,2);
}
 
void Remote_::clear(void)
{
	u8 m[2];
	m[0] = 0;
	m[1] = 0;
	HID_SendReport(4,m,2);
}

On place la description HID des élément de Remote après
#if RAWHID_ENABLED
	//	RAW HID
    ...
#endif
(ligne 130 environ)
//-----------------------------------------------------------------------------

    /* Cross-platform support for controls found on IR Remotes */

    0x05, 0x0c,                    //	Usage Page (Consumer Devices)
    0x09, 0x01,                    //	Usage (Consumer Control)
    0xa1, 0x01,                    //	Collection (Application)
    0x85, 0x04,                    //	REPORT_ID (4)
    0x15, 0x00,                    //	Logical Minimum (0)
    0x25, 0x01,                    //	Logical Maximum (1)
    0x09, 0xe9,                    //	Usage (Volume Up)
    0x09, 0xea,                    //	Usage (Volume Down)
    0x75, 0x01,                    //	Report Size (1)
    0x95, 0x02,                    //	Report Count (2)
    0x81, 0x06,                    //	Input (Data, Variable, Relative)

    0x09, 0xe2,                    //	Usage (Mute)
    0x95, 0x01,                    //	Report Count (1)
    0x81, 0x06,                    //	Input (Data, Variable, Relative)

    0x09, 0xb0,                    //	Usage (Play)
    0x95, 0x01,                    //	Report Count (1)
    0x81, 0x06,                    //	Input (Data, Variable, Relative)

    0x09, 0xb1,                    //	Usage (Pause)
    0x95, 0x01,                    //	Report Count (1)
    0x81, 0x06,                    //	Input (Data, Variable, Relative)

    0x09, 0xb7,                    //	Usage (Stop)
    0x95, 0x01,                    //	Report Count (1)
    0x81, 0x06,                    //	Input (Data, Variable, Relative)

    0x09, 0xb5,                    //	Usage (Next)
    0x95, 0x01,                    //	Report Count (1)
    0x81, 0x06,                    //	Input (Data, Variable, Relative)

    0x09, 0xb6,                    //	Usage (Previous)
    0x95, 0x01,                    //	Report Count (1)
    0x81, 0x06,                    //	Input (Data, Variable, Relative)

    0x09, 0xb3,                    //	Usage (Fast Forward)
    0x95, 0x01,                    //	Report Count (1)
    0x81, 0x06,                    //	Input (Data, Variable, Relative)

    0x09, 0xb4,                    //	Usage (Rewind)
    0x95, 0x01,                    //	Report Count (1)
    0x81, 0x06,                    //	Input (Data, Variable, Relative)

    0x95, 0x06,                    //	Report Count (6) Number of bits remaining in byte
    0x81, 0x07,                    //	Input (Constant, Variable, Relative) 
    0xc0                           //	End Collection

Voici le fichier USBAPI.h modifié(les fichiers .cpp ne peuvent pas etre importé sur le wiki):

Programme de réglage du volume

void setup(){
  DDRD=0x0f;
  PORTD=0xff;
}

void loop(){
  if((PIND&(1<<PD6))==0){
    PORTD^=(1<<PD3);
    Remote.increase();
    Remote.clear();
    delay(1000);
    PORTD^=(1<<PD3);
  }
  if((PIND&(1<<PD5))==0){
    PORTD^=1;
    Remote.decrease();
    Remote.clear();
    delay(1000);
    PORTD^=1;
  }
  if((PIND&(1<<PD4))==0){
    PORTD^=0b0110;
    Remote.mute();
    Remote.clear();
    delay(1000);
    PORTD^=0b0110;
  }
}

Deuxième carte

Nous devons créer un nouvelle version de la clé USB plus compacte, qui permettrait l'usage de deux programme : le programme de gestion du volume ou un programme permettant le stockage et la saisie d'identifiant et de mots de passes. Pour ce dernier, nous allons également réaliser une interface graphique pour permettre à l'utilisateur de saisir les mots de passes à mémoriser.

Conception de la deuxième carte

Dans cette version de notre clé, le PCB fera office de connecteur USB mâle, on supprimera donc le connecteur utilisé dans la précédente clé. Le connecteur ISP sera aussi supprimé. Dans le but de miniaturiser la carte, nous avons choisi d'utiliser un format de boitier plus petit pour les résistances, et la résistance associée au quartz a été supprimée. Le quartz ne peut cependant pas être supprimé, il est nécessaire pour les fonctions d'interface homme-machine que nous voulons utiliser. Les ports des entrées et sorties sont modifiés afin d'optimiser le routage.

Schéma
Board
Perçage
Composants top
Composants bottom
Nomenclature
quantité valeur package
4 (led) PLCC2
4 330R R0805
4 (bouton poussoir) SWITCH-6*6.5
2 100nF C1206
2 22R R1206
1 1MR R1206
1 10kR R1206
1 1uF C1206
1 (ATMEGA32U4) TQFP44-PAD
1 quartz 16MHz
1 (USB mâle A)

composant utilisé pour le connecteur USB:

Contrôle du volume

Ce programme est le même que dans la partie précédente, il est juste nécessaire de changer les broches des entrées et sorties.

void setup(){
  DDRD=0x00;
  DDRF=0xf0;
  PORTD=0xf0;
  PORTF=0xf0;
}

void loop(){
  if((PIND&(1<<PD6))==0){
    PORTD^=(1<<PF7);
    Remote.increase();
    Remote.clear();
    delay(1000);
    PORTD^=(1<<PF7);
  }
  if((PIND&(1<<PD4))==0){
    PORTD^=(1<<PF4);
    Remote.decrease();
    Remote.clear();
    delay(1000);
    PORTD^=(1<<PF4);
  }
  if((PIND&(1<<PD7))==0){
    PORTD^=0b01100000;
    Remote.mute();
    Remote.clear();
    delay(1000);
    PORTD^=0b01100000;
  }
}

Gestion de mot de passe

Pour faciliter l'utilisation de la clé, nous avons choisi de ne stocker que 4 couples identifiants/mots de passe, ainsi, chaque bouton de la clé permet de l'envoie d'un mot de passe. Pour la communication entre l'interface graphique et la clé, nous avons mis en place un protocole de communication. Pour le moment, les chaines de caractères stockés doivent contenir 10 caractères ou moins, cette valeur pourra être augmenté en changeant l'adressage utilisé pour le stockage dans l'EEPROM.

Protocole de communication

chaque élément est séparé par le caractère '\n' (retour à la ligne).

demande de la liste des identifiants/mots de passe 1
commande de changement d'un couple 2 n° du couple identifiant mot de passe
envoie de la liste des couples 3 n° du 1er couple identifiant n°1 mot de passe n°1 n° du 2ème couple identifiant n°2 ...


Fonctionnement du microcontrôleur

Partie communication avec l'interface

class Com {

  public:  
  Com() {
      Serial.begin(9600);
    }

la fonction change() modifie un couple identifiant mot de passe lorsqu'on reçoit une consigne de changement venant de l'interface graphique.
Pour réduire l'usure de l'EEPROM, on change le caractère stocké seulement si il est différent du précédent.

void change() {
  byte* r;
  Serial.readBytesUntil('\n', r, 15);
  byte num = r[0] - '0';
  //on stocke le numéro du couple(converti automatiquement en ascii par la liaison série)
  // après l'avoir converti en valeur numérique

  char id[16];
  for (int i = 0; i < 16; i++) id[i] = 0;
  while (Serial.available() == 0);
  Serial.readBytesUntil('\n', id, 15);
   //on stocke dans id les caractères suivant jusqu'à '\n' ou 15 caractères

  char mp[16];
  for (int i = 0; i < 16; i++) mp[i] = 0;
  while (Serial.available() == 0);
  Serial.readBytesUntil('\n', mp, 15);

  char i = 0;
  char adresse = 0x10 + num * 0x20;
  do {
    if (EEPROM.read(i + adresse) != id[i]) EEPROM.write( i + adresse, id[i]);
    i++;
  } while (id[i] != 0);
  if (EEPROM.read(i + adresse) != 0) EEPROM.write( i + adresse, 0);

  i = 0;
  adresse += 0x10;
  do {
    if (EEPROM.read(i + adresse) != mp[i]) EEPROM.write( i + adresse, mp[i]);
    i++;
  } while (mp[i] != 0);
  if (EEPROM.read(i + adresse) != 0) EEPROM.write( i + adresse, 0);
}

La fonction liste retourne à l'interface graphique la liste des couples identifiant/mot de passe stockés

void liste() {
  Serial.print(3);
  Serial.print('\n');

  char adresse = 0x10;
  for (int n = 0; n < 4; n++) {
    Serial.print(n + "\n");
    char i = 0;
    do {
      char c = EEPROM.read(adresse + i);
      Serial.print(c);
      i++;
    } while (EEPROM.read(adresse + i) != 0);
    adresse += 0x10;
    i = 0;
    do {
      char c = EEPROM.read(adresse + i);
      Serial.print(c);
      i++;
    } while (EEPROM.read(adresse + i) != 0);
    adresse += 0x10;
  }
}

La fonction suivante lit la demande de l'interface graphique et lance la fonction correspondant.
Placer la définition de serialEvent ne marche peut être pas dans une classe, nous testerons avec cette écriture lorsque la carte sera réalisée, si cela ne fonctionne pas,
nous placerons la définition de serialEvent à l’extérieur et appellerons cette fonction ou directement les fonctions concernées si un evenement survient sur la liaison série.

void serialEvent() {
  byte* r;
  Serial.readBytesUntil('\n', r, 15);
  if (r[0] = 1) liste();
  else if (r[0] = 2) change();
}

Partie HID
partie du programme gérant les couples identifiants/mots de passe

Le texte envoyé par l'ARDUINO LEONARDO est basé sur un clavier "qwerty" et le signal envoyé correspond à la position de la touche sur le clavier.
La fonction translate permet de remplacer le caractère demandé par l'emplacement de ce caractère sur un clavier "qwerty".
Il est également possible de modifier la classe Keyboard d'ARDUINO afin qu'elle utilise un clavier "azerty"

char translate(char c) {
  switch (c) {
    case '1':
      c = '!';
      break;
    case '2':
      c = '@';
      break;
    case '3':
      c = '#';
      break;
    case '4':
      c = '$';
      break;
    case '5':
      c = '%';
      break;
    case '6':
      c = '^';
      break;
    case '7':
      c = '&';
      break;
    case '8':
      c = '*';
      break;
    case '9':
      c = '(';
      break;
    case '0':
      c = ')';
      break;
    case '°':
      c = '_';
      break;
    case '&':
      c = '1';
      break;
    case 'é':
      c = '2';
      break;
    case '"':
      c = '3';
      break;
    case '(':
      c = '5';
      break;
    case '-':
      c = '6';
      break;
    case 'è':
      c = '7';
      break;
    case '_':
      c = '8';
      break;
    case 'ç':
      c = '9';
      break;
    case 'à':
      c = '0';
      break;
    case ')':
      c = '-';
      break;
    case 'a':
      c = 'q';
      break;
    case 'A':
      c = 'Q';
      break;
    case 'z':
      c = 'w';
      break;
    case 'Z':
      c = 'W';
      break;
    case '^':
      c = '[';
      break;
    case '$':
      c = ']';
      break;
    case '£':
      c = '}';
      break;
    case 'q':
      c = 'a';
      break;
    case 'Q':
      c = 'A';
      break;
    case 'm':
      c = ';';
      break;
    case 'M':
      c = ':';
      break;
    case 'µ':
      c = '|';
      break;
    case 'w':
      c = 'z';
      break;
    case 'W':
      c = 'Z';
      break;
    case ',':
      c = 'm';
      break;
    case '?':
      c = 'M';
      break;
    case ';':
      c = ',';
      break;
    case '.':
      c = '<';
      break;
    case ':':
      c = '.';
      break;
    case '/':
      c = '>';
      break;
    case '!':
      c = '/';
      break;
    default:
      break;
  }
  return c;
}

La fonction login saisie le couple identifiant/mot de passe correspondant au nombre sélectionné en paramètre.

void login(char num) {
  Keyboard.begin();
  char adresse = 0x10 + 0x20 * num;
  char i = 0;
  do {
    char c = EEPROM.read(adresse + i);
    Keyboard.print(translate(c));
    i++;
  } while (EEPROM.read(adresse + i) != 0);
  Keyboard.write(KEY_TAB);
  i = 0;
  adresse += 0x10;
  do {
    char c = EEPROM.read(adresse + i);
    Keyboard.print(translate(c));
    i++;
  } while (EEPROM.read(adresse + i) != 0);
  Keyboard.println("");//==write(KEY_RETURN)
  Keyboard.end();
}

Programme principal

#include <EEPROM.h>
char bp[4] = {1 << PD5, 1 << PD4, 1 << PD7, 1 << PD6};
char led[4] = {1 << PF4, 1 << PF5, 1 << PF6, 1 << PF7};

void setup() {
  Serial.begin(9600);

  DDRD = 0;
  PORTD = 0xf0;
  DDRF = 0xf0;
  PORTF = 0xf0;
}


void loop() {
  for (char i = 0; i < 4; i++) {
    if ((PIND & bp[i]) == 0) {
      PORTF ^= led[i];
      login(i);
      delay(100);
      PORTF ^= led[i];
    }
  }
}

Interface graphique

Nous avons choisi de réaliser notre interface graphique avec Processing.

classe Com
classe gérant les protocoles de communications avec la clé

class Com extends PApplet {
  private Serial myport;
  private String[] id;
  private String[] mp;
  private int num;

Normalement, le premier port série devrait être la clé donc on peut initialiser la communication série sur le port Serial.list()[0].
Nous pourront par la suite ajouter un élément d'interface graphique affichant la liste des ports série pour une sélection par l'utilisateur.

  Com(String[] id, String[] mp) {
    this.id=id;
    this.mp=mp;
    myport=new Serial(this, Serial.list()[0], 9600);
    
  }

  //demande mp
  void demande() {
    myport.write(1);
    myport.write('\n');
  }

  //envoie nouveau mp
  void changer(int num) {
    myport.write(2);
    myport.write("\n"+num+"\n"+id+"\n"+mp+"\n");
  }

  void serialEvent(Serial myport) {
    byte[] inBuffer = new byte[7];
    myport.readBytesUntil('\n', inBuffer);
    if (inBuffer[0]=='3') {
      while (myport.available ()!=0) {
         myport.readBytesUntil('\n', inBuffer);
         num=inBuffer[0]-'0';
         id[num]=myport.readStringUntil('\n');
         mp[num]=myport.readStringUntil('\n');
      }
    }
  }
}

classe ...
classe gérant l'affichage de l'interface et l'interaction avec l'utilisateur

/******************
programme à ajouter
******************/