Go Down

Topic: Librairie Keyboard (utilisation, explication et conversion AZERTY) (Read 780 times) previous topic - next topic

nico78

Explication du fonctionnement de la librairie Keyboard

Nous allons étudier la façon dont la librairie keyboard fonctionne, son utilisation pour produire les caractères français ainsi que les problèmes de conversion.

Le clavier est un ensemble de touches, à chaque touche correspond un code 8 bits (scan code) qui n'est pas directement en rapport avec le caractère qui lui est assigné, c'est le système d'exploitation qui va mapper le scan code suivant le type de clavier qui est déclaré dans le système, il peut être d'ailleurs changé en passant par les paramètres. La touche a du clavier français AZERTY dont la disposition correspond à la valeur 'q' du clavier QWERTY a la valeur 0x14 en hexa soit 20 en décimal.

Pour voir l'ensemble des scan codes du clavier QWERTY, cliquer sur ce lien, l'image se trouve en bas de page:
par rapport au clavier AZERTY, l'emplacement de la touche 0x31 fait partie de la touche 'Entrée', par contre l'emplacement de la touche du scan code 0x32 sera pour nous le 0x31 et correspond à la touche '* et µ'.
https://github.com/qlyoung/armory-keyboard

Il existe un document PDF microsoft listant une correspondance HID USB et PS2 où l'on retrouve ces scan codes, suivre la colonne HID Usage ID :
https://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/translate.pdf

La librairie Keyboard n'offre pas la possibilité directement de simuler les touches du clavier par leurs 'scan codes' mais offre une translation par l'alphabet à partir du clavier de référence qui est le QWERTY. On comprend tout de suite les problèmes que l'on va rencontrer, d'une part parce que l'emplacement des caractères n'est pas la même ou diffère demandant l'appui du shift pour la produire et d'autre part le nombre de caractère français est plus important et demande en plus de la commande shift, l'utilisation de la commande Alt Gr pour produire l'ensemble des caractères de notre clavier AZERTY.

Cependant, la lecture du code source de la librairie Keyboard.cpp nous permet de comprendre que l'on peut utiliser les scan codes en ajoutant la valeur 0x88 en hexa ou 136 en décimal.

Voici un exemple de code qui reproduit la frappe des 48 touches imprimables par les scan codes:

Code: [Select]

// ---------------------------------------------------
// nico78
// scancode keyboard qwerty et correspondance azerty
// ---------------------------------------------------
//  Alt Gr azerty                   €                                                                       ~  #  {  [  |  `  \  ^    @  ]  }  ¤       
//   Shift azerty       Q  B  C  D  E  F  G  H  I  J  K  L  ?  N  O  P  A  R  S  T  U  V  Z  X  Y  Z  1  2  3  4  5  6  7  8  9  0    °  +  ¨  £  µ  No fr  M  %  NONE  .  /  §    >
//         azerty       q  b  c  d  e  f  g  h  i  j  k  l  ,  n  o  p  a  r  s  t  u  v  z  x  y  z  &  é  "  '  (  -  è  _  ç  à    )  =  ^  $  *  No fr  m  ù   ²    ;  :  !    <
//         qwerty       a  b  c  d  e  f  g  h  i  j  k  l  m  n  o  p  q  r  s  t  u  v  w  x  y  z  1  2  3  4  5  6  7  8  9  0    -  =  [  ]  \  No US  ;  '   `    ,  .  /   No US     
//       scancode       4, 5, 6, 7, 8, 9, 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,  45,46,47,48,49,  50,  51,52, 53,  54,55,56,  100};

#include <Keyboard.h>

void setup() {

  // put your setup code here, to run once:
 
  Keyboard.begin();
  delay(5000);

  for (int c=4; c<40; c++){
    keyboardScanCode(c);
  }

  // 50 ou 0x32 n'est pas utilisé sur notre clavier
  for (int c=45; c<50; c++){
    keyboardScanCode(c);
  }

  for (int c=51; c<57; c++){
    keyboardScanCode(c);
  }

  // Scan code pour la touche '< et >'
  keyboardScanCode(100);
 
  Keyboard.end();
}

void loop() {
  // put your main code here, to run repeatedly:
}

void keyboardScanCode(byte code){
  Keyboard.press(code+136);
  delay(5);
  Keyboard.release(code+136);
}


Pour composer les caractères nécessitant l'appui de la touche shift ou Alt Gr, il faut le programmer tel qu'on le ferait à la main sur le clavier, en voici quelques exemples:

Code: [Select]

