BBC micro:bit

Editeur Micro-Python :

Présentation

Processeur : PU ARM® Cortex ™ M0 32 bits
Fréquence de l’horloge de processeur : 16 Mhz
RAM : 16 Ko
Courant des broches : 3,3 V
6 broches analogiques + 19 broches digitales

Entrée / Sortie

Sortie : écrire sur une broche

Digitale

pin2.write_digital(1)    impose 3V sur la broche 2 (en mode sortie).
pin2.write_digital(0)    abaisse la tension à 0V

~Analogique

pin2.write_analog(1015)   met une tension sur la broche 2 proportionnelle à la valeur 0-1023 (en mode sortie).

Entrée : mesure le signal sur une broche

Digitale

pin2.read_digital()    renvoie 0 ou 1 selon la tension mesurée sur la broche 2.
NB: read_digital() met automatiquement la broche en mode INPUT.


pin2.get_mode()    renvoie 'read_digital' ou 'write_digital' pour savoir si la broche est configurée en entrée ou sortie.


import machine
machine.time_pulse_us()
  attend que la broche passe à 1 puis envoie la durée en µs pendant laquelle la broche reste à 1.

from machine import time_pulse_us

# On met pin2 en mode INPUT    
pin2.read_digital()

# time_pulse_us attend que la broche passe à 1 puis
# renvoie la durée en µs pendant laquelle elle reste à 1
duree = time_pulse_us(pin2, 1)

~Analogique

pin2.read_analog()   renvoie une valeur de 0-1023 proportionnelle à la tension mesurée sur la broche 2.

x = pin2.read_analog()
print((x,)) # tuple 1 valeur

Attendre (delay) – pause

  • sleep(10)    pour attendre 10ms
  • import utime
    utime.sleep_us(10)
      pour mettre en pause pendant 10µs
  • running_time()   donne le temps écoulé depuis de démarrage de la carte en ms.

Affichage : 25 leds

  • display.clear()   efface tout

  • display.show("Hi!")     affiche un texte (lettre par lettre)
    display.show(Image.ARROW_W)     ou une image.
  • display.scroll("bonjour")   affiche un texte, en le faisant défiler progressivement.
  • display.set_pixel(x, y, n)   allume le pixel de coordonnée (x,y) d’une intensité n (de 1-9).
    NB : x et y sont dans 0-4. Donc le pixel central est (2,2).

dir(Image) tapé dans REPL : permet d’obtenir la liste des images


Pour utiliser les broches P3, P4, P6, P7, P9, P10 pour d’autres capteurs/actionneurs, il faut désactiver les leds :

  • display.off()   pour désactiver l’affichage
  • display.on()    pour réactiver les leds

display.is_on() et display.is_off() permettent de connaître l’état de l’écran.

Boutons A et B

from microbit import *
while True:
    if button_a.was_pressed():
        display.show(Image.HAPPY)
    else:
        display.show(Image.SAD)
    sleep(500)
  • was_pressed()   cette méthode renvoie True si le bouton a été actionné pendant que le programme était occupé à une autre tâche. 
  • is_pressed()     renvoie True si le bouton est actionné à l’instant où cette méthode est invoquée.
  • get_presses()   renvoie le nombre de pressions effectuées. Puis dès que cette méthode a été invoquée, le compteur est remis à 0.

Radio

La radio consomme de l’énergie et de la mémoire !!!

  • radio.on()   et  radio.off()     régulent l’usage de la radio

  • radio.config(channel=21)     règle la liaison radio sur un canal 0-83
  • radio.config(power=7)     règle la force du signal 0-7. La force 7 peut atteindre une distance de 70m.

  • radio.send("bonjour")     envoie un message

  • msg_recu = radio.receive()     stocke le message reçu dans la variable … ou None si rien n’a été reçu.

  • radio.receive_full()     renvoie un tuple (message, force du signal, timestamp) où :
    • la force du signal varie de 0 (fort) à 255 (faible).
    • Le timestamp est le résultat en microsecondes de time.ticks_us() .
  • radio.config(queue=4)     règle la file d’attente à 4 messages (par défaut c’est 3).

Coupler 2 cartes :
Il y a plusieurs possibilités :
* radio.config(channel=21)    pour choisir une fréquence
* radio.config(address=0x75626974)     une adresse fictive codée sur 4 octets.
* radio.config(group=7)     un groupe fictif entre 0-255. Seules les cartes ayant la même adresse et le même groupe communiqueront. L’adresse représente un immeuble, et le groupe l’appartement.

