4
votes

Identifier la cause des artefacts \ r lors de la lecture depuis un terminal série?

J'essaie de diagnostiquer la cause des artefacts \ r lors de la lecture à partir d'un terminal série.

Le code suivant peut provoquer le problème. Il y a un périphérique Linux embarqué connecté à l'extrémité physique du câble uart

stty_cmd_set = b'stty cols 200'
ser.write(stty_cmd_set + b_NEW_LINE_WRITTEN)
ser.reset_input_buffer()
ser.reset_output_buffer()
stty_cmd_confirm = b'stty -a'
ser.write(stty_cmd_confirm + b_NEW_LINE_WRITTEN)

# After reading a few lines there is a confirmation that the tty device on the target has indeed been set to 200 
print(ser.readline())                           
b'speed 115200 baud; rows 56; columns 200; line = 0;\r\n'
ser.reset_input_buffer()
ser.reset_output_buffer()

ser.write(b_alphabet + b_NEW_LINE_WRITTEN)

raw_line = ser.readline()
print(raw_line)                                                                                  
# b'A a B b C c D d E e F f G g H h I i J j K k L l M m N n O o P p Q q\rq R r S s T t U u V v W w X x Y y Z z\r\n'

L'impression des données lues a un \ r autour de la lettre q .

Lors de l'exécution d'un émulateur de terminal sur / dev / ttyUSB0 , la ligne commence à s'enrouler après 80 caractères, de la même manière que cette question . Ce retour à la ligne semble être une manifestation du / r que j'obtiens en lisant directement avec l'exemple de code python.

Lorsqu'il est connecté à un émulateur de terminal ( picocom ) ce problème est résolu en exécutant:

# Separate terminal on the host
stty -F /dev/ttyUSB0  cols 100
stty -F /dev/ttyUSB0 raw
stty -F /dev/ttyUSB0  -a
speed 115200 baud; rows 80; columns 100; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^A; eol = <undef>; eol2 = <undef>; swtch = <undef>;
start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; discard = ^O;
min = 1; time = 0;
-parenb -parodd -cmspar cs8 hupcl -cstopb cread clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff -iuclc -ixany
-imaxbel -iutf8
-opost -olcuc -ocrnl -onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
-isig -icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt -echoctl -echoke
-flusho -extproc


#ipython
...
ser.write(b_alphabet + b_NEW_LINE_WRITTEN)
raw_line = ser.readline()
print(raw_line)                                                                                  
# b'A a B b C c D d E e F f G g H h I i J j K k L l M m N n O o P p Q q\rq R r S s T t U u V v W w X x Y y Z z\r\n'