// ------------------------------------------------
// nico78
// scancode, exemple de composition de caractères
// ------------------------------------------------
//  Alt Gr azerty                   €                                                                    ~  #  {  [  |  `  \  ^  @    ]  }     ¤       
//   Shift azerty       Q  B  C  D  E  F  G  H  I  J  K  L  ?  N  O  P  A  R  S  T  U  V  Z  X  Y  Z  1  2  3  4  5  6  7  8  9  0    °  +  ¨  £  µ  No fr  M  %  NONE  .  /  §    >
//         azerty       q  b  c  d  e  f  g  h  i  j  k  l  ,  n  o  p  a  r  s  t  u  v  z  x  y  z  &  é  "  '  (  -  è  _  ç  à    )  =  ^  $  *  No fr  m  ù   ²    ;  :  !    <
//         qwerty       a  b  c  d  e  f  g  h  i  j  k  l  m  n  o  p  q  r  s  t  u  v  w  x  y  z  1  2  3  4  5  6  7  8  9  0    -  =  [  ]  \  No US  ;  '   `    ,  .  /   No US     
//       scancode       4, 5, 6, 7, 8, 9, 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,  45,46,47,48,49,  50,  51,52, 53,  54,55,56,  100};

#include <Keyboard.h>

void setup() {
  // put your setup code here, to run once:
 
  Keyboard.begin();
  delay(5000);

  // Pour composer le A majuscule"
  Keyboard.press(KEY_LEFT_SHIFT);
  keyboardScanCode(20); // ou keyboard.print('q');
  Keyboard.release(KEY_LEFT_SHIFT);

  // Pour composer le caractère @
  // Alt Gr = Ctrl + Alt
  Keyboard.press(KEY_LEFT_CTRL);
  Keyboard.press(KEY_LEFT_ALT);
  keyboardScanCode(39); // ou keyboard.print('0');
  Keyboard.release(KEY_LEFT_ALT);
  Keyboard.release(KEY_LEFT_CTRL); 

  // Pour composer le caractère ê
  // nécessite deux séquences de touches, l'appui sur la touche ^ (0x2F)(dead key)
  // puis ensuite la touche e (0x08)
  keyboardScanCode(47);
  keyboardScanCode(8); // ou keyboard.print('e');

 
  Keyboard.end();
}

void loop() {
  // put your main code here, to run repeatedly:

}

void keyboardScanCode(byte code){
  Keyboard.press(code+136);
  delay(5);
  Keyboard.release(code+136);
}

nico78

Nous avons vu comment accéder aux touches du clavier, concernant les caractères imprimables; maintenant voyons comment utiliser les touches de commandes.

Le site Arduino les listent sur leur site, ils sont appelés 'Keyboard Modifiers', consultable ici:
https://www.arduino.cc/en/Reference/KeyboardModifiers

En fait si vous regardez bien, on comprend que les valeurs supérieures à 0x87 ne sont pas les valeurs réelles, il a été ajouté la valeur 0x88 à la valeur réelle comme nous l'avons vu précédemment. C'est le codage utilisé dans le fichier Keyboard.cpp qui l'impose.

Par exemple, la touche BackSpace (retour arrière) à la valeur réelle 0x2A hexa ou 42 en décimal, si on lui ajoute la valeur 0x88, on obtient 0xB2, soit bien la valeur déclarée dans les fichiers de la librairie.

Les valeurs allant de 0x80 à 0x87 ne sont pas non plus les valeurs réelles mais cela n'aura pas d'impact pour nous. On pourra utiliser toutes ces valeurs sans difficulté.


Réalisation d'un clavier:

