Tuto pas à pas Python : les potentiomètres rotatifs

Dans ce tuto nous verrons comment utiliser simplement les potentiomètres fournis dans le pack d’accessoires avec CircuitPython.

Avant tout : Installation des outils

Ce tuto est réalisé sur l’IDE Mu pour Circuit Python.
Si vous n’avez pas encore installé l’un et l’autre veuillez vous référer au guide d’installation Mu et CircuitPython disponible sur le site.

Les branchements

En parallèle de notre code il faut brancher les potentiomètres à la console, si possible avec le backpack.
On branche les alimentations (VCC) sur des pins 3v3, les masses (GND) sur des masses et les sorties SIG (signal) sur A1 pour le potentiomètre de gauche et A2 pour celui de droite.

Potentiomètre Backpack Couleur Description
GND GND BRUN Masse
VCC 3V3 ROUGE Alimentation
SIG A1/A2 JAUNE Sortie de signal



Etape 1 : La base du programme

Commencez par ouvrir Mu et créer un nouveau fichier.

Copiez-y le code suivant :

from gamebuino_meta import begin, waitForUpdate, display, buttons, color
import analogio
import board
import math

while True:
    waitForUpdate()
    display.clear()

Cela va importer les libraires Gamebuino nécessaires au fonctionnement de notre programme, ainsi qu’une libraire “board” et une libraire “analogio” dont on aura besoin pour travailler avec nos potentiomètres. On ajoute également une librairie “math” qui va se révéler indispensable.

La partie commencant par “while True:” constitue la boucle principale de notre programme, sans elle il ne se passerait rien.

Etape 2 : Lire les données des potentiomètres

Ici nous allons faire en sorte de récupérer les valeurs brutes envoyées par nos deux potentiomètres pour travailler avec.

Nos potentiomètres sont branchés sur des pins analogiques, on va donc utiliser la librairie analogio pour lire les données qu’ils envoient vers leurs pins respectifs.

On a besoin pour cela d’assigner le bon pin à chaque potentiomètre. Cela se fait de la manière suivante :

#assign A1 pin to left potentiometer
left_pin = analogio.AnalogIn(board.A1)
#assign A2 pin to right potentiometer
right_pin = analogio.AnalogIn(board.A2)

Pour chacun des deux potentiomètres on appelle la fonction AnalogIn depuis analogio et on lui passe en paramètre le pin analogique qui convient avec la librairie board.

A partir de là on peut obtenir très facilement la valeur de chaque potentiomètre.
On créée pour cela une fonction read_pot prenant en paramètre un pin telle que :

#read from designated analog pin and returns the value
def read_pot(pin):
    return pin.value

Cette fonction très simple renvoie tout simplement la valeur obtenue en lecture du pin passé en paramètre.

On pourrait afficher dès maintenant les valeurs obtenues. Seulement voilà, ces valeurs vont être encodées sur 16 bits et donc varier entre 0 et 65536, ce qui je ne vous le cache pas n’est pas vraiment compréhensible.

On va donc déclarer une nouvelle fonction, très simple elle aussi, pour remédier à cela.
Cette fonction, appelée make_angle prend la forme suivante :

#report raw value from 16bits to 360°
def make_angle(value):
    return value * 360 // 65536

Par un simple produit en croix on reporte notre valeur 16 bits vers une fourchette de 0 à 360 correspondant à des degrés, ce qui est quand même plus facile pour représenter le mouvement d’un potentiomètre rotatif.

Etape 3 : Afficher les valeurs et créer une interface

Pour le moment tout ce qu’on a fait dans la partie précédente ne sert à rien, car les fonctions ne sont pas appelées et surtout les valeurs ne sont pas utilisées.

On va donc tout de suite y remédier.

On peut commencer à travailler dans notre boucle principale while True qui devrait être identique à ceci :

while True:
    waitForUpdate()
    display.clear()

On va commencer par afficher les valeurs de nos potentiomètres grâce à nos fonctions read_pot et make_angle.

Pour cela on utilisera la fonction basique display.print de la façon suivante :

while True:
    waitForUpdate()
    display.clear()

    #display the value of the left potentiometer after transforming it to angle
    display.print(make_angle(read_pot(left_pin)))
    display.print(' degree\n')
    #display the value of the right potentiometer after transforming it to angle
    display.print(make_angle(read_pot(right_pin)))
    display.print(' degree')

Cela va lire les valeurs brutes avec read_pot, les transformer en angle avec make_angle puis les afficher avec display.print. On ajoute aussi un peu de texte pour spécifier " degree" après l’affichage de chaque valeur.
Notez la présence d’un espace avant “degree” et le “\n” qui est un retour à la ligne pour séparer nos deux valeurs.

Bon, c’est déjà un début mais on peut pas vraiment parler d’interface.

Aussi on va s’atteler à donner une représentation plus “graphique” à nos deux potentiomètres.

On va créer une fonction dédiée que l’on appellera draw_interface tout simplement :

def draw_interface():

Pas de paramètre à prendre ici.

