Télécommande Universelle Arduino

Miniature3

Difficulté:

Bonjour à tous,

Je vous présente ici une télécommande universelle que j'ai fabriquée à partir d'une carte Arduino. On trouve de nombreux tutoriels de ce genre sur internet, mais le mien est un peu différent puisqu'il permet notamment d'enregistrer et réutiliser jusqu'à 400 boutons facilement, même après avoir éteint puis rallumé la carte Arduino.

Si vous voulez plus d'articles, n'hésitez pas à me suivre, aimer, partager et à commenter. 


Voici les principales étapes de fonctionnement:

  1.  On positionne les 2 encodeurs rotatoires sur les positions souhaitées. Le premier correspond à l'appareil, le second à la fonction.
  2. Après avoir appuyé longtemps (1 seconde) sur un bouton poussoir, la télécommande est prête à recevoir le signal infrarouge (IR). On place alors une télécommande classique devant, on appuie sur le bouton, et le signal IR est "absorbé" par la télécommande universelle.
  3. Ce signal est stocké sur une carte SD.
  4. Puis pour contrôler l'appareil souhaité, on appuie rapidement (moins d'une seconde) sur le bouton poussoir, et le signal IR est envoyé.


Pour résumer:

  • Il y a des encodeurs rotatoires pour sélectionner l'appareil et la fonction.
  • Les signaux IR sont stockés sur une carte SD, et réutilisables même après extinction de la carte Arduino.
  • Après une pression longue sur le bouton poussoir, le signal IR est reçu et enregistré.
  • Après une courte pression, le signal IR est émis.


Avant de commencer:

Je décris ici les différentes étapes de construction par lesquelles je suis passé pour fabriquer cette télécommande. Pour les débutants ça peut être pratique, en revanche si vous êtes déjà un habitué des cartes Arduino vous pouvez passer directement à l'étape "Le circuit final, branché en USB". Je suis moi aussi relativement débutant dans le domaine et ceci est mon second projet Arduino (mon premier étant un pétanque-mètre que vous pouvez trouver ici), donc si vous trouvez des erreurs dans mes programmes, dans le texte ou dans les connexions entre les composants, merci de m'en faire part.

Cette télécommande est un projet dans lequel je me suis lancé en Janvier 2018, pour en apprendre plus sur les cartes Arduino et les signaux IR. Et pour enfin créer une télécommande Arduino universelle facile à fabriquer, fiable et digne de ce nom.

Si vous faites partis des makers anglophones, vous avez peut-être déjà vu passer la version anglaise que j'ai écrite sur le site instructables ici, et qui a été relayé par plusieurs site et notamment sur le site Arduino ici.


Matériel

Budget:

10 €
  • Carte Arduino (x1)
  • Des fils jumpers (pour connecter vos modules à la carte Arduino)
  • Encodeur rotatoire - KY-040 (x2)
  • Recepteur infrarouge - KY022 (x1)
  • Lecteur de carte microSD pour Arduino (x1)
  • Carte microSD (x1)
  • LED infrarouge (x1)
  • LED colorée (x1)
  • Batterie 18650 (x1)
  • Adaptateur pour batterie 18650 (x1)
  • Module de charge TP4065 (x1)
  • Convertisseur de tension (<5V à 5V) (x1)

Etape 1 : Utiliser une simple LED avec une carte Arduino

Sur la télécommande universelle j'ai ajouté une petite LED rouge pour s'assurer que tout fonctionne bien:

  • Après une pression longue sur le bouton poussoir, la LED reste allumée. Je sais donc que la carte Arduino est prête à recevoir le signal IR.
  • Après une pression courte sur le bouton poussoir, la LED s'allume puis s'éteint immédiatement. Je sais donc que le signal IR a été envoyé correctement (puisqu'un signal infrarouge est invisible à l'oeil nu).
  • En revanche si la LED ne réagit pas comme prévu, je dois débugger mon programme ou vérifier les connexions.

Pour cette partie, vous avez juste besoin d'une résistance pour protéger la LED, et la LED elle-même. 

Ici pas trop de difficultés, il faut jeter un coup d’œil au tutoriel Arduino pour faire clignoter une LED. Et le code que j'ai utilisé est très similaire à celui-ci:

int LED = 4;//LED connectée la broche 4