Si l'on désire concevoir un clavier avec un montage arduino, choses que je n'ai pas réalisé, il faudra faire attention à l'utilisation des matrices qui ne permettent pas d'exploiter deux appuis sur deux touches en même temps. Il sera alors nécessaire de séparer certaines touches des autres dans la gestion du code comme les deads key ^ , ¨ , ~ , ` le shift et le Ctrl + Alt

Concernant le clavier numérique, on pourra aussi le simuler; encore une fois toutes les valeurs sont listées dans le document microsoft téléchargeable dans le premier post.

Ainsi le chiffre 1 du clavier numérique a le scan code 0x59, il faudra ajouter 0x88, ce qui donne 0xE1 en hexa ou 225 en décimal.



Ecrire du texte à la volée:

Pour écrire du texte à la volée, il faut soit modifier le code de la librairie soit créer un code de conversion.

Il existe une librairie KeyboardAzertyFr qui permet d'écrire une partie des caractères français disponible, on ne pourra pas écrire les caractères avec accents, les dead keys (combinaison de deux séquences de touche) et les caractères accessibles par Alt Gr; mais je pense qu'elle pourra suffire si l'on souhaite exécuter des scripts, le lien ici:
https://github.com/martin-leo/KeyboardAzertyFr

L'installation de cette librairie pourra sembler ne pas fonctionner, en fait ça dépendra si on a dèjà compiler ou pas le même code avant cette librairie. J'ai dû faire des suppressions à la main, je ne le met pas ici car je ne suis plus très sûr de ce que j'ai fais. De mémoire dans le dossier arduino de 'mes document's et dans les fichiers de compilation temporaires, le chemin s'affiche en bas lors de la compilation.


Pourquoi la conversion de tous les caractères français n'existent pas encore?

D'abord, elle existe maintenant puisque je l'ai moi-même réalisé mais ce n'est pas une librairie; c'est la raison pour laquelle je fais ce tuto. Le code de conversion sera disponible dans les prochains posts. J'aurais évidemment voulu contribué à la librairie KeyboardAzertyFr, mais mes connaissances en codage en c pure sont limités, de plus avec le problème des caractères que nous allons abordé, je ne voyais pas de solution simple à part utiliser des tableaux sur deux octets au lieu de 1 octet actuellement mais ça risque de consommer énormément de mémoire.

Le problème que j'ai rencontré, c'est que les chaînes de caractères sont codées en UTF-8, ainsi l'ensemble de nos caractères français ne tient pas sur un octet pour tous, il en faut parfois 2 (asccii supérieur à 127), voire trois pour le caractère €. Donc traité des caractères qui n'occupent pas le même espace devient nettement plus ardu pour une conversion et devient un problème pour modifier la librairie Keyboard. Aussi si vous utilisez des Teensyduino, et que vous avez installé l'exécutable qui met en place les fichiers, celui-ci supprime la librairie de son emplacement.

Cela dit, il faut savoir que sur le site d'Arduino, des codes de conversions UTF-8 ascii étendu existent, ce qui pourrait simplifier la création d'une nouvelle librairie au moins pour nous, à voir ici:
https://playground.arduino.cc/Main/Utf8ascii

J'ai donc entrepris la création d'une conversion qui je l'espère permettra aussi d'adapter le code à d'autres langages que le français.

Vu l'heure tardive, je posterais l'ensemble du code qui nécessitera deux posts demain, cela me permettra de procéder à une dernière vérification.

nico78

Notification des leds du clavier (CAPS LOCK, NUM LOCK, SCROLL LOCK)
Comme vous le savez, lorsque vous appuyer sur Verr Maj (verrouillage des majuscules), une led s'affiche sur votre clavier et cela ne va pas seulement affecter votre clavier sur lequel la touche a été activée mais les autres périphériques claviers aussi. (l'emploi de plusieurs claviers ne posant pas de problème pour l'OS). Le système d'exploitation va notifier systématiquement les autres périphériques concernés de l'état de ces touches. Ainsi si Verr Maj est activé sur un clavier, l'autre sera automatiquement en verrouillage des majuscules, les miniscules se transformeront en majuscules etc...

Il devient alors nécessaire si on souhaite envoyer des chaînes de caractères de s'assurer notamment que la touche Verr Maj n'est pas activée, voire aussi pour NUM LOCK et SCROLL LOCK.

Par chance, sur le forum arduino, une personne a fourni l'ensemble des fichiers à remplacer. J'ai testé ces fichiers et cela a bien fonctionné.

Je vous donne le lien où j'ai puisé l'information:
https://forum.arduino.cc/index.php?topic=173583.15


Le code de conversion (première partie)
N'hésiter pas à me faire remonter tous les problèmes liés à l'utilisation de ce code.
Afin de simplifier au maximum l'envoie de chaîne de caractères, j'ai créé plusieurs fonctions assez simples que vous pourrez supprimer éventuellement si vous n'en n'avez pas l'utilité.
A noter que grâce à l'émulation du pavé numérique, il est possible d'écrire toutes sortes de caractères unicodes sur les éditeurs (applications) qui les acceptent, fonctionne très bien sur Notepad.
Code: [Select]
//--------------------------------------------------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------------------------------------------------
//
// French Keyboard by Nico78 (Juin 2018) V1.00
//
// POUR AVOIR LE RETOUR DES NOTIFICATIONS DES LED, IL FAUT PATCHER, VOIR ICI
// https://forum.arduino.cc/index.php?topic=173583.15
//
//--------------------------------------------------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------------------------------------------------
//
// Ce code source utilise la bibliothèque Keyboard standard
// Tester avec un Arduino pro micro 5V 16Mhz et l'IDE arduino 1.8.5
//
//--------------------------------------------------------------------------------------------------------------------------------------------------------
// Fonctions:
//--------------------------------------------------------------------------------------------------------------------------------------------------------
// sendtext("abcd...") --> pour envoyer du texte (longueur maxi 255 caractères à la fois)
// sendtextln("abcd...") --> pour envoyer du texte avec retour chariot (longueur maxi 255 caractères à la fois)
// sendkeypad(0123456789/*-+E) pour simuler le clavier numérique, E pour la touche Entrée
// altunicode("0169"); pour afficher d'autres caractères unicodes dans les éditeurs qui les acceptent, ici affichera le symbole copyright © en tapant alt+0169
// sendkeycombi(KEY_LEFT_GUI, "r") permet de taper deux touches en même temps (une touche system + une touche ascii), ici 'window + r'
// selectall() permet de sélectionner tout le texte
// cut() permet de couper du texte
// copy() permet de copier du texte
// paste() permet de coller du texte
// homekey() permet de déplacer le curseur vers le début du texte lors d'une sélection
// endkey() permet de déplacer le curseur vers la fin du texte lors d'une sélection
// leftkey(nb) déplace le curseur vers la gauche (nb = nombre de fois)
// rightkey(nb) déplace le curseur vers la droite (nb = nombre de fois)
// upkey(nb) déplace le curseur vers le haut (nb = nombre de fois)
// downkey(nb) déplace le curseur vers le bas (nb = nombre de fois)
// tabkey(nb) permet de faire une tabulation (nb = nombre de fois)
// returnkey(nb) permet de revenir à la ligne (nb = nombre de fois)
// sendkeycommand(command) permet d'envoyer des commandes qui ne sont pas utiliser dans les fonctions précédentes comme
// KEY_ESC , KEY_INSERT , KEY_DELETE , KEY_PAGE_UP , KEY_PAGE_DOWN , KEY_CAPS_LOCK
//--------------------------------------------------------------------------------------------------------------------------------------------------------
// Source info:
// http://www.zem.fr/utiliser-mouse-keyboard-azerty-arduino-pro-micro-teensy/ pour la compréhension conversion qwerty <--> azerty
// http://forum.arduino.cc/index.php?topic=179548.0 pour l'utilisation du pad numérique, servira pour l'unicode alt+xxxx
//--------------------------------------------------------------------------------------------------------------------------------------------------------
// Valeur des constantes des leds pour info et présentation de la fonction permettant de récupérer l'état des leds du clavier
// #define LED_CAPS_LOCK      0x02
// #define LED_NUM_LOCK       0x01
// #define LED_SCROLL_LOCK    0x04
// bool getLedStatus(uint8_t led);
//--------------------------------------------------------------------------------------------------------------------------------------------------------

#include <Keyboard.h>

int first=0;
  
void setup() {
  // put your setup code here, to run once:
  // Start Keyboard and Mouse
  Keyboard.begin();
  delay(5000);
}


void loop() {
  if(first==0){
    
    first = 1;

//    Décommenter ces 3 lignes si vous avez patcher
//    if (Keyboard.getLedStatus(LED_CAPS_LOCK)==true){
//       sendkeycommand(KEY_CAPS_LOCK);
//    }
//    -------------------------------------------------
    
    sendkeycombi(KEY_LEFT_GUI, "r");
    delay(2000);
    sendtextln("notepad");
    returnkey(1);
    delay(2000);
        
    sendtextln("Alphabet miniscule");
    sendtext("abcdefghijklmnopqrstuvwxyz\r"); //supporte l'échappement \t (tab )et \r (retour chariot)
    sendtextln("Alphabet majuscule");
    sendtextln("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
    sendtextln("Chiffre");
    sendtextln("0123456789");
    sendtextln("Accents et cédille");
    sendtextln("àùéèç");
    sendtextln("Dead key");
    sendtextln("âêîôûäëïöüÿ ÂÊÎÔÛÄËÏÖÜ ~~``");
    sendtextln("Opération mathématique");
    sendtextln("/*-+=");
    sendtextln("Ponctuation et caractères d'ouverture et de fermeture");
    sendtextln(",;:!?<>()[]{}");
    sendtextln("Autres");
    sendtextln("²¤£§^%$#@€µ\"");
    sendtextln("Unicode exemple");
    altunicode("26"); // alt+26 donne →
    altunicode("27");  // alt+27 donne ←
    returnkey(1);
    
    sendtextln("Clavier numérique");
    sendkeypad("0123456789");
    returnkey(1);
    sendkeypad("/*-+");
    sendkeypad("E"); // Touche ENTER du clavier

    selectall();
    copy();
    endkey();
    returnkey(1);
    paste();

    
    }else{
     Keyboard.end();
  }
}

void sendkeycombi(byte key1, String keys){
    Keyboard.press(key1);
    sendtext(keys);
    Keyboard.release(key1);
    delay(280);
}

void selectall(){
  sendkeycombi(KEY_LEFT_CTRL,"a");
  delay(280);
}
void cut(){
  sendkeycombi(KEY_LEFT_CTRL,"x");
  delay(280);
}
void copy(){
  sendkeycombi(KEY_LEFT_CTRL, "c");
  delay(280);
}
void paste(){
  sendkeycombi(KEY_LEFT_CTRL, "v");
  delay(280);
}
void homekey(){
    sendkeycommand(KEY_HOME);
}
void endkey(){
    sendkeycommand(KEY_END);
}
void leftkey(int nbgauche){
  for (int i=0; i<nbgauche; i++){
    sendkeycommand(KEY_LEFT_ARROW);
  }
}
void rightkey(int nbdroite){
  for (int i=0; i<nbdroite; i++){
    sendkeycommand(KEY_RIGHT_ARROW);
  }
}
void upkey(int nbhaut){
  for (int i=0; i<nbhaut; i++){
    sendkeycommand(KEY_UP_ARROW);
  }
}
void downkey(int nbbas){
  for (int i=0; i<nbbas; i++){
    sendkeycommand(KEY_DOWN_ARROW);
  }
}

void returnkey(int nbrl){
  for (int i=0; i<nbrl; i++){
    sendkeycommand(KEY_RETURN);
  }
}

void backkey(int nbrl){
  for (int i=0; i<nbrl; i++){
    sendkeycommand(KEY_BACKSPACE);
  }
}


void tabkey(int nbtab){
  for (int i=0; i<nbtab; i++){
    sendkeycommand(KEY_TAB);
  }
}

nico78

Le code de conversion (deuxième partie)
Code: [Select]

void sendkeycommand(byte key){
    Keyboard.write(key);
    delay(5);
}

void sendkeypad(String keys){
    // Trick by johnwasser
    // add 136 to the keycode

    byte test;
    byte keyc;
    String key;

    
   for (int i=0; i < keys.length(); i++){
      key = keys.substring(i, i + 1);
      
      test=1;
      if (key=="1"){
        keyc=225;
      }else if (key=="2"){
        keyc=226;
      }else if (key=="3"){
        keyc=227;
      }else if (key=="4"){
        keyc=228;
      }else if (key=="5"){
        keyc=229;
      }else if (key=="6"){
        keyc=230;
      }else if (key=="7"){
        keyc=231;
      }else if (key=="8"){
        keyc=232;
      }else if (key=="9"){
        keyc=233;
      }else if (key=="0"){
        keyc=234;
      }else if (key=="/"){
        keyc=220;
      }else if (key=="*"){
        keyc=221;
      }else if (key=="-"){
        keyc=222;
      }else if (key=="+"){
        keyc=223;
      }else if (key=="E"){ // touche enter du clavier
        keyc=224;
      }else{
        test=0;
      }
  
      if (test==1){
        Keyboard.press(keyc);
        delay(5);
        Keyboard.release(keyc);
        delay(5);
      }
   }
}

void altunicode(String keys){

    Keyboard.press(KEY_LEFT_ALT);
    sendkeypad(keys);
    Keyboard.release(KEY_LEFT_ALT);
    delay(5);
}

void SendKey(char key, byte control, byte alt, byte shift){
  
    if (control==1){
      Keyboard.press(KEY_LEFT_CTRL);
    }

    if (alt==1){
      Keyboard.press(KEY_LEFT_ALT);
    }


    if (shift==1){
        Keyboard.press(KEY_LEFT_SHIFT);
    }
  
    Keyboard.press(key);
    delay(5);
    Keyboard.release(key);


    if (shift==1){
        Keyboard.release(KEY_LEFT_SHIFT);
    }
    
    if (alt==1){
      Keyboard.release(KEY_LEFT_ALT);
    }
    
    if (control==1){
      Keyboard.release(KEY_LEFT_CTRL);
    }
    delay(5);
}


int indexOfCharacter(String Character){
  // Les chaines de caractères UTF-8 sont composées de 1 ou plusieurs octets
  // C'est pourquoi la variable Character est transmise comme une chaîne et non comme un char
  // La fonction calcul la position du caractère comme si tous les caractères tenaient sur 1 seul octet
  // pour une comparaison future avec une autre chaine de référence dont tous les caractères tiennent sur 1 octet

    // il y a un doublon avec le caractère ^ qui peut être soit interprété comme un accent ou soit comme une puissance
    // il apparait dans la chaine keyAzertyAltGr et dans la chaine keyAzerty
    // si le caractère ^ apparait seul, il sera traité comme une puissance, cas le plus intéressant pour nous
    // par conséquent le chaîne keyAzertyAltGr sera traité avant la chaîne keyAzerty

    String keyAzertyShift      = F("1234567890°+¨£ABCDEFGHIJKLMNOPQRSTUVWXYZ?./§%µ£");
    String keyAzertyAltGr      = F("1~#{[|`\\^@]}1¤1111€"); //le '1' ne sera pas prise en compte, sert juste pour l'alignement
    String keyAzerty           = F("&é\"'(-è_çà)=^$abcdefghijklmnopqrstuvwxyz,;:!ù*²");
 
    String circomflexNoShift   = F("âêîôû");
    String circomflexShift     = F("ÂÊÎÔÛ");
    String tremaNoShift        = F("äëïöüÿ");
    String tremaShift          = F("ÄËÏÖÜ");
    
    String compare="", r="";
    byte c, align;
    int indexCharacter=0;

    if (keyAzertyShift.indexOf(Character)>-1){
      compare=keyAzertyShift;
      indexCharacter=2<<8;
    }else if (keyAzertyAltGr.indexOf(Character)>-1){
      compare=keyAzertyAltGr;
      indexCharacter=3<<8;
    }else if (keyAzerty.indexOf(Character)>-1){
      compare=keyAzerty;
      indexCharacter=1<<8;
    }else if (circomflexNoShift.indexOf(Character)>-1){
      compare=circomflexNoShift;
      indexCharacter=4<<8;
    }else if (circomflexShift.indexOf(Character)>-1){
      compare=circomflexShift;
      indexCharacter=5<<8;
    }else if (tremaNoShift.indexOf(Character)>-1){
      compare=tremaNoShift;
      indexCharacter=6<<8;
    }else if (tremaShift.indexOf(Character)>-1){
      compare=tremaShift;
      indexCharacter=7<<8;
    }

    if (compare!=""){
        for (int i=0; i<compare.length(); i++)
        {
            align=0;
            c = compare.charAt(i);
            if (c<128){
              // 1 octet
              align=1;
            }else if (c>191 && c<224){
              // 2 octets
              align=2;
            }else if (c>223 && c<240){
              // 3 octets
              align=3;
            }else if (c>239 && c<248){ // section qui ne sera pas utilisé dans notre cas
              // 4 octets
              align=4;
            }
    
            if (align!=0){    
              r=compare.substring(i, i + align);
              if (r==Character){
                return indexCharacter;
              }
            }else{
              return -1;
            }
            i=i+align-1;
            indexCharacter+=1;
        }
    }else{
        return -1;
    }
}

