MicroPython-Hack-Debug

De MCHobby - Wiki
Révision datée du 15 février 2016 à 13:24 par Admin (discussion | contributions) (Controle+C et Controle+D)
(diff) ← Version précédente | Voir la version actuelle (diff) | Version suivante → (diff)
Aller à : navigation, rechercher

MCHobby investit du temps et de l'argent dans la réalisation de traduction et/ou documentation. C'est un travail long et fastidieux réalisé dans l'esprit Open-Source... donc gratuit et librement accessible.
SI vous aimez nos traductions et documentations ALORS aidez nous à en produire plus en achetant vos produits chez MCHobby.

Introduction

Vous écrivez-vos premiers bouts de code... ou modifiez un code existant et PATATRA, PLUS RIEN NE MARCHE!

En plus, en le relisant votre code, l'erreur ne saute pas aux yeux.

C'est frustrant et la première question qui vient, c'est comment déboguer facilement du code MicroPython.

Le code

Pour cet article, j'ai repris le code de Déparasitage des entrées que j'ai adapté pour:

  1. Lire l'état de la broche X4 plutôt que X1
  2. Attendre deux secondes après avoir changé l'état de la LED(4)
  3. Allumer la LED(2) pendant le temps d'attente.

PyBoard-LEDs.jpg

Comme j'ai travaillé comme un cochon, j'ai introduis pas mal d'erreur dans mon bout de code, ce fut l'occasion de me pencher sur les méthodes de débogage... puis de la rédaction de ce tutoriel.

Débogage à l'ancienne

C'est les vieilles méthodes qui marchent le mieux!

Au tout début de la programmation, il suffisait d'afficher des messages à l'écran (un peu partout), cela permettait de déterminer la partie du code qui fonctionne et donc d'approcher par étapes successives la partie du code qui était problématique.

C'est bien beau tout cela mais sur la PyBoard, il n'y a pas d'écran! Mais nous avons néanmoins 4 LEDs utilisateurs.

Utiliser les LEDs

Allumer les LEDs (voire en combinaison) dans votre programme permet de déterminer (1) s'il fonctionne et (2) de déterminer où il s'est arrêté.

J'ai repris le programme du point "déparasiter une entrée" et je l'ai modifié pour faire une lecture en broche X4 puis attendre deux seconde.

Malheureusement, ma modification faite sur le coin d'une table ne fonctionnait pas (une erreur de distraction) et j'ai donc essayé d'allumer la LED(2) pendant la pause.

import pyb

def wait_pin_change(pin):
    # attendre que la broche change de valeur
    cur_value = pin.value()
    # Elle doit être stable pendant un temps continu de 20ms
    active = 0
    while active < 20:
        if pin.value() != cur_value:
            active += 1
        else:
            active = 0
        pyb.delay(1)

pin_x4 = pyb.Pin('X4', pyb.Pin.IN, pyb.Pin.PULL_DOWN)
while True:
    wait_pin_change(pin_x4)
    pyb.LED(4).toggle()
    pyb.LED(2).on()
    delay(2000)
    pyb.LED(2).off()

Notez les instructions pyb.LED(2).on() et pyb.LED(2).off() qui me permettent de savoir quand le programme cycle.

Il y a une énorme erreur dans ce code (hé oui, parfois pas les yeux en face des trous)... la LED(2) s'allume puis la PyBoard fait des trucs bizarres et reste dans un état non fonctionnel.

Les print()

Si vous vous êtes penché sur le tutoriel concernant l'invite REPL, vous y aurez appris qu'il est possible de dialoguer avec la PyBoard par l'intermédiaire d'un simple terminal.

Figurez-vous que si un programme Python utilise une instruction print() alors cette dernière est envoyée sur la console.

J'ai donc modifié mon programme Python pour qu'il affiche (print) des informations ici et là histoire de trouver où se localise mon problème.

import pyb

def wait_pin_change(pin):
    # attendre que la broche change de valeur
    cur_value = pin.value()
    # Elle doit être stable pendant un temps continu de 20ms
    active = 0
    while active < 20:
        if pin.value() != cur_value:
            active += 1
        else:
            active = 0
        pyb.delay(1)

pin_x4 = pyb.Pin('X4', pyb.Pin.IN, pyb.Pin.PULL_DOWN)
while True:
    print( 'attendre pression bouton' )
    wait_pin_change(pin_x4)
    print( 'bouton enfonce' )
    pyb.LED(4).toggle()
    print( 'LED modifiee' )
    pyb.LED(2).on()
    print( 'LED 2 allumee')
    pyb.delay(2000)
    pyb.LED(2).off()
    print( 'LED 2 eteinte')

Je sauve mon programme, j'éjecte le disque Flash de ma PyBoard, je presse le bouton Reset.

