Utiliser un encodeur rotatif en MicroPython avec un ESP32
(Mis à jour le 09/01/2023)
Un codeur rotatif est un type de capteur de position qui convertit la position angulaire (rotation) d’un axe en un signal de sortie utilisé pour déterminer la position et le sens de rotation. Il est utilisé essentiellement dans les moteurs pour faire de l’asservissement, mais également dans les interfaces utilisateurs pour remplacer les potentiomètres. En plus, la plupart des modules que l’on trouve dans les kits DIY sont munis d’un bouton-poussoir intégré !
Prendre en main un codeur rotatif : le KY-040
Dans ce tutoriel, nous allons voir comment utiliser un module avec un codeur rotatif KY-040, très présent dans les kits DIY. On l’utilise souvent pour faire une interface utilisateur avec notre programme : sélection d’un menu, augmenter/diminuer une variable…
Différence entre codeur rotatif et potentiomètre
À première vue, le codeur rotatif KY-040
peut être confondu avec un potentiomètre. Mais en réalité, le potentiomètre est un capteur analogique alors que le codeur rotatif est numérique. Un potentiomètre modifie la valeur d’une résistance, mais possède un nombre de tours limité.
Le codeur rotatif, quant à lui, détecte un nombre de « pas » par tour et envoie un signal à chaque pas. Il tourne aussi à l’infini. On peut sentir la différence à travers le toucher : le potentiomètre tourne de manière fluide, alors que le codeur rotatif tourne de manière saccadée.
Le codeur rotatif peut être directement utilisé par un microcontrôleur puisqu’il envoie des niveaux logiques. Sa résolution est déterminée par le nombre de pas par tour, alors que la résolution du potentiomètre dépend de la résolution de l’ADC nécessaire pour estimer sa position.
Note
On utilise un potentiomètre pour régler des paramètres analogiques, comme le volume d’un ampli, tandis que le codeur rotatif est employé pour déterminer précisément une position angulaire et une direction.
Fonctionnement théorique d’un encodeur rotatif
Ce tutoriel ne rentre pas trop dans les détails techniques, mais il y a beaucoup de choses à dire 🤓. Si vous voulez connaître réellement le fonctionnement physique, les différentes technologies et les topologies classiques (par exemple, le comprendre le fonctionnement d’un encodeur rotatif optique à quadrature de phase 😨), je vous redirige vers la présentation théorique d’un encodeur rotatif .
Branchements de l’encodeur rotatif KY-040 sur l’ESP32
Ce codeur rotatif possède 2 signaux pour connaître la position : CLK
et DT
. La broche SW
est reliée au bouton-poussoir intégré (SWitch).
Avertissement
Si vous utilisez un codeur rotatif seul (pas intégré dans un module), vous devrez rajouter des résistances pull-up sur les 3 broches logiques. En effet, comme sur le circuit basique du bouton-poussoir, elles sont nécessaires pour différencier correctement les niveaux logiques.
Voici une proposition de branchement sur la carte uPesy ESP32 Wroom DevKit :
Codeur Rotatif |
ESP32 |
---|---|
|
|
|
|
|
Pas utilisé ici |
|
|
|
|
Le schéma à faire est le suivant :
Note
Je vous conseille d’alimenter l’encodeur en 3.3V pour avoir des niveaux logiques de 3.3V (ESP32 friendly).
Et voici un exemple de montage sur breadboard :
Connaître la position angulaire du codeur rotatif KY-040
Avec ce type d’encodeur incrémental, il ne renvoie pas précisément la position angulaire en degré mais plutôt le nombre d’incrément fait par l’utilisateur. C’est dans le programme que l’on peut en déduire l’angle. Ici, on veut juste connaître la valeur de l’incrément.
Il existe différentes façons de procéder pour déterminer l’incrément du codeur. Idéalement on voudrait une méthode fiable, qui compte bien tous les pas sans être bloquante dans le programme. En effet, si on utilise une approche simple, qui regarde dans une boucle en permanence si des signaux logiques sont reçus depuis le codeur, il risque d’y avoir des pas qui ne seront pas pris en compte si on tourne très rapidement le codeur.
Note
Parfois le programme compte dans le mauvais sens s’il n’arrive plus à suivre la cadence. (Un bug classique)
L’approche optimale c’est d’utiliser des interruptions hardware qui vont se déclencher dès qu’un changement de niveau logique est détecté. Elles seront complètement détachées du CPU: le code sera en prime non bloquant.
Note
On peut avoir des interruptions sur n’importe quelle broche de sorties de l’ESP32.
Je vous propose d’utiliser une libraire MicroPython micropython-rotary très bien faite, pour lire de manière asynchrone les incréments du codeur rotatif. Elle est constituée de 2 fichiers qu’il faut transférer sur votre carte (comme toutes librairies uPython). Cela peut se faire facilement depuis Thonny IDE :
rotary.py
: https://github.com/miketeachman/micropython-rotary/blob/master/rotary.pyrotary_irq_esp.py
: https://github.com/miketeachman/micropython-rotary/blob/master/rotary_irq_esp.py
Voici un script très basique qui permet de tester la librairie et valider le montage électrique :
import time
from rotary_irq_esp import RotaryIRQ
r = RotaryIRQ(
pin_num_clk=22,
pin_num_dt=23,
reverse=True,
incr=1,
range_mode=RotaryIRQ.RANGE_UNBOUNDED,
pull_up=True,
half_step=False,
)
val_old = r.value()
while True:
val_new = r.value()
if val_old != val_new:
val_old = val_new
print("step =", val_new)
time.sleep_ms(50)
On peut paramétrer pas mal de choses lors de la création de l’objet RotaryIRQ
. Ils sont bien détaillés sur la page de présentation de la librairie . Le travail est fait en arrière-plan. On peut récupérer la valeur au moment voulu avec la fonction r.value()
.
Note
Pour que cela tourne dans l’autre sens, vous pouvez inverser soit les 2 fils CLK
et DT
ou le faire dans le script en changeant la valeur de renverse
lors de la création de l’objet .
Dans cet exemple, l’incrément du codeur n’a pas de limite : il incrémente à l’infini (jusqu’à l’overflow de la variable). On peut limiter l’incrément à une certaine plage, par exemple -20
à +20
en modifiant le paramètre range_mode
.
Détecter la pression du bouton-poussoir
La librairie n’intègre pas de détection du bouton-poussoir. Cependant on peut facilement le détecter comme avec un bouton classique avec du code MicroPython. On pourrait même utiliser une interruption pour détecter de manière asynchrone l’appui sur un bouton. Par exemple, l’appui sur le bouton peut réinitialiser l’incrément en cours du compteur.