Communication USB Série

Ce que l'on va faire ici

Ici, nous allons voir comment utiliser la communication série via USB

La première chose à faire

Par défaut, la carte micropython déclenche le prompt micropython lorsque l'on se connecte en USB série (cf screen /dev/ttyACM0).

Là, ce qu'on veut, c'est que la carte micropython se comporte comme un device série lorsqu'on s'y connecte. Pour cela, on va activer le mode 'CDC+MSC' au boot qui a pour effet de transformer la carte en device USB série simple (sans le lancement de l'interpréteur à la connexion).

Pour cela, ouvrir le fichier boot.py sur la carte et le modifier selon :

# boot.py -- run on boot-up
# can run arbitrary Python, but best to keep it minimal

import pyb
pyb.main('trans.py') # main script to run after this one
pyb.usb_mode('CDC+MSC') # act as a serial and a storage device
#pyb.usb_mode('CDC+HID') # act as a serial device and a mouse

Il faut donc décommenter la ligne d'activation du mode CDC+MSC. Enregistrer boot.py, démonter la carte puis faire un reset.

IMPORTANT : Lorsque l'on utilise le port série USB dans des codes en développement, il est préférable que les codes se trouvent sur le PC local plutôt que dans la Flash de la carte (voir installation). Sinon, des problèmes de collision du fait de l'accès aux fichiers et au port série simultanément entraînent perte de fichiers et autre dans la FLASH. Ainsi, la FLASH de la carte peut rester démonté tant qu'on code. Ceci s'entend uniquement en phase de développement, les codes opérationnels étant à mettre sur la carte évidemment.

Envoyer une chaine vers le port série USB :

Pour cela, on va utiliser non pas la classe Uart qui concerne le Uart matérielle mais on va utiliser la classe USB_VCP qui émule un port série via l'USB nativement.

Voir la doc : http://docs.micropython.org/en/latest/pyboard/library/pyb.USB_VCP.html

Info trouvée ici : https://forum.micropython.org/viewtopic.php?t=584

Ainsi, notre code devient :

import pyb

serial=pyb.USB_VCP() # le port série par USB

while(True):
    serial.write("Hello\n") # écrit sur le port série
    #print("Hello") # équivalent
    pyb.delay(1000)

A présent, il suffit d'ouvrir un terminal série en ligne de commande ou en interface graphique et de se connecter sur le port /dev/ttyACM0 (ou autre selon) pour voir s'afficher le message :

Ce qui est "cool" ici, c'est que le script fonctionne via l'exécution depuis Geany et le terminal affiche la sortie d'une part, comme le ferait print(). Et d'autre part, cela fonctionne avec un terminal classique.

En réception

En réception, on fera :

import pyb

serial=pyb.USB_VCP() # le port série par USB

serial.write("Hello\n") # écrit sur le port série
#print("Hello") # équivalent
pyb.delay(1000)

while(True):

    if serial.any():
        print (serial.any())
        data=serial.read()
        print (data)

Pour lire les lignes une à une, autrement dit les lignes se terminant par un saut de ligne étant séparées, on fera :

import pyb

serial=pyb.USB_VCP() # le port série par USB

serial.write("Hello\n") # écrit sur le port série
#print("Hello") # équivalent
pyb.delay(1000)

while(True):

    if serial.any():
        #print (serial.any())
        #data=serial.read()
        #print (data)

        line=serial.readline()
        print (line)

Pour stocker dans un buffer n lignes, on fera :

import pyb

serial=pyb.USB_VCP() # le port série par USB

serial.write("Hello\n") # écrit sur le port série
#print("Hello") # équivalent
pyb.delay(1000)

lines=[] # list vide = buffer des lignes
nbLines=100 # nb de lines à stocker dans le buffer... 

while(True):

    while (serial.any() and len(lines)<=nbLines) : # si quelque chose en réception et si buffer pas rempli
        #print (serial.any())
        #data=serial.read()
        #print (data)

        line=serial.readline() # lecture d'une ligne
        print ("<ok>") # message reception ligne 

        lines.append(line) # ajoute la line au list

    if len(lines)==nbLines : # si on a rempli le buffer
        print (lines) # affiche le list
        lines=[] # RAS lines 

A des fins de test, j'ai testé çà avec le Simple GCode GUI, une de mes interfaces de contrôle de CNC pour envoyer du G-Code et voilà le résultat ((les "envoi ligne :" c'est l'interface, les "ok" c'est les retours série de micropython ainsi que l'affichage du list :

envoi ligne : G01 X18.14 Y31.19
<ok>
envoi ligne : G01 X18.41 Y31.15
<ok>
envoi ligne : G01 X18.66 Y31.09
<ok>
envoi ligne : G01 X18.89 Y31.03
<ok>
envoi ligne : G01 X19.11 Y30.97
<ok>
envoi ligne : G01 X19.31 Y30.89
<ok>

(...) 

envoi ligne : G01 X21.09 Y28.99
<ok>
envoi ligne : G01 X21.14 Y28.79
<ok>
envoi ligne : G01 X21.18 Y28.58
<ok>
envoi ligne : G01 X21.21 Y28.36
<ok>
envoi ligne : G01 X21.24 Y28.13
<ok>
envoi ligne : G01 X21.25 Y27.87
<ok>
envoi ligne : G01 X21.26 Y27.61
<ok>
envoi ligne : G01 X21.25 Y27.35
<ok>
envoi ligne : G01 X21.24 Y27.11
<ok>
envoi ligne : G01 X21.21 Y26.88

[b'G01 X17.87 Y31.23\n',
 b'G01 X18.14 Y31.19\n',
 b'G01 X18.41 Y31.15\n',
 b'G01 X18.66 Y31.09\n',
 b'G01 X18.89 Y31.03\n',
 b'G01 X19.11 Y30.97\n',
 b'G01 X19.31 Y30.89\n',
 b'G01 X19.50 Y30.82\n',
 b'G01 X19.67 Y30.73\n',
 b'G01 X19.83 Y30.65\n',
 b'G01 X19.98 Y30.56\n',
 b'G01 X20.11 Y30.46\n',
 b'G01 X20.24 Y30.36\n',
 b'G01 X20.36 Y30.25\n',
 b'G01 X20.47 Y30.14\n',
 b'G01 X20.57 Y30.03\n',
 b'G01 X20.67 Y29.91\n',
 b'G01 X20.75 Y29.78\n',
 b'G01 X20.83 Y29.64\n',
 b'G01 X20.91 Y29.50\n',
 b'G01 X20.97 Y29.34\n',
 b'G01 X21.04 Y29.17\n',
 b'G01 X21.09 Y28.99\n',
 b'G01 X21.14 Y28.79\n',
 b'G01 X21.18 Y28.58\n',
 b'G01 X21.21 Y28.36\n',
 b'G01 X21.24 Y28.13\n',
 b'G01 X21.25 Y27.87\n',
 b'G01 X21.26 Y27.61\n',
 b'G01 X21.25 Y27.35\n',
 b'G01 X21.24 Y27.11\n',
 b'G01 X21.21 Y26.88\n',
 b'G01 X21.18 Y26.66\n',
 b'G01 X21.14 Y26.46\n',
 b'G01 X21.09 Y26.27\n',
 b'G01 X21.04 Y26.09\n',
 b'G01 X20.98 Y25.93\n',
 b'G01 X20.91 Y25.78\n',
 b'G01 X20.84 Y25.63\n',
 b'G01 X20.77 Y25.50\n',
 b'G01 X20.68 Y25.38\n',
 b'G01 X20.59 Y25.26\n',
 b'G01 X20.49 Y25.15\n',
 b'G01 X20.39 Y25.05\n',
 b'G01 X20.27 Y24.94\n',
 b'G01 X20.14 Y24.84\n',
 b'G01 X20.00 Y24.74\n',
 b'G01 X19.86 Y24.65\n',
 b'G01 X19.70 Y24.57\n',
 b'G01 X19.52 Y24.48\n',
 b'G01 X19.33 Y24.41\n',
 b'G01 X19.13 Y24.33\n',
 b'G01 X18.91 Y24.27\n',
 b'G01 X18.68 Y24.21\n',
 b'G01 X18.43 Y24.15\n',
 b'G01 X18.16 Y24.11\n',
 b'G01 X17.88 Y24.07\n',
 b'G01 X17.58 Y24.04\n',
 b'G01 X17.27 Y24.01\n',
 b'G01 X16.94 Y24.00\n',
 b'G01 X16.59 Y24.00\n',
 b'G01 X12.13 Y24.00\n',
 b'G01 X12.13 Y24.00\n',
 b'G01 Z-8.5 F50.0\n',
 b'G01 X12.13 Y31.30 F500.0\n',
 b'G01 X16.59 Y31.30\n',
 b'G01 X16.94 Y31.30\n',
 b'G01 X17.26 Y31.28\n',
 b'G01 X17.57 Y31.26\n',
 b'G01 X17.87 Y31.23\n',
 b'G01 X18.14 Y31.19\n',
 b'G01 X18.41 Y31.15\n',
 b'G01 X18.66 Y31.09\n',
 b'G01 X18.89 Y31.03\n',
 b'G01 X19.11 Y30.97\n',
 b'G01 X19.31 Y30.89\n',
 b'G01 X19.50 Y30.82\n',
 b'G01 X19.67 Y30.73\n',
 b'G01 X19.83 Y30.65\n',
 b'G01 X19.98 Y30.56\n',
 b'G01 X20.11 Y30.46\n',
 b'G01 X20.24 Y30.36\n',
 b'G01 X20.36 Y30.25\n',
 b'G01 X20.47 Y30.14\n',
 b'G01 X20.57 Y30.03\n',
 b'G01 X20.67 Y29.91\n',
 b'G01 X20.75 Y29.78\n',
 b'G01 X20.83 Y29.64\n',
 b'G01 X20.91 Y29.50\n',
 b'G01 X20.97 Y29.34\n',
 b'G01 X21.04 Y29.17\n',
 b'G01 X21.09 Y28.99\n',
 b'G01 X21.14 Y28.79\n',
 b'G01 X21.18 Y28.58\n',
 b'G01 X21.21 Y28.36\n',
 b'G01 X21.24 Y28.13\n',
 b'G01 X21.25 Y27.87\n',
 b'G01 X21.26 Y27.61\n',
 b'G01 X21.25 Y27.35\n',
 b'G01 X21.24 Y27.11\n']

Si je vous dis que çà n'a duré que 2 secondes à peine et qu'un fichier de 1000 lignes de G-Code a été "avalé" sans sourcillé par la pyboard par blocs de 100 lignes successifs !!

Je pense que vous pigez l'intérêt de la pyboard et de micropython ici : à titre indicatif, en Arduino, quasi impossible de stocker au-delà de quelques lignes successives dans un buffer vu qu'on a que 4Ko de RAM... ici on a plus de 100Ko de RAM, donc pas de problème ! Et c'est sans parler de la puissance de MicroPython à gérer facilement les lignes en réception sur le port série, etc...