J'ouvre alors un terminal sur la Pyboard avec Pytty ou Screen (voyez l'article invite REPL pour plus d'information).

Voici ce que j'y ai vu une fois le bouton X4 enfoncé...

MicroPython-HAck-Debug-01.jpg

Nous pouvons clairement voir:

  1. Un monstrueux message d'erreur (mais nous y reviendrons juste ci-dessous).
  2. Les différents messages print() envoyés par la PyBoard sur le terminal.
  3. Le message attendre pression bouton ne s'y retrouve pas
    Normal, ce message a été envoyé dès le démarrage du programme... bien avant que j'ai eu le temps de démarrer screen ou putty pour voir les message

Détecter les erreurs de compilation

Ce fût le cas dans mon script ci-dessus... j'ai une horrible erreur de compilation qu'il m'aura fallut bien du temps à identifier.

Sans l'utilisation d'un terminal, je n'aurais probablement pas identifier cette source d'erreur.

MicroPython-HAck-Debug-01.jpg

Le message est sans équivoque, il ne sait pas appeler la fonction delay(). J'avais oublié le nom du module pyb... l'instruction correcte est pyb.delay(2000).

La version corrigée

import pyb

def wait_pin_change(pin):
    # attendre que la broche change de valeur
    cur_value = pin.value()
    # Elle doit être stable pendant un temps continu de 20ms
    active = 0
    while active < 20:
        if pin.value() != cur_value:
            active += 1
        else:
            active = 0
        pyb.delay(1)

pin_x4 = pyb.Pin('X4', pyb.Pin.IN, pyb.Pin.PULL_DOWN)
while True:
    print( 'attendre pression bouton' )
    wait_pin_change(pin_x4)
    print( 'bouton enfonce' )
    pyb.LED(4).toggle()
    print( 'LED modifiee' )
    pyb.LED(2).on()
    print( 'LED 2 allumee')
    pyb.delay(2000)
    pyb.LED(2).off()
    print( 'LED 2 eteinte')

Attendre pour déboguer

Nous avons vu que l'utilisation d'un terminal série et des instructions print() sont bien utiles pour déboguer un programme.

Par contre, le programme dans main.py est exécuté immédiatement... peut être trop vite pour avoir le temps de lancer un programme terminal et voir les premiers print().

La petite astuce, c'est d'attendre que l'utilisateur presse le bouton USR (Utilisateur) de la carte PyBoard pour lancer le programme.

Voici donc une première version de debug_wait_start(), cette fonction:

  • Est appelée en tout début de programme.
  • debug_wait_start():
    • Allume la LED(4), bleue, pour signaler que la fonction attend la pression du bouton USR
      C'est maintenant qu'il faut ouvrir votre session terminal ;-)
    • Attend la pression du bouton USR
    • Eteind la LED(4) pour signaler que la fonction debug_wait_start() s'achève.
    • Fait un premier print() avec le message "PROGRAMME STARTED" (ou similaire).
# main.py -- put your code here!

import pyb

def wait_pin_change(pin):
    # attendre que la broche change de valeur
    cur_value = pin.value()
    # Elle doit être stable pendant un temps continu de 20ms
    active = 0
    while active < 20:
        if pin.value() != cur_value:
            active += 1
        else:
            active = 0
        pyb.delay(1)

def wait_user_button():
    pin_user = pyb.Pin('X17', pyb.Pin.IN, pyb.Pin.PULL_UP)
    wait_pin_change( pin_user )


def debug_wait_start():
    print( 'PRESS USR TO START' )
    # allumer la LED bleue
    pyb.LED(4).on()
    # attendre la pression du bouton user
    wait_user_button()
    # eteindre LED bleue
    pyb.LED(4).off()
    # Informer l'utilisateur
    print( 'PROGRAMME STARTED' )


debug_wait_start()
pin_x4 = pyb.Pin('X4', pyb.Pin.IN, pyb.Pin.PULL_DOWN)
while True:
    print( 'attendre pression bouton' )
    wait_pin_change(pin_x4)
    print( 'bouton enfonce' )
    pyb.LED(4).toggle()
    print( 'LED modifiee' )
    pyb.LED(2).on()
    print( 'LED 2 allumee')
    pyb.delay(2000)
    pyb.LED(2).off()
    print( 'LED 2 eteinte')

Controle+C et Controle+D

Nous l'avons vu plus haut, l'utilisation d'un terminal est bien pratique pour détecter les erreurs mais il se sera aussi pour modifier et redémarrer votre programme sans devoir éjecter et réinitialiser la carte.

nous allons créer un bouton à deux états qui contrôle la LED(2) (presser une fois pour allumer, une autre fois pour éteindre).

MicroPython-Hack-deboucing.jpg
Made with - réalisé avec - Fritzing fritzing.org