void setup() {  
pinMode (LED, OUTPUT); // La broche 4 est definie comme une sortie digitale
}

void loop() {  // Le code principal se trouve ci-dessous, et sera lu en boucle
digitalWrite(LED, HIGH );// La LED est allumée
delay(1000);// Le programme est stoppé pour 1 seconde
digitalWrite(LED, LOW );// La LED est éteinte
delay(1000);// Le programme est stope pour 1 seconde
}

Etape 2 : Comment utiliser le lecteur de carte SD avec l'Arduino?

Une partie cruciale de cette télécommande est la sauvegarde des données. Pour cela j'ai utilisé le lecteur de carte microSD pour Arduino. Je me suis aidé de la bibliothèque (ou library en anglais) "SD library to read/write to a file on an SD card". Ce tutoriel permet facilement de comprendre comment enregistrer des données sur la carte microSD. Pour simplifier les branchements, j'ai connecté la broche CS du module de lecteur de carte à la broche 10 de mon Arduino. De cette manière, les broches 10, 11, 12 et 13 sont à côté les unes des autres et toutes utilisée pour le module de lecteur de carte SD.

Mais j'ai aussi modifié le programme pour qu'il puisse être utilisé avec la télécommande universelle, avec les caractéristiques suivantes:

  • Les noms des fichiers enregistrés sur la carte SD, doivent être nommés en fonction des valeurs données par les encodeurs rotatoires.
  • Un fichier peut être effacé puis réécrit.


Voilà comment j'ai procédé:

Premier point:

Pour ma télécommande, je veux que chaque information soit sauvée dans un fichier texte individuel (extension .txt), avec le nom de chaque fichier correspondant aux valeurs des encodeurs rotatoirs. Voici quelques exemples:

  • L'encodeur rotatoire n°1 est en position 1 (donc pour contrôler l'appareil n°1, ici une TV par exemple), et l'encodeur rotatoire n°2 est en position 3 (bouton ON). Dans ce cas-là, je veux que mon fichier texte s'appelle "1_3.txt".
  • Si l'encodeur rotatoire n°1 est sur la position 1 (toujours la TV), mais cette fois l'encodeur rotatoire est sur la position 4 (volume + par exemple), alors mon fichier s'appellera "1_4.txt".
  • Si l'encodeur rotatoire n°1 est en position 2 (la radio cette fois), et l'encodeur rotatoire n°2 est en position 3 (bouton ON), alors mon fichier sera "2_3.txt".

Pour tester cette fonctionnalité, j'ai utilisé un petit bout de code qui me créé une variable sous forme de chaîne de caractère (string), que je peux modifier facilement en lui ajoutant 2 variables entières (int). Jetez donc un coup d’œil au code suivant:

int value1 = 1; // Valeur numérique qui imite la valeur du 1er encodeur rotatoire
String middle = "_"; // Chaîne de caractère avec seulement 1 caractère
int value2 = 2; // Valeur numérique qui imite la valeur du 2nd encodeur rotatoire
String ext = ".txt"; // Chaîne de caractères pour indiquer l'extension
String SDname; // Je crée la chaine de caractère final, qui est vide ici
SDname = value1 + middle + value2 + ext; // Enfin, j'ajoute toutes ces données ensemble!

Second point:

Si le signal IR n'a pas été enregistré correctement, il faut pouvoir effacer le mauvais signal. Et enregistrer le bon. 

J'ai d'abord essayé avec la fonction "SD.remove()" qui fonctionne très bien avec un code de taille réduite mais qui malheureusement ne fonctionnait pas sur mon programme final. Peut-être un conflit avec une autre fonction? Ou une mauvaise utilisation de la fonction? Si vous avez une idée n'hésitez pas à laisser un commentaire.

Puis j'ai trouvé une autre fonction, qui s'intitule "FileSeek", et qui permet de cherche une position dans un fichier. L'idée ici est donc de retourner à la position 0 de mon fichier et y ajouter du texte. Le fichier n'est pas renouvelé comme je voulais le faire dans un premier temps, mais les premières lignes sont remplacées ce qui est suffisant. Voici la ligne de code qui permet de faire ça:

myFile.seek(0);

Pour résumer cette sous-partie:

Dans le code, les actions suivantes sont donc réalisées:

  • un fichier .txt est créé à partir des valeurs des encodeurs rotatoires.
  • 3 lignes de code sont écrites sur ce fichier.
  • Le fichier est fermé, ré-ouvert, puis les 3 lignes sont remplacées par 3 nouvelles lignes.
  • Puis la boucle recommence en changeant les valeurs des encodeurs rotatoires.

Fichiers techniques de cette étape :

SD_card.ino

Etape 3 : Comment utiliser les encodeurs rotatoires (KY-040) ?

Dans ce projet les encodeurs sont très importants puisqu'ils permettent d'obtenir plus de 400 combinaisons possibles (20 positions par encodeur, donc 20x20), soit 400 boutons différents!!

J'ai utilisé la source suivante "How rotary encoder works and how to use it with Arduino?", et j'y ai ajouté quelques modifications:

  • Tout d'abord, j'ai changé l'incrémentation de chaque pas. Sur l'exemple tiré de la source ci-dessus, les valeurs de l'encodeur vont de 2 en 2 (0, 2, 4, 6, 8...). En divisant par 2 ces valeurs (à vrai dire j'ai multiplié par 0,5 comme vous pouvez le voir sur le code) j'ai pu obtenir des valeurs qui augmentent de 1 en 1.
  • Puis j'ai modifié le code pour obtenir des valeurs qui sont toujours situées entre 0 et 19. Dans la référence ci-dessus, un tour complet de l'encodeur donne des valeurs de 0 à 19. Puis le second tour de 20 à 39... Or dans mon cas, je veux que les valeurs du second tour aillent de 0 à 19 à nouveau! De la même manière, si l'encodeur est tourné dans le sens inverse des aiguilles d'une montre, les valeurs sont négatives et vont de -1 à -20. Pour ce faire, j'ai donc utilisé le modulo d'une division par 40 (ici 40 puisque comme indiqué au point précédent, les valeurs de l'encodeur vont de 2 en 2, donc de 0 à 40). Le résultat est le reste de la division, et on l’obtient facilement avec la ligne de code suivante:
val1 = (counter % 40) * 0.5;

La variable "counter" correspond à la valeur de l'encodeur, et la variable val1 donne le résultat final.

Finalement, voici le code que j'ai écrit pour 1 encodeur rotatoire. Dans la version finale (c'est-à-dire la télécommande universelle), il y a un second encodeur, et j'ai donc simplement utilisé le même code dupliqué (en changeant les noms des variables bien entendu).

#define outputA 6 //CLK 
#define outputB 7 //DT int counter = 0; 

int aState; 
int aLastState; 
int val1; 

void setup() {  
pinMode (outputA, INPUT);  
pinMode (outputB, INPUT);  
Serial.begin (9600);  // Reads the initial state of the outputA  
aLastState = digitalRead(outputA); 
}

void loop() {  
aState = digitalRead(outputA); // Reads the "current" state of the outputA  
// If the previous and the current state of the outputA are different, that means a Pulse has occured  
if (aState != aLastState) {    
// If the outputB state is different to the outputA state, that means the encoder is rotating clockwise    
if (digitalRead(outputB) != aState) {      
counter ++;    
} else {      
counter --;    
}    
Serial.println(counter);    
val1 = (counter % 40) * 0.5;    
Serial.print("Position: ");    
Serial.println(val1);  
}  
aLastState = aState; // Updates the previous state of the outputA with the current state
}

Fichiers techniques de cette étape :

rotary_encoder.ino

Etape 4 : Comment utiliser le bouton poussoir? (pression courte/longue)

Pushbutton

Le bouton poussoir ici utilise le même module que l'encodeur vu dans l'étape précédente. En effet le module KY-040 est à la fois un encodeur et un bouton poussoir!

Comme je l'ai déjà dit précédemment, sur ma télécommande universelle, j'ai besoin de 2 commandes principales:

  • Une pression longue, qui va permettre d'enregistrer un signal.
  • Une pression rapide, qui va permettre d'envoyer un signal.

Pour cette étape j'ai donc utilisé la bibliothèque "bounce library".

Pour une pression longue:

Quand le bouton poussoir est pressé, le code entre dans une boucle "while". Dans cette boucle il y a un compteur qui augmente de +1 à chaque tour de boucle. Et si ce compteur dépasse une valeur seuil, l'action est déclenchée. Sur l'exemple ci-dessous, le message "pression longue" est affiché dans ce cas:

while ( value == LOW ) { 
debouncer.update();    
value = debouncer.read();    
debouncer.update();    
delay(5);    
if (counter > 100) {      
Serial.println("Pression longue!");      
delay(1000);      
counter = 0;    
}    
counter++;  
}

Pour une pression courte:

Dans la suite du programme j'ai ajouté une boucle si ("if"), dans laquelle le code rentre si le compteur qui a été lancé dans la boucle "while" précédente est au dessus d'un nouveau seuil que j'ai empiriquement choisi avec une valeur de 5. Donc si le code rentre dans cette boucle, cela signifie que le bouton poussoir aura été pressé un court instant. Voici le code:

if (counter > 5) { 
Serial.println("Pression courte!");     
delay(1000);   
}

Et finalement, voici le code complet pour les pressions courtes et longues:

#include <Bounce2.h>
#define BUTTON_PIN 5
int counter = 0;
Bounce debouncer = Bounce();
void setup() {
Serial.begin(9660);
// Setup the button with an internal pull-up :
pinMode(BUTTON_PIN, INPUT_PULLUP);
// After setting up the button, setup the Bounce instance :
debouncer.attach(BUTTON_PIN);
debouncer.interval(5); // interval in ms
}

void loop() {
// Update the Bounce instance :
debouncer.update();
// Get the updated value :
int value = debouncer.read();
while ( value == LOW ) {
debouncer.update();
value = debouncer.read();
debouncer.update();
delay(5);
if (counter > 100) {
Serial.println("Long press!");
delay(1000);
counter = 0;
}
counter++;
}
if (counter > 5) {
Serial.println("Short press!");
delay(1000);
}
counter = 0;
}

Fichiers techniques de cette étape :

bounce.ino

Etape 5 : Comment utiliser le module de réception IR (KY-022) ?

Pour cette étape et celle suivante, je me suis basé sur ce tutoriel trouvé sur Adafruit, et j'ai utilisé l'exemple qui s'appelle "comboDump.ino" dans la bibliothèque IRLib2.

Dans ce tutoriel, j'ai découvert qu'on pouvait obtenir le numéro de protocole utilisé pour le signal IR (en utilisant myDecoder.protocolNum), le nombre de bits du signal (myDecoder.bits) et la valeur décodée (myDecoder.value). Cette partie est importante puisque j'ai besoin de ces 3 informations pour renvoyer le signal IR dans l'étape suivante.

J'ai décidé d'afficher ces valeurs avec les lignes de code suivantes:

Serial.println(myDecoder.protocolNum); 
Serial.println(myDecoder.bits); 
Serial.println(myDecoder.value);

Maintenant avec n'importe quelle télécommande vous pouvez comparer les données des protocoles utilisés.

Fichiers techniques de cette étape :

IR_receiver.ino

Etape 6 : Comment envoyer un signal IR?

Pour envoyer un signal IR, j'ai suivi la suite du tutoriel sur le site Adafruit, j'ai utilisé le même circuit et le même code. Mais j'y ai ajouté quelques changements.

Tout d'abord, j'ai utilisé les 3 données enregistrées précédemment (le numéro de protocole, le nombre de bits et la valeur du signal), et je les ai utilisés avec la fonction suivante pour envoyer le signal IR:

mySender.send(Protocol, Data, Bits);

Pour faire ceci, j'ai créé 3 chaines de caractères pour contenir ces données (string). Puis je les ai changées en entiers (int). Ça peut paraître un peu bizarre de passer par des chaines de caractère avant de passer par des entiers, mais c'est une partie que j'ai été obligé de faire dans le code final, je l'explique dans l'étape suivante.

Au lieu d'utiliser le LED IR (qui est invisible), vous pouvez utiliser pour cet exemple une LED colorée pour vous assurer que le programme fonctionne bien. Mais penser à remettre la LED IR ensuite. Ou vous pouvez également observer le signal IR à travers un appareil photo, comme celui de votre smartphone par exemple.

#include <IRLibAll.h>
IRsend mySender;
void setup() {
Serial.begin(9600);
}
void loop() {
String line1 = "1";
String line2 = "32";
String line3 = "33454215";
//mySender.send(1,33454215, 32);
mySender.send(line1.toInt(), line3.toInt(), line2.toInt());
delay(1000);
}

Fichiers techniques de cette étape :

Send_IR.ino

Etape 7 : Le circuit final, branché en USB

Voici enfin le circuit final pour la télécommande universelle! Il contient tous les exemples que j'ai décrits précédemment, avec les mêmes codes.

Mais il y a aussi quelques différences:

  • J'ai utilisé une breadboard pour connecter tous les modules au 5V et à la masse.
  • Il y a ici 2 encodeurs. Et comme je l'ai expliqué précédemment, j'ai dupliqué ce code mais en utilisant de nouveaux noms de variables pour le second encodeur.
  • Certaines des variables dans mes exemples avaient des noms similaires, elles ont donc été renommées.
  • Pour envoyer les informations, mon code lit les 3 lignes contenues dans chaque fichier texte sur la carte SD. Pour cela j'ai utilisé la fonction file.readStringUntil(), et comme je veux les informations sur chaque ligne indépendamment, j'ai utilisé un saut de ligne '\n'. Ce qui donne file.readStringUntil('\n'), et qui signifie: lit la ligne jusqu'à la prochaine.

Voici donc le fichier avec le code final de la télécommande.

Fichiers techniques de cette étape :

Universal_remote.ino

Etape 8 : Le circuit final, mobile avec une batterie

Bien entendu une télécommande c'est beaucoup moins pratique au bout d'un câble. Donc j'ai branché mon circuit à une batterie 18650 et son support. Vous pouvez également voir sur le schéma qu'il y a aussi un module pour charger la batterie (TP4056) et un convertisseur de tension pour fournir une tension de 5V à la carte Arduino.

A la place de la breadboard j'ai aussi soudé les fils ensemble, pour que le circuit soit plus compact.

Etape 9 : La télécommande finie

Finalement pour une meilleure utilisation de la télécommande, j'ai fabriqué une petite boîte en plastique avec mon imprimante 3D. Je dois reconnaître qu'elle n'est pas très jolie je l'ai fabriqué assez rapidement, mais j'en suis satisfait quand même! 

J'ai recouvert de scotch les parties métalliques du circuit pour éviter les courts-circuits. La plupart des modules électroniques sont libres dans la boîte (la carte Arduino, le lecteur de carte SD), et certains y sont attachés (comme par exemple l'emetteur IR, le récepteur IR, les encodeurs, l'interrupteur) à l'aide de vis M2. Et bien entendu la LED IR et le récepteur IR sont visibles à travers 2 trous dans la boîte.

J'ai dessiné 2 disques avec des petits pictogrammes pour indiquer les appareils à contrôler (disque en haut) et les actions à réaliser (disque en bas)

Fichiers techniques de cette étape :

Top_panel.stl Button1.stl Front_panel.stl Main_body.stl Back_panel.stl Button2.stl

Etape 10 : J'espère que ça vous a plu!

Enfin, sachez que les encodeurs doivent impérativement être replacés dans leur position d'origine avant l'allumage de la carte Arduino. En effet, les données sont stockées en fonction des valeurs, et le premier fichier 0_0.txt est appelé une fois la carte Arduino allumée, peu importe leur position.


N'hésitez pas à essayer le projet et commenter si vous avez des questions, des critiques ou autres!

Vous aimez ce tutoriel ?


la.couenne

22 fév. 2018 à 11:04

Excellent tuto! Très bien fait, bien documenté :-)

Vais faire qques tests, merci pour ton partage

Matlek

22 fév. 2018 à 11:47

Merci beaucoup :) ! Tiens moi au courant du résultat si tu essayes!

quentingard

14 mar. 2018 à 15:08

Super tuto bravo à toi.
je voulais juste demander comment tu fais pour mettre le code dans des encadrés gris comme ça ?

Merci beaucoup 

Matlek

14 mar. 2018 à 15:39

Merci!
Quand tu écris du texte, il y a la barre d'outils juste au dessus. Et il y a notamment un outil "Paragraph format", qui a comme symbole un pied-de-mouche. Clique dessus, et la dernière option qui est proposée (tout en bas), c'est "code". Et ça te permet de mettre ton texte au format code.

quentingard

15 mar. 2018 à 21:20

J'ai trouvé merci !


Recevez une fois par mois les meilleurs tutoriels Technologie dans votre boîte mail




Champion

Gagnant du Concours Concours DIY Arduino


S'inscrire:


  • Vues: 1595
  • J'aime: 7