void decodeUTF8(String Texte){
    String keyQuerty      = F("1234567890-=[]qbcdefghijkl;noparstuvzxywm,./'\\`");
                         // ("&é\"'(-è_çà)=^$abcdefghijklmnopqrstuvwxyz,;:!ù*²");
                          
  // Dead key (combinaison de deux touches produisant un ou deux caractères) ne concernant que les voyelles, traitement dans cette chaîne  
    String keyDead        = F("qeiouy");  
    
    String r="";
    byte c;
    byte ctrl, alt, shift;
    int indice=-1, sequence, align;
    char char_array[5];
    
    for (int i=0; i<Texte.length(); i++)
    {
        ctrl=0, alt=0, shift=0;
        align=0;
        c = Texte.charAt(i);
        if (c<128){
          // 1 octet
          align=1;
        }else if (c>191 && c<224){
          // 2 octets
          align=2;
        }else if (c>223 && c<240){
          // 3 octets
          align=3;
        }else if (c>239 && c<248){ // section qui ne sera pas utilisé dans notre cas
          // 4 octets
          align=4;
        }

        if (align!=0){
            indice = i;
            i=i+align-1;  
            r=Texte.substring(indice, indice + align);
            
            // traitement particulier de certaines commandes et caractères
            if (r==" "){
              sendkeycommand(0x2c+136);
            }else if (c==9){
              sendkeycommand(KEY_TAB);
            }else if (c==13){
              sendkeycommand(KEY_RETURN);
            }else if (r=="<"){
              SendKey(0x64+136, 0, 0, 0);
            }else if (r==">"){
              SendKey(0x64+136, 0, 0, 1);
            }else{
                indice = indexOfCharacter(r);
                sequence=(unsigned int)indice>>8;
                if (indice>-1){
                    indice=(unsigned int)indice & 0xFF;  
                    if (sequence==1){         // normal
                        shift=0;
                    }else if (sequence==2){   // shift
                        shift=1;
                    }else if (sequence==3){   // altgr  
                        ctrl=1;
                        alt=1;
                        // delay supplémentaire pour dead key avec Alt Gr
                        if (indice==1 || indice==6){
                          delay(50);
                        }
                    }else if (sequence==4){   // ajout de délai supplémentaire dead key
                        delay(50);          
                        SendKey('[', 0, 0, 0);
                    }else if (sequence==5){  
                        delay(50);
                        SendKey('[', 0, 0, 0);
                        shift=1;
                    }else if (sequence==6){  
                        delay(50);
                        SendKey('[', 0, 0, 1);
                    }else if (sequence==7){  
                        delay(50);
                        SendKey('[', 0, 0, 1);
                        shift=1;  
                    }
                    if (sequence<4){
                        r=keyQuerty.substring(indice, indice + 1);
                    }else{
                        r=keyDead.substring(indice, indice + 1);
                    }
                    r.toCharArray(char_array, 5);
                    SendKey(char_array[0], ctrl, alt, shift);
                }
            }  
        }  
    }      
}

