Editeur Micro-Python :
- logiciel à installer : Mu Editor
- éditeur en ligne : https://python.microbit.org/v/2.0
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
attend que la broche passe à 1 puis envoie la durée en µs pendant laquelle la broche reste à 1.
machine.time_pulse_us()
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
pour mettre en pause pendant 10µs
utime.sleep_us(10)
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’affichagedisplay.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 renvoieTrue
si le bouton a été actionné pendant que le programme était occupé à une autre tâche.
is_pressed()
renvoieTrue
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()
etradio.off()
régulent l’usage de la radio
radio.config(channel=21)
règle la liaison radio sur un canal 0-83radio.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 … ouNone
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
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
.
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.
- La limitation de durée est facultative, par défaut elle est illimitée
music.play(music.BIRTHDAY)
joue la mélodie.
Ajouter l’argumentwait=False
pour que l’instruction ne bloque pas le programme.
Ajouter l’argumentloop=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’estpin0
.
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 |
do | 131 | 262 |
ré | 147 | 294 |
mi | 165 | 330 |
fa | 175 | 349 |
sol | 196 | 392 |
la | 220 | 440 |
si | 247 | 494 |
(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
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
- 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’affichagedisplay.on()
pour réactiver les leds
Broches Moteurs
P8 | Direction de M1 | sens positif de rotation en tension HIGH (digital = 1); sens négatif sur LOW (digital = 0). |
P1 | Vitesse de M1 | PWM (analog = entre 0 et 1023) |
P2 | Vitesse de M2 | PWM (analog = entre 0 et 1023) |
P12 | Direction de M2 | sens 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
- 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 = 529pin1.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
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)