On pense toute de suite à ajouter l’appel à cette fonction dans notre boucle principale tel que :

while True:
    waitForUpdate()
    display.clear()

    #display the value of the left potentiometer after transforming it to angle
    display.print(make_angle(read_pot(left_pin)))
    display.print(' degree\n')
    #display the value of the right potentiometer after transforming it to angle
    display.print(make_angle(read_pot(right_pin)))
    display.print(' degree')

    draw_interface()

Avant de remplir notre fonction il faut poser ce qu’on a l’intention de réaliser. Ici on veut représenter nos potentiomètres sous forme de deux cadrans circulaires avec des aiguilles se déplaçant en fonction du mouvement enregistré. Le résultat devrait ressembler à ceci :
00004

Au-dessus de notre fonction draw_interface on va déclarer quelques variables qui serviront de référence pour tracer les cercles de nos cadrans :

#variable declarations for circle drawing
#radius
CIRCLE_R = 15
#left circle centre horizontal position
L_CIRCLE_X = (display.width()//4)
#rigght circle centre horizontal position
R_CIRCLE_X = (display.width()//4*3)
#both circle center vertical position
CIRCLE_Y = (display.height()//2)

On définit ainsi un rayon de base de 15 unités, puis les positions horizontales respectives des deux centres de nos cercles et enfin la position verticale de ces centres qui est la même pour les deux cadrans.
On utilise pour cela des références à la taille de notre écran (display.height et display.width) qui rendent notre affichage dynamique, CAD que changer la taille de l’écran ne changera pas la position relative de nos cercles.

On peut à présent tracer nos cercles dans notre fonction draw_interface de la façon suivante :

def draw_interface():
    display.drawCircle(L_CIRCLE_X, CIRCLE_Y, CIRCLE_R)
    display.drawCircle(R_CIRCLE_X, CIRCLE_Y, CIRCLE_R)

Cela affiche à l’écran deux cercles tels que :
00003
La fonction drawCircle prend en paramètres les différentes valeurs établies au-dessus pour tracer nos cercles aux coordonnées voulues avec un rayon de 15 unités comme on l’a spécifié.

On va ensuite tracer les aiguilles de nos deux cadrans. La manœuvre est un poil plus compliquée car elle fera appel à un petit calcul mathématique “avancé”.

Pour commencer on va devoir définir deux variables correspondant aux valeurs angulaires de nos potentiomètres. On déclare ces deux variables à la suite dans notre fonction draw_interface :

    #determining the two angular values of our potentiometers
    left_angle = make_angle(read_pot(left_pin))
    rigt_angle = make_angle(read_pot(right_pin))

En utilisant la fonction make_angle et la fonction read_pot comme dans l’étape 1 on récupère ainsi sous forme de variables nos deux valeurs.

On peut à présent utiliser ces deux variables pour calculer la position sur chaque cercle de l’extrémité de nos aiguilles.
Pour cela on effectue un calcul avec les fonctions sin et cos chères à tous les matheux (et incompréhensibles pour le commun des mortels).

Le calcul de ces coordonnées se fait ainsi :

    #determining the X and Y coordinates on the circle for the LEFT hand
    L_point_x = L_CIRCLE_X + CIRCLE_R * math.cos(left_angle * math.pi // 180)
    L_point_y = CIRCLE_Y + CIRCLE_R * math.sin(left_angle * math.pi // 180)

    #determining the X and Y coordinates on the circle for the RIGHT hand
    R_point_x = R_CIRCLE_X + CIRCLE_R * math.cos(rigt_angle * math.pi // 180)
    R_point_y = CIRCLE_Y + CIRCLE_R * math.sin(rigt_angle * math.pi // 180)

On obtient ainsi deux jeux de coordonnées X et Y correspondant à nos deux potentiomètres.

On peut maintenant passer à l’affichage de nos aiguilles avec la fonction drawLine. Subtilité importante cette fonction ne prend pas les valeurs décimales (en principe des floats en programmation), on doit donc “caster” (transformer à la volée) les paramètres qu’on lui donne vers des entiers (int).
Cela prend la forme suivante :

    #switch display colo to RED
    display.setColor(color.RED)
    #draw hands with int cast for each coordinate points
    #drawLine doesn't support float entries
    display.drawLine(int(L_CIRCLE_X), int(CIRCLE_Y), int(L_point_x), int(L_point_y))
    display.drawLine(int(R_CIRCLE_X), int(CIRCLE_Y), int(R_point_x), int(R_point_y))

Notez qu’on utilise la fonction setColor pour passer la couleur d’affichage en rouge, c’est plus joli et un surtout plus impactant visuellement.
On trace ainsi une ligne entre le centre de chaque cercle et un point de son pourtour qui correspond à la valeur actuelle de chaque potentiomètre.

Une fois le programme téléchargé sur la console on obtient bien le résultat attendu :
00004

Félicitations ! Vous avez (encore) survécu à un de mes tutos, et en python qui plus est. A vous de jouer à présent.