void sendtext(String Texte){
  decodeUTF8(Texte);
}

void sendtextln(String Texte){
  decodeUTF8(Texte);
  sendkeycommand(KEY_RETURN);
}

nico78

En téléversant le code de conversion avec le test inclus, notepad va s'exécuter et tous les caractères du clavier vont s'afficher avec en plus l'exécution d'un copier-coller à la fin comme le montre le gif ci-dessous.





jfs

Héberge ton gif ailleurs que sur google.... là ça marche pas.
Pas d'aide par MP !!!

Concernant le fonctionnement du forum tout se trouve dans les messages épinglés en tête de page.

nico78

Une mise à jour en passant par les scancodes plutôt que les caractères, ainsi on peut afficher toute la table et voir les correspondances. J'ai aussi mis à jour les premiers exemples de code dans le premier post.

Ici, la partie du code qui change.


Code: [Select]

void SendKey(byte key, byte control, byte alt, byte shift){
 
    if (control==1){
      Keyboard.press(KEY_LEFT_CTRL);
    }

    if (alt==1){
      Keyboard.press(KEY_LEFT_ALT);
    }

    if (shift==1){
        Keyboard.press(KEY_LEFT_SHIFT);
    }
   
    Keyboard.press(key+136);
    delay(5);
    Keyboard.release(key+136);

    if (shift==1){
        Keyboard.release(KEY_LEFT_SHIFT);
    }
   
    if (alt==1){
      Keyboard.release(KEY_LEFT_ALT);
    }
   
    if (control==1){
      Keyboard.release(KEY_LEFT_CTRL);
    }
    delay(5);
}


