Décoder du morse avec un arduino
L’alphabet morse ou code morse, est un code permettant de transmettre un texte à l’aide de séries d’impulsions courtes et longues, qu’elles soient produites par des signes, une lumière ou un geste.
- Wikipedia
Origines du projet
Partie 1 - Montage électronique
Partie 2 - Programmation
Origines du projet
Qu'est-ce qui peut bien pousser un être humain normal à avoir besoin
de comprendre du code morse ?
Dans mon cas, tout vient de l'excellent jeu
Keep Talking and Nobody Explodes, dans lequel vous devez résoudre de nombreux puzzles. L'un d'entre
eux requiert de comprendre un mot en morse.
Bien entendu, un
"dictionnaire" morse est fourni, mais il ne m'aura pas fallu longtemps
avant de vouloir automatiser tout ça.
La première solution était simplement un programme de quelques lignes en NodeJs, mais pour ne pas complètement casser l'immersion du jeu, j'ai choisi d'en faire un objet physique.
Partie 1 - Montage électronique
Toutes les pièces nécessaires au montage de ce projet étaient
présentes dans le
starter kit Arduino, que j'utilise depuis le début de mon aventure dans
l'électronique.
Aucune soudure n'est nécessaire pour le prototype que nous faisons sur
une breadboard.
La principale difficulté de ce projet est le montage de l'écran LCD. S'il nécessite un bon paquet de fils, il ne présente pas d'obstacle, pourvu qu'on y prête un minimum d'attention.
Les paragraphes suivants décrivent en détail le montage et la fonction
de chaque fil. Je vous recommande vivement de vous y intéresser pour
comprendre ce que vous faites.
Dans tous les cas, je mettrai
plus bas un schéma de montage récapitulant le tout.
Le câblage de l'écran peut être décomposé en trois catégories.
L'alimentation :
Toute l'alimentation de l'écran sera
effectuée en 5V. On utilisera donc les deux lignes supérieures de
notre breadboard pour apporter le courant.
L'alimentation principale de l'écran se fait via les pins #1 et #2. On
relie donc le pin #1 au GND, et le pin #2 au V+.
Le
rétroéclairage de l'écran nécessite une alimentation séparée, via les
pins #15 et #16.
Cette alimentation a besoin d'être protégée. Certains LCD disposent
d'une résistance intégrée. N'étant pas certain que ce soit le cas du
mien, j'ai joué la sécurité et utilisé une résistance de 230Ω. On
relie donc le pin #15 au V+ (avec la résistance) et le pin #16 au
GND.
Le pin #3 du LCD permet de régler le contraste. Il sera au minimum en
y appliquant 5V, et au minimum lorsqu'il est relié au GND. Dans un
montage propre, il serait malin d'utiliser un potentiomètre afin de
pouvoir le régler. J'ai choisi un montage plus simple en reliant ce
pin au GND afin d'avoir toujours le contraste maximum.
Les pins de contrôle :
Les choses sérieuses commencent. Les pins suivants servent à
communiquer avec l'écran. On commence donc à les relier au Arduino.
- Pin #4 : Nommé RS, il est utilisé pour dire au LCD que l'information que nous allons lui envoyer est soit un élément à afficher, soit une commande à effectuer (déplacer le curseur par exemple). Ne vous attardez pas là dessus pour le moment, nous verrons comment programmer tout ça dans le prochain chapitre. On le relie au port #7 du Arduino.
- Pin #5 : Comme son nom l'indique, le pin RW sert à indiquer que nous utilisons l'écran en lecture (en y appliquant 5V) ou en écriture (en y appliquant 0V). Dans notre cas, nous utiliserons uniquement l'écran en écriture. On peut donc directement relier ce pin au GND.
- Pin #6 : Nommé EN, il sert à prévenir le LCD que des informations sont prêtes à être lues. On le relie au port #8 du Arduino.
Les pins de données :
Maintenant que notre LCD est
alimenté et qu'il est capable de suivre nos instructions, il faut être
en mesure de lui envoyer des informations à afficher. Ces informations
sont envoyées sur les pins #11 à #14 du LCD. On les relie
respectivement aux pins #9 à #12 du Arduino.
C'est tout pour l'écran !
Il ne reste plus qu'à ajouter un bouton pour avoir un prototype
fonctionnel.
Reliez un pin au GND, l'autre au V+ avec une résistance 10kΩ et reliez
le tout au Arduino. J'ai choisi d'utiliser le pin #13 pour avoir un
retour visuel grâce à la LED intégrée.
Une fois le montage terminé, vous devriez obtenir quelque chose comme ça :

Dans la vraie vie, en faisant ce qu'on peut niveau câbles, ça ressemble plutôt à ça :

Partie 2 - Programmation
L'essentiel du code est axé sur deux principes :
- Pour gérer les durées d'appui sur le bouton, une variable contient le temps pendant lequel le bouton est appuyé ou relâché
- Pour décoder le code morse, deux tableaux contiennent l'alphabet dans l'ordre, respectivement en code morse et en ASCII.
Les tableaux permettront de faire la correspondance entre le code
morse et l'alphabet.
Par exemple, si je cherche à décoder le code morse ".-.." (un signal
court, puis un long, puis deux courts), je peux chercher cette
occurrence dans le tableau en morse.
Je trouve ce code à la douzième position de mon tableau en morse, je
peux donc en conclure qu'il s'agit de la douzième lettre de l'alphabet
: le L.
Pour contrôler le LCD, j'utilise la bibliothèque
LiquidCrystal.
Il suffit d'instancier le LCD en spécifiant les pins utilisées sur le
arduino, puis la taille de l'écran (en digits - dans notre cas, 16x2).
Voici le code complet (voir sur github) :
/* * Arduino morse code decoder * Build instructions (french) : https://www.mallorycoeur.com/posts/decoder-du-morse-avec-un-arduino * Mallory COEUR - 2016 */ #include <LiquidCrystal.h> LiquidCrystal lcd(7, 8, 9, 10, 11, 12); int input; int tick = 10; // ms int state; String dictionnary[] = {".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--.."}; char alphabet[27] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; String morse = ""; String translation = ""; void setup() { lcd.begin(16, 2); pinMode(13, INPUT); // button } void loop() { input = digitalRead(13); if (input == HIGH) { if (state <= 0) state = 0; state++; } else if (input == LOW) { if (state > 0) { if (state > 10) morse += "-"; else morse += "."; state = 0; } state--; } if (state <= -30 && morse != "") { translation += findInDictionnary(morse); morse = ""; writeLine(0, " "); writeLine(1, " "); } writeLine(0, translation); writeLine(1, morse); delay(tick); } char findInDictionnary(String word) { for(int i = 0; i < 26; i++) { if (dictionnary[i] == word) return alphabet[i]; } return false; } void writeLine(int line, String text) { lcd.setCursor(0, line); lcd.print(text); }