from microbit import *
import radio

radio.on()
radio.config(channel=19)  # Choisir un n° de channel (0-83)
radio.config(power=7)     # Règle la force du signal

message = "Hello NSI !"

# on appuiera sur Bouton A pour arrêter le programme
while not button_a.was_pressed():
    # on appuie sur bouton B pour envoyer le message
    if button_b.was_pressed():
        radio.send(message)
    msg_recu = radio.receive()
    if msg_recu is not None:
        display.show(msg_recu)
        print(msg_recu)
    sleep(500)
radio.off()

NB : L’instruction radio.receive() est non bloquante : elle n’attend pas la réception d’un message.
Si aucun message n’a été reçu au moment de l’appel à cette méthode, elle renvoie la valeur None.
Pour qu’elle soit bloquante : voir l’exemple de code ci-dessous.

Pour attendre la réception d’un message, c’est-à-dire bloquer le radio.receive() :

msg_recu = None
while msg_recu is None:
    msg_recu = radio.receive()
    sleep(500)

Gestion de la queue :
* Si la queue est pleine, les messages suivants seront ignorés (et perdus).
* Lire un message l’enlève de la queue.
* L’ordre de lecture est FIFO « First In First Out » : le premier message arrivé est le premier lu.

Pour plus d’information : https://microbit-micropython.readthedocs.io/en/latest/radio.html

Bus I2C et SPI

Le bus I2C (broches 19 et 20) permet de brancher plusieurs périphériques partageant le même bus de communication.
Ce bus est, par ailleurs, utilisé par Micro:bit pour le Magnétomètre et l’accéléromètre, il est possible d’ajouter d’autres senseurs I2C.

Le bus SPI, apprécié pour sa rapidité, est également disponible sur la Micro:bit.

Capteurs internes

Température

temperature()     affiche la température mesurée par le capteur interne de la micro:bit.

from microbit import *
while True:
    temp = temperature()
    print((temp,))
    sleep(1000)

Accéléromètre (gravité)

définition

On peut afficher les effets de la gravité terrestre sur l’accéléromètre de la micro:bit.
Elle est mesurée en milli-g où 1g est la force de la gravité de la Terre.

  • x = transversal
  • y = longitudinal
  • z = vertical
les 3 axes de l'accéléromètre de micro:bit

Si la micro:bit repose à plat sur une table :

Bleu X = Vert Y = 0

Orange Z = -1000 car la gravité s’exerce uniquement sur Z.
En retournant la micro:bit : Orange Z = +1000

exemple

from microbit import *

xSeulmt = True
while True:
    if button_a.was_pressed():
        xSeulmt = not xSeulmt
    if xSeulmt:
        print( (accelerometer.get_x(), ) )
    else:
        print( accelerometer.get_values() )
    sleep(100) # on attend 100 ms
  • accelerometer.get_x()   renvoie la mesure transversale.
  • accelerometer.get_y()   renvoie la mesure longitudinale.
  • accelerometer.get_z()   renvoie la mesure verticale.
  • accelerometer.get_values()   renvoie le tuple.
from microbit import *
while True:
    accX = accelerometer.get_x()
    if accX > 40:
        display.show(">")
    elif accY < -40:
        display.show("<")
    else:
        display.show("-")

détection de gestes

La microbit peut également détecter des gestes tels que « secouer » la carte.
Les gestes sont : up, down, left, right, face up, face down, freefall, 3g, 6g, 8g, shake
Comme pour les boutons, les gestes sont accumulés dans une pile.

  • accelerometer.was_gesture("shake")   renvoie True si une secousse a eu lieu.
  • accelerometer.is_gesture("shake")   renvoie True si la carte est secouée au moment où la méthode est invoquée.
  • accelerometer.get_gestures()   renvoie la pile de gestes

Boussole (compass)

/!\ Le compas doit toujours être calibré au préalable.

from microbit import *

compass.calibrate()
while True:
    angle = compass.heading()
    display.scroll(str(angle))
  • compass.calibrate()   pour calibrer le compas.
  • compass.is_calibrated()   renvoie True si le compas est calibré.
  • compass.heading()   donne la mesure entre 0 et 360.