int indexOfCharacter(String Character){
    // Les chaines de caractères UTF-8 sont composées de 1 ou plusieurs octets
    // C'est pourquoi la variable Character est transmise comme une chaîne et non comme un char
    // La fonction calcul la position du caractère comme si tous les caractères tenaient sur 1 seul octet
    // pour une comparaison future avec une autre chaine de référence dont tous les caractères tiennent sur 1 octet

    // il y a un doublon avec le caractère ^ qui peut être soit interprété comme un accent ou soit comme une puissance
    // il apparait dans la chaine keyAzertyAltGr et dans la chaine keyAzerty
    // si le caractère ^ apparait seul, il sera traité comme une puissance, cas le plus intéressant pour nous
    // par conséquent le chaîne keyAzertyAltGr sera traité avant la chaîne keyAzerty

    // \ est un caractère d'échappement, donc \\ sera transformé en \ et  \" sera transformé en "
 
    String keyAzertyAltGr      = F("0000€0000000000000000000000~#{[|`\\^@]}0¤");
    String keyAzertyShift      = F("QBCDEFGHIJKL?NOPARSTUVZXYZ1234567890°+¨£µ0M%0./§>");
    String keyAzerty           = F("qbcdefghijkl,noparstuvzxyz&é\"'(-è_çà)=^$*0mù²;:!<");
 
    String circomflexNoShift   = F("âêîôû");
    String circomflexShift     = F("ÂÊÎÔÛ");
    String tremaNoShift        = F("äëïöüÿ");
    String tremaShift          = F("ÄËÏÖÜ");
     
    String compare="", r="";
    byte c, align;
    int indexCharacter=0;

    if (keyAzertyShift.indexOf(Character)>-1){
      compare=keyAzertyShift;
      indexCharacter=2<<8;
    }else if (keyAzertyAltGr.indexOf(Character)>-1){
      compare=keyAzertyAltGr;
      indexCharacter=3<<8;
    }else if (keyAzerty.indexOf(Character)>-1){
      compare=keyAzerty;
      indexCharacter=1<<8;
    }else if (circomflexNoShift.indexOf(Character)>-1){
      compare=circomflexNoShift;
      indexCharacter=4<<8;
    }else if (circomflexShift.indexOf(Character)>-1){
      compare=circomflexShift;
      indexCharacter=5<<8;
    }else if (tremaNoShift.indexOf(Character)>-1){
      compare=tremaNoShift;
      indexCharacter=6<<8;
    }else if (tremaShift.indexOf(Character)>-1){
      compare=tremaShift;
      indexCharacter=7<<8;
    }

    if (compare!=""){
        for (int i=0; i<compare.length(); i++)
        {
            align=0;
            c = compare.charAt(i);
            if (c<128){
              // 1 octet
              align=1;
            }else if (c>191 && c<224){
              // 2 octets
              align=2;
            }else if (c>223 && c<240){
              // 3 octets
              align=3;
            }else if (c>239 && c<248){ // section qui ne sera pas utilisé dans notre cas
              // 4 octets
              align=4;
            }
   
            if (align!=0){     
              r=compare.substring(i, i + align);
              if (r==Character){
                return indexCharacter;
              }
            }else{
              return -1;
            }
            i=i+align-1;
            indexCharacter+=1;
        }
    }else{
        return -1;
    }
}