Le programme utilise la méthode décrite précédemment pour attendre la pression du bouton utilisateur avant de démarrer.

Connectez votre PyBoard sur votre ordinateur... vous pourrez ainsi modifier le fichier main.py de la carte.

Nous vous proposons de copier le code suivant, sauvez le fichier... puis de lire la suite de l'article.

# main.py -- put your code here!

import pyb


def wait_pin_change(pin):
    # attendre que la broche change de valeur
    cur_value = pin.value()
    # Elle doit être stable pendant un temps continu de 20ms
    active = 0
    while active < 20:
        if pin.value() != cur_value:
            active += 1
        else:
            active = 0
        pyb.delay(1)

def wait_user_button():
    pin_user = pyb.Pin('X17', pyb.Pin.IN, pyb.Pin.PULL_UP)
    wait_pin_change( pin_user )


def debug_wait_start():
    print( 'PRESS USR TO START' )
    # allumer la LED bleue
    pyb.LED(4).on()
    # attendre la pression du bouton user
    wait_user_button()
    # eteindre LED bleue
    pyb.LED(4).off()
    # Informer l'utilisateur
    print( 'PROGRAMME STARTED :-)' )


debug_wait_start()
pin_x1 = pyb.Pin('X1', pyb.Pin.IN, pyb.Pin.PULL_DOWN)
current_state = 0 # dernier état connu de la broche X1
bascule = 0 # Variable qui change d etat a chaque fois que l'on pousse le bouton 

while True:
    # lecture des entrees
    val1 = pin_x1.value()

    # déparasitage logiciel
    pyb.delay(10) # Attendre 10 millisecondes

    # relecture des entrées
    val2 = pin_x1.value()

    # Si val1=Val2 --> Etat Stable
    if val1 == val2:
        # Si valeur lue différente de l'état connu
        if val1 != current_state:
          # On memorise l'etat du bouton comme etat connu
          current_state = val1
          # Bien entendu, le programme ne doit agir que lorsque le bouton
          # est enfoncé (par lorsqu'il est relaché)
          if val1 == 1:
              print( 'bascule before %i' % bascule )
              # On inverse l'état de la "bascule" 
              bascule = 0 if bascule == 1 else 1
              print( 'bascule after %i' % bascule )
              # Apliquer l'etat de la bascule sur la LED(2)
              if bascule == 1:
                  pyb.LED(2).on()
              else:
                  pyb.LED(2).off()
 
    # exécuter le reste du programme

Sauvez le fichier main.py sur la PyBoard. Gardez votre éditeur de texte ouvert... nous allons faire quelque-chose de magique.

Maintenant, rendez-vous dans le terminal (celui que vous avez ouvert pour vous connecter sur la PyBoard).

A l'invite de commande REPL, pressez la combinaison de touche CTRL+D. Cela à pour effet de faire un SOFT REBOOT de votre PyBoard.


Ce qu'il y a de génial avec le Soft Reboot:

  1. Le lecteur Flash de la PyBoard reste connecté.
  2. Le programme main.py est compilé et démarré.

Micro-python-hack-debug-soft-reboot.jpg

Comme vous pouvez le voir, le programme main.py a bien été démarré au Soft Reboot et nous avons même fait quelques opérations.

Maintenant, pressez CTRL+C dans le terminal pour arrêter le programme.

Micro-python-hack-debug-halt-program.jpg

Je vous propose maintenant de modifier le programme main.py (normalement toujours ouvert dans votre éditeur de texte favori).

Modifiez la ligne suivante:

print( 'PROGRAMME STARTED :-)' )

en

print( 'Allez HOP! c est parti...' )

Sauvez votre votre modification,

Rendez-vous dans votre terminal et pressez la combinaison de touche CTRL+D pour faire un Soft Reboot.

Pressez ensuite le bouton utilisateur (USR) et regardez la magie opérer...

Notre modification est prise en compte :-)

Micro-python-hack-debug-soft-reboot2.jpg

Ce n'est pas génial ça?



Créer par Meurisse D. pour MCHobby.be - Translated by Meurisse D. for MCHobby.be

Traduit avec l'autorisation de micropython.org - Translated with the authorisation of micropython.org

Toute référence, mention ou extrait de cette traduction doit être explicitement accompagné du texte suivant : «  Traduction par MCHobby (www.MCHobby.be) - Vente de kit et composants » avec un lien vers la source (donc cette page) et ce quelque soit le média utilisé.

L'utilisation commercial de la traduction (texte) et/ou réalisation, même partielle, pourrait être soumis à redevance. Dans tous les cas de figures, vous devez également obtenir l'accord du(des) détenteur initial des droits. Celui de MC Hobby s'arrêtant au travail de traduction proprement dit.