il n'y a pas de retour à la ligne après cela. J'essaie de comprendre pourquoi cela se produit ou où (est l'hôte où / dev / tttyUSB0 ce qui les ajoute ou sont-ils ajoutés par le périphérique intégré auquel on accède), et comment contourner le problème sans avoir pour exécuter des commandes externes.

Les candidats suivants pourraient être à l'origine du comportement:

  • paramètres du pilote pour / dev / ttyUSB0 sur le ps du coup
  • paramètres du pilote pour / dev / S0 sur le périphérique intégré cible
  • shell sur l'appareil cible

Paramètre /dev/ttyUSB0 final

Essayer de modifier / dev / ttyUSB0 dans un terminal séparé pendant que l'appareil est consommé par le script python n'affiche aucun changement.

shopt -s checkwinsize
resize

Paramètre /dev/S0

La définition du périphérique tty cible n'influence pas non plus l'existence des artefacts.

import serial                      

ser = serial.Serial( 
    port='/dev/ttyUSB0', 
    baudrate=115200,  
    timeout=5) 

ser.reset_input_buffer()
ser.reset_output_buffer()

b_NEW_LINE_WRITTEN = b'\n'
b_alphabet = b'A a B b C c D d E e F f G g H h I i J j K k L l M m N n O o P p Q q R r S s T t U u V v W w X x Y y Z z'
ser.write(b_alphabet + b_NEW_LINE_WRITTEN)

raw_line = ser.readline()
print(raw_line)                                                                                  
# b'A a B b C c D d E e F f G g H h I i J j K k L l M m N n O o P p Q q\rq R r S s T t U u V v W w X x Y y Z z\r\n'

Par exemple, une solution de contournement serait de définir en quelque sorte une quantité fixe de 200 colonnes avant de lire afin que le terminal série cesse d'essayer d'être intelligent.


9 commentaires

Sous Linux, votre application n'a pas accès au "port série" . Vous lisez plutôt à partir d'un terminal série, qui est désigné par le nom du périphérique / dev / tty ... . Votre application est plusieurs couches supprimées du "port série" . Voir Pilotes série Linux . Avec Python et Pyserial, vous avez ajouté plus de couches d'abstraction et de perte de contrôle.


Essayez une expérience avec un programme d'émulation de terminal, tel que minicom . Utilisez la commande Envoyer ASCII d'un fichier contenant la même chaîne de texte au périphérique distant et voyez ce qui revient.


Dans la communication série, il existe une convention très, très ancienne d'utilisation de la paire de caractères \ r \ n - un CR ASCII (Carriage Return) suivi d'un ASCII LF (Line Feed) - pour indiquer la fin d'une ligne de texte. Si vous n'aimez pas le \ r que l'appareil envoie, alors demandez simplement à votre programme de le supprimer. (Vous avez de la chance que l'appareil ne vous oblige pas à l'envoyer \ r \ n comme terminateur de ligne. Apparemment, il était prêt à accepter uniquement un \ n comme marqueur de fin de ligne dans les données entrantes.)


Quels sont vos paramètres de ligne série? L'un des paramètres série pourrait concerter un octet. Vérifier avec stty


@ rm5248 Lesquels, pour / dev / ttyUSB0 ou / dev / S0 ?


@TheMeaningfulEngineer Selon le port série, il peut être de chaque côté. Un côté peut avoir -ocrnl défini (qui mappe \ r à \ n en sortie), ou -icrnl qui fait la même chose sauf en entrée. Cette réponse peut vous aider: stackoverflow.com/questions/48679189/...


En lisant entre les lignes, vous exécutez quelque chose comme un shell Linux sur votre appareil embarqué. Celui-ci aura le même ensemble de hiérarchie d'application / TTY / pilote Linux que votre hôte et utilise par défaut le traitement en mode cuisiné de l'entrée qu'il reçoit de votre application. Le passage à l'entrée brute à la fois sur votre hôte et sur le shell intégré devrait résoudre ce problème.


@PeterBrittain Votre commentaire semble être le plus proche d'une explication de ce qui se passe. Si vous le présentez comme une réponse, je lui attribuerai la prime en l'absence de meilleures réponses


@TheMeaningfulEngineer terminé. Faites-moi savoir si vous souhaitez plus d'informations.


3 Réponses :


0
votes

Ce n'est pas clair ce qui se passe réellement mais c'est une solution qui semble fonctionner.

Cela dépend de l'existence de shopt -s checkwinsize et de resize sur le carte cible donc ce n'est pas une solution assez générique pour être la réponse acceptée.

De plus, cela ne donne pas un aperçu sur la façon d'appliquer un correctif non d'exécution (en définissant les paramètres par défaut du pilote ou une configuration dans bash).

import serial             
import time         

ser = serial.Serial( 
    port='/dev/ttyUSB0', 
    baudrate=115200,  
    timeout=5) 

b_NEW_LINE_WRITTEN = b'\n'
b_NEW_LINE_READ = b'\r\n'
b_alphabet = b'A a B b C c D d E e F f G g H h I i J j K k L l M m N n O o P p Q q R r S s T t U u V v W w X x Y y Z z'

ser.write(b_alphabet + b_NEW_LINE_WRITTEN) 
print(ser.readline())
# b'A a B b C c D d E e F f G g H h I i J j K k L l M m N n O o P p Q q\rq R r S s T t U u V v W w X x Y y Z z\r\n'

shopt_cmd = b'shopt -s checkwinsize' 
ser.write(shopt_cmd + b_NEW_LINE_WRITTEN)
ser.readline()
ser.readline()

resize_cmd = b'resize' 
ser.write(resize_cmd + b_NEW_LINE_WRITTEN)
ser.readline()
ser.readline()

stty_cmd_set = b'stty cols 200'
ser.write(stty_cmd_set + b_NEW_LINE_WRITTEN)
ser.readline()
ser.readline()

ser.write(b_alphabet + b_NEW_LINE_WRITTEN) 
print(ser.readline())
# b'A a B b C c D d E e F f G g H h I i J j K k L l M m N n O o P p Q q R r S s T t U u V v W w X x Y y Z z\r\n'

ser.reset_output_buffer()
stty_cmd_set = b'stty cols 5'
ser.write(stty_cmd_set + b_NEW_LINE_WRITTEN)
ser.readline()
ser.readline()


ser.write(b_alphabet + b_NEW_LINE_WRITTEN) 
print(ser.readline())
# A a B \r b C c\rc D d \r E e F\rF f G \r g H h\rh I i \r J j K\rK k L \r l M m\rm N n \r O o P\rP p Q \r q R r\rr S s \r T t U\rU u V \r v W w\rw X x \r Y y Z\rZ z\r\n'


0 commentaires

-1
votes

\ r q se produit parce que votre écran se termine près de q donc il est déplacé à la ligne suivante son retour chariot d'Unix. le retour chariot unix peut être utilisé comme nouvelle ligne, aller à la fin de ligne suivante les deux. Remplacez \ r par vide. Essayez le bouclage automatique

\ r (Retour chariot) → déplace le curseur au début de la ligne sans passer à la ligne suivante \ n (Saut de ligne) → déplace le curseur vers la ligne suivante sans revenir au début de la ligne - Dans un environnement * nix \ n se déplace au début de la ligne. \ r \ n (Fin de ligne) → une combinaison de \ r et \ n


0 commentaires

1
votes

En lisant entre les lignes, vous exécutez quelque chose comme un shell Linux sur votre périphérique embarqué.

Il aura le même ensemble de hiérarchie d'application / TTY / pilote Linux que votre hôte et utilise par défaut le traitement en mode cuisiné de l'entrée qu'il reçoit de votre application. C'est pourquoi l'exécution de la commande pour modifier le nombre de colonnes (sur votre application intégrée) fonctionne. Il indique à la discipline de ligne de cet appareil de traiter l'écran comme ayant une largeur de 200 colonnes (et donc la logique d'édition de ligne n'a pas besoin de diviser la ligne).

Le passage à l'entrée brute à la fois sur votre hôte et sur le shell intégré devrait résoudre ce problème.

Si vous souhaitez en savoir plus sur la façon dont les terminaux Linux gèrent l'entrée (et l'écho de l'entrée vers le flux de sortie), consultez https://www.linusakesson.net/programming/tty/


1 commentaires

La définition des paramètres corrects sur les options tty cible (périphérique intégré) résout le problème. Finalement, j'ai opté pour l'absence d'écho, ce qui simplifie le problème dans son ensemble. Cela signifiait appeler stty raw -echo -echoe -echok -echoctl -echoke sur la cible (via le script python)