Puis utiliser les images pré-définies pour indiquer le nord par une flèche : Image.ARROW_N, Image.ARROW_NE, Image.ARROW_E, Image.ARROW_SE, Image.ARROW_S, Image.ARROW_SW, Image.ARROW_W, Image.ARROW_NW.

from microbit import *
if not compass.is_calibrated():
    compass.calibrate()
while True:
    aiguille = ((15 - compass.heading()) // 30) % 12
    display.show(Image.ALL_CLOCKS[aiguille])

Magnétisme (compass)

from microbit import *

while True:
    sleep(100)
    print((compass.get_field_strength(),)) # tuple 1 valeur

compass.get_field_strength()  renvoie la force du champ magnétique (détecte la présence d’un aimant).

compass.get_x() et compass.get_y() et compass.get_z()   décompose la force selon les axes x, y et z

Son – Musique

Le module music

Sur la plupart des cartes shield incluant un module sonore, la broche est pin0 .

interrupteur du mode buzzer sur IObit_v2
l’interrupteur du mode buzzer sur IObit_v2

Dans le module music :

  • music.pitch(fréquence_en_Hz, durée_en_ms, broche)   génère un son de fréquence maximale 10 000 Hz (un entier).
    • La limitation de durée est facultative, par défaut elle est illimitée -1 .
    • La broche est facultative, par défaut c’est pin0 .
    • Ajouter l’argument wait=False pour que l’instruction ne bloque pas le programme.
  • music.play(music.BIRTHDAY)   joue la mélodie.
    Ajouter l’argument wait=False pour que l’instruction ne bloque pas le programme.
    Ajouter l’argument loop=True pour que la musique soit jouée en boucle.
  • music.stop(broche)   stoppe le son sur la broche.
    La broche est facultative, par défaut c’est pin0 .
from microbit import *
import music

for i in range (1,11):
    music.pitch(100*i, 2000, pin0) # i*100 Hz pendant 2 secondes
    print('Un son de fréquence',i*100,'Hz pendant 2 secondes')
from microbit import *
import music
while True:
    music.pitch(440,1000)
    sleep(1000)
    music.pitch(660,500)
    sleep(1000)
    music.pitch(880,750)
    sleep(1000)
    music.stop()
    sleep(1000)

La liste des mélodies :

dir(music) tapé dans REPL :

music.DADADADUM, music.ENTERTAINER, music.PRELUDE, music.ODE, music.NYAN, music.RINGTONE, music.FUNK, music.BLUES, music.BIRTHDAY, music.WEDDING, music.FUNERAL, music.PUNCHLINE, music.PYTHON, music.BADDY, music.CHASE, music.BA_DING, music.WAWAWAWAA, music.JUMP_UP, music.JUMP_DOWN, music.POWER_UP, music.POWER_DOWN

Exercice : Placer toutes ces mélodies dans une liste, puis écrire un code qui joue une musique aléatoire à chaque fois qu’on appuie sur le bouton A.
On pourra utiliser la fonction random.choice(tab) qui renvoie un élément aléatoire de la liste tab (importer le module random).

Mélodies

durée = 35 min

Voici quelques mélodies qu’on souhaiterait faire jouer :

melodie1 = "do - do - do - re - mi - re - do - mi - re - re - do"

melodie2 = "mi mi mi mi mi mi mi sol do re mi fa fa fa mi mi mi re re re mi re sol mi mi mi mi mi mi mi sol do re mi"

1ère étape : Choisir une de ces mélodies et la transformer en liste de notes ["do", "do", "do", "ré"] en utilisant la méthode split. Voici un exemple d’utilisation de split :

### Transforme la chaine en liste selon le séparateur "; " (avec l'espace)
liste = "thé; café; jus d'orange; eau gazeuse".split("; ")
# le résultat est la liste de str :
# liste = ['thé', 'café', "jus d'orange", 'eau gazeuse']

### Idem selon le séparateur espace
donnée = "13 -5 8 12.1"
liste = donnée.split(" ")
# attention, le résultat est une liste de str :
# liste = ['13', '-5', '8', '12.1']

2ème étape : Choisir une octave et créer un dictionnaire où les clés sont les 7 notes de cette octave et les valeurs sont les fréquences :

NOTE   1ère octave    2e octave  
do131262
147294
mi165330
fa175349
sol196392
la220440
si247494
(do)(262)(523)

3ème étape : Au moyen d’une boucle, parcourir la liste des notes de la mélodie et grâce au dictionnaire jouer la fréquence de la note avec l’instruction music.pitch(fréquence_en_Hz, durée_en_ms) .

4ème étape : Voici une 3e mélodie avec un peu de rythme cette fois : en multipliant l’entier par 125 ms, vous obtiendrez la durée de la note.
Ex : sol 4 signifie qu’il faut jouer la note « sol » pendant 500 ms.

Adapter le code précédent pour jouer cette nouvelle mélodie.
Après le premier split, vous devrez utiliser un 2e split sur chaque élément pour séparer la note et la durée.
Ex : En appliquant un split sur sol 4 , on obtient la note sol et la durée 4.

melodie3 = "sol 4, sol 4, sol 4, mi 3, si 1, sol 4, mi 3, si 1, sol 8"

Petite correction pour les oreilles absolues : le « si » est plutôt un « si bémol ».

Distance (Ultrason)

4-pin

⚠ Bien enlever le jumper au dos pour être en mode HC-SR04. (Le jumper active le mode UART)

Notez que :
* a trigger = un déclencheur
* an echo = un rappel/retour

from microbit import *
from machine import time_pulse_us
import utime

################################
## 4-pin : avec ECHO et TRIG ###
################################

### Constantes
TRIG_PIN = pin1
ECHO_PIN = pin2

### Définition d'une fonction
def mesure_distance():
    ### On envoie une impulsion de 10 µs sur la broche TRIG
    ### pour démarrer une salve d'ultrasons
    TRIG_PIN.write_digital(1)
    utime.sleep_us(10) # on attend pendant 10 µs
    TRIG_PIN.write_digital(0)
    # fin de l'impulsion

    ### Lecture du résultat et calcul de la distance
    # configuration de la broche ECHO en mode INPUT
    ECHO_PIN.read_digital()
    # Puis time_pulse_us attend que la broche ECHO passe à 1 puis
    # renvoie la durée en µs pendant laquelle la broche est restée à 1
    duree = time_pulse_us(ECHO_PIN, 1)
    # Calcul la distance en cm
    distance = int(duree * 0.01715)
    return distance


### Programme principal
while True:
    # bouton A pour effectuer et afficher une mesure
    if button_a.was_pressed(): 
        d = mesure_distance()
        # affichage dans la console REPL de l'éditeur
        print(d)
        # affichage sur la carte micro:bit
        display.show(' '+str(d)+" # ", delay=500, loop=True, wait=False)

    # bouton B pour effacer
    if button_b.was_pressed():
        display.clear()

3-pin

from microbit import *
from machine import time_pulse_us
##################################
## 3-pin : broche SIG sur pin2 ###
##################################
### Définition d'une fonction
def mesure_vitesse(distance):
    """ Calcule la vitesse du son en m/s
    sur une distance 'distance' exprimée en cm """
 
    # envoie une impulsion de 10 ms sur pin2 pour démarrer une salve US #
    pin2.write_digital(1)
    sleep(10)
    pin2.write_digital(0)
    # fin de l'impulsion

    # configuration de pin2 en INPUT
    pin2.read_digital()
    # attend que pin2 passe à 1 puis renvoie la durée 
    # en µs pendant laquelle pin2 reste à 1
    duree = time_pulse_us(pin2, 1)

    # calcul de la vitesse (cm/s) grâce à la duree aller-retour (µs) : 
    vitesse = 2 * distance / (duree/1000000)
    # conversion en m/s (et arrondie)
    vitesse = round(vitesse/100) 
    return vitesse

### Programme principal
# distance utilisée pour mesurer la vitesse du son .. en cm !
DISTANCE = 50 
while True:
    # bouton A pour effectuer une mesure
    if button_a.was_pressed():
        vitesse = mesure_vitesse(DISTANCE)
        # retour REPL dans la console de l'éditeur
        print(vitesse)
        display.show("v="+str(vitesse)+" ", delay=500, loop=True, wait=False)
    
    # bouton B pour effacer
    if button_b.was_pressed():
        display.show(" "+str(vitesse)+" ", delay=500, loop=True, wait=False)

Carte moteur motor:bit

carte Motor:Bit de ELECFREAKS
  • Motor Drive Chip: TB6612
  • 2 voies pour des moteurs TT
  • chaque moteur alimenté à 1.2A maximum
  • un buzzer contrôlé par P0.

Broches bleues SVG

Si le commutateur VCC (juste au-dessus) est sur 5V, alors les broches rouges donnent du 5V et le niveau de signal des broches bleues est sur 5V aussi.
Si le commutateur VCC est sur 3.3V, alors les rouges délivrent du 3.3V et le niveau du signal des bleues est aussi 3.3V.

  • P13-P16 : 4 ports GPIO
  • P19-P20 : 1 connecteur de communication IIC

Broches jaunes SVG

  • P3-P7, P9-P11 : 8 broches alimentées en 3.3V .
  • P3, P4, P10 : peuvent être utilisées en mode « Entrée de signal analogique ».

ATTENTION : pour utiliser les broches P3, P4, P6, P7, P9, P10 (dédiées aux 25 leds), il faut ajouter :

  • display.off() pour désactiver l’affichage
  • display.on() pour réactiver les leds

Broches Moteurs

P8Direction de M1sens positif de rotation en tension HIGH (digital = 1);
sens négatif sur LOW (digital = 0).
P1Vitesse de M1PWM (analog = entre 0 et 1023)
P2Vitesse de M2PWM (analog = entre 0 et 1023)
P12Direction de M2sens positif de rotation en tension HIGH;
sens négatif sur LOW.
from microbit import *
 
def avance(v):
    # le mode "avance" à la vitesse v (entier entre 0 et 1023)
    pin8.write_digital(1)  # direction M1
    pin12.write_digital(0) # direction M2
    pin1.write_analog(v) # vitesse M1
    pin2.write_analog(v) # vitesse M2
 
def recule(v):
    pin8.write_digital(0)  # direction M1
    pin12.write_digital(1) # direction M2
    pin1.write_analog(v) # vitesse M1
    pin2.write_analog(v) # vitesse M2
 
avance(300) # le robot avance pendant 2s (vitesse faible)
sleep(2000)
recule(300) # le robot recule pendant 2s
sleep(2000)
avance(0)   # le robot s'arrête

Carte Joystick:bit

Joystick:bit v2.0 de ELECTROFREAKS
  • pin2 : pin2.read_analog() détecte le bouton pressé
    buttons = {2: "A", 517: "B", 686: "C", 769: "D", 853: "E", 820: "F", 1021 : "aucun"}
  • pin0 et pin1 donnent la position du joystick :
    • pin0.read_analog() sur X : 3~1021 et Xcentre = 529
    • pin1.read_analog() sur Y : 3~1021 et Ycentre = 506

/!\ Ces valeurs sont approximatives car elles varient d’une carte Joystick:bit à l’autre !

Tester la Joystick:bit

Les coordonnées du joystick
from microbit import *
import radio
#######################################
# TEST : JOYSTICK:BIT
while True:
    press = pin2.read_analog()
    print("bouton : "+ str(press))
    X = pin0.read_analog()
    Y = pin1.read_analog()
    print("joystick : "+str(X)+" , "+str(Y))
    sleep(100)

Emetteur radio (carte Joystick:bit)

from microbit import *
import radio

#######################################
# JOYSTICK:BIT
def button_press():
    press = pin2.read_analog()
    if press > 938:  # le plus fréquent car aucun bouton : press=1021
        return ""
    elif press < 256:
        return "A"
    elif press < 597:
        return "B"
    elif press < 725:
        return "C"
    elif press < 793:
        return "D"
    elif press < 836:
        return "F"
    else:
        return "E"

def joystick_push():
    # Par défaut : x = 529 (3~1021)   y = 506 (3~1022)
    # map (1~1023) to (-1022~1022)
    x = 2 * pin0.read_analog() - 1024
    y = 2 * pin1.read_analog() - 1024
    if -100 < x < 100:
        x = 0
    if -100 < y < 100:
        y = 0
    return x, y


radio.config(channel=7, group=0, queue=1, power=7)
radio.on()
while True:
    X, Y = joystick_push()       
    message = str(X) + "|" + str(Y) + "|" + button_press()
    radio.send(message)      # ex : "-700|400|"

Récepteur radio (carte motor:bit)

from microbit import *
import radio

###################################
# MOTOR:BIT   M1=gauche   M2=droite

def drive(vitesseX, vitesseY): # vitesse : -1023 ~ 1023
    if vitesseX < 0:
        vitesseX = - vitesseX
        pin8.write_digital(0)  # direction M1
    else:
        pin8.write_digital(1)  # direction M1

    if vitesseY < 0:
        vitesseY = - vitesseY
        pin12.write_digital(1)  # direction M2
    else:
        pin12.write_digital(0)  # direction M2

    if vitesseX > 900:
        vitesseX = 900
    if vitesseY > 900:
        vitesseY = 900
    pin1.write_analog(vitesseX) # vitesse M1
    pin2.write_analog(vitesseY) # vitesse M2


radio.config(channel=7, group=0, queue=1, power=7)
radio.on()
ancien_bouton = ""
while True:
    msg_recu = radio.receive()
    if msg_recu is not None:
        [joystickX, joystickY, bouton] = msg_recu.split("|")
        
        # moteur
        joystickX = int(joystickX)
        joystickY = int(joystickY)
        drive(joystickY + joystickX//3 , joystickY - joystickX//3)
        
        # bouton
        # /!\ redondances car un appui bref de btn A donne "A" "A" "A" "A"
        if bouton != ancien_bouton:
            # faire qqch
        ancien_bouton = bouton

Servo sur 5V

Code direct

Un servomoteur fonctionne par signal PWM (Pulse Width Modulation) à une fréquence de 50 Hz. Cela représente une période = 20 ms = 20 000 µs.
Sur chaque intervalle de 20 000 µs, c’est la durée du signal haut qui détermine l’angle. Pour la majorité des servo, cette durée est comprise entre 540 µs et 2400 µs.
Or write_analog a pour argument un entier entre 0 et 1023 (où 1023 est 100%).
Donc on recalcule les angles extrêmes ainsi :
1023 * 540 / 20000 = 28
1023 * 2400 / 20000 = 122

from microbit import *

pin16.set_analog_period(20) # 20 ms est la période PWM d'un servo

# La fourchette 540-2400 sur 20000 du servo est transposée
# en l'intervalle 28-122 sur 1023 de write_analog ainsi :
#  28 =>  547µs => angle minimum (0°) (tourne vers droite) 
#  76 => 1485µs => angle central (90°) 
# 122 => 2385µs => angle maximum (180°) (tourne vers gauche) 

pin16.write_analog(28) # tout à gauche
sleep(1000) # donne le temps au servo de tourner (0.24s pour 60°)
pin16.write_analog(122) # tout à droite
sleep(1000) # donne le temps au servo de tourner
pin16.write_analog(76) # au milieu
sleep(1000) # donne le temps au servo de tourner

pin16.write_digital(0)  # éteint la broche

En utilisant une classe Servo

Il existe une classe pour les servo en micropython disponible sur cette page, cependant il faut ajouter un sleep() entre write_analog et write_digital(0) .

from microbit import *
class Servo:
    """
    Args:
        pin (pin0 .. pin3): The pin where servo is connected.
        freq (int): The frequency of the signal, in hertz.
        min_us (int): The minimum signal length supported by the servo.
        max_us (int): The maximum signal length supported by the servo.
        angle (int): The angle between minimum and maximum positions.
    Usage :
        sv1 = Servo(pin0)   # ou sv1 = Servo(pin0, min_us=1000, max_us=2000)
        sv1.write_angle(50) # turn servo to 50 degrees 
    """
    def __init__(self, pin, freq=50, min_us=600, max_us=2400, angle=180):
        self.min_us = min_us
        self.max_us = max_us
        self.us = 0
        self.freq = freq
        self.angle = angle
        self.analog_period = 0
        self.pin = pin
        analog_period = round(1000/self.freq)   # hertz to milliseconds
        self.pin.set_analog_period(analog_period)

    def write_us(self, us):
        us = min(self.max_us, max(self.min_us, us))
        duty = round(us * 1024 * self.freq // 1000000)
        self.pin.write_analog(duty)
        sleep(750)
        self.pin.write_digital(0)  # turn the pin off

    def write_angle(self, degrees=None):
        degrees = degrees % 360
        total_range = self.max_us - self.min_us
        us = self.min_us + total_range * degrees // self.angle
        self.write_us(us)

sv = Servo(pin1, min_us=750, max_us=2250)
t = 90
while True:
    if button_a.was_pressed():
        t = t + 5
        sv.write_angle(t)