void decodeUTF8(String Texte){
  // Tous les caractères de keyQuerty et keyDead ici tiennent sur 1 octet, ce sont les chaînes de référence
                   
       //  Alt Gr azerty                   €                                                                    ~  #  {  [  |  `  \  ^  @    ]  }     ¤       
       //   Shift azerty       Q  B  C  D  E  F  G  H  I  J  K  L  ?  N  O  P  A  R  S  T  U  V  Z  X  Y  Z  1  2  3  4  5  6  7  8  9  0    °  +  ¨  £  µ  No fr  M  %  NONE  .  /  §    >
       //         azerty       q  b  c  d  e  f  g  h  i  j  k  l  ,  n  o  p  a  r  s  t  u  v  z  x  y  z  &  é  "  '  (  -  è  _  ç  à    )  =  ^  $  *  No fr  m  ù   ²    ;  :  !    <
       //         qwerty       a  b  c  d  e  f  g  h  i  j  k  l  m  n  o  p  q  r  s  t  u  v  w  x  y  z  1  2  3  4  5  6  7  8  9  0    -  =  [  ]  \  No US  ;  '   `    ,  .  /   No US     
    byte scancode[] PROGMEM = {4, 5, 6, 7, 8, 9, 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,  45,46,47,48,49,  50,  51,52, 53,  54,55,56,  100};

                         
  // Dead key (combinaison de deux touches produisant un ou deux caractères) ne concernant que les voyelles, traitement dans cette chaîne 
                   // azerty   a   e   i   o   u   y         
    byte keyDead[] PROGMEM = {20,  8, 12, 18, 24, 28};

     
    String r="";
    byte c, ctrl, alt, shift;
    int indice=-1, sequence, align;
   
    for (int i=0; i<Texte.length(); i++)
    {
        ctrl=0, alt=0, shift=0;
        align=0;
        c = Texte.charAt(i);
        if (c<128){
          // 1 octet
          align=1;
        }else if (c>191 && c<224){
          // 2 octets
          align=2;
        }else if (c>223 && c<240){
          // 3 octets
          align=3;
        }else if (c>239 && c<248){ // section qui ne sera pas utilisé dans notre cas
          // 4 octets
          align=4;
        }

        if (align!=0){
            indice = i;
            i=i+align-1; 
            r=Texte.substring(indice, indice + align);
           
            // traitement particulier de certaines commandes et caractères
            if (r==" "){
              sendkeycommand(0x2c+136);
            }else if (c==9){
              sendkeycommand(KEY_TAB);
            }else if (c==13){
              sendkeycommand(KEY_RETURN);
            }else{
                indice = indexOfCharacter(r);
                sequence=(unsigned int)indice>>8;
                if (indice>-1){
                    indice=(unsigned int)indice & 0xFF;   
                    if (sequence==1){         // normal
                        shift=0;
                    }else if (sequence==2){   // shift
                        shift=1;
                    }else if (sequence==3){   // altgr 
                        ctrl=1;
                        alt=1;
                        // delay supplémentaire pour dead key simple avec Alt Gr
                        if (indice==31 || indice==36){
                          delay(50);
                        }
                    }else if (sequence==4){   // à partir de là, traitement des dead keys (deux combinaisons de touches nécessaires)
                        delay(50);            // ajout de délai supplémentaire pour ces caractères
                        SendKey(47, 0, 0, 0);

                    }else if (sequence==5){ 
                        delay(50);                     
                        SendKey(47, 0, 0, 0);
                        shift=1;

                    }else if (sequence==6){ 
                        delay(50);     
                        SendKey(47, 0, 0, 1);

                    }else if (sequence==7){ 
                        delay(50);
                        SendKey(47, 0, 0, 1);
                        shift=1; 

                    }
                   
                    if (sequence<4){
                        c=scancode[indice];
                        SendKey(c, ctrl, alt, shift);
                    }else{
                        c=keyDead[indice];
                        SendKey(c, ctrl, alt, shift);               
                    }

                }
            }   
        } 
    }       
}

void sendtext(String Texte){
  decodeUTF8(Texte);
}

void sendtextln(String Texte){
  decodeUTF8(Texte);
  sendkeycommand(KEY_RETURN);
}

nico78

J'ai modifié les codes pour ajouter des delay supplémentaires dans la procédure decodeUTF8 pour le traitement des dead key ^¨~`

Il ne s'affichait pas toujours correctement, je ne l'avais pas remarqué auparavant. Il faut comprendre que les deads keys sont enregistrés par le système d'exploitation avant la production du ou des caractères définitifs dans l'attente de l'appui de la touche suivante, j'imagine qu'il faut lui laissé du temps supplémentaire.

Ainsi l'appui sur la touche ^ du premier clavier avec l'appui sur la touche a sur un second clavier relié sur le même ordinateur produira bien le caractère â.

Go Up