J'utilise un script dans Python pour collecter des données à partir d'un microcontrôleur PIC via un port série à 2 Mbit/s.
Le PIC fonctionne avec une synchronisation parfaite à 2 Mbps, ainsi que le port série usb FTDI à 2 Mbps (tous deux vérifiés avec un oscilloscope)
J'envoie des messages (taille d'environ 15 caractères) environ 100 à 150 fois par seconde et leur nombre augmente (pour vérifier si des messages sont perdus, etc.)
Sur mon ordinateur portable, Xubuntu est exécuté en tant que machine virtuelle. Je peux lire le port série via PuTTY et via mon script (python 2.7 et pySerial).
Le problème:
Voici le code (j'ai omis la plus grande partie du code, mais la boucle est la même):
ser = serial.Serial('/dev/ttyUSB0', 2000000, timeout=2, xonxoff=False, rtscts=False, dsrdtr=False) #Tried with and without the last 3 parameters, and also at 1Mbps, same happens.
ser.flushInput()
ser.flushOutput()
While True:
data_raw = ser.readline()
print(data_raw)
Tout le monde sait pourquoi pySerial prend autant de temps à lire du port série jusqu'à la fin de la ligne? De l'aide?
Je veux avoir cela en temps réel.
Merci
Vous pouvez utiliser inWaiting()
pour obtenir le nombre d'octets disponibles dans la file d'attente d'entrée.
Ensuite, vous pouvez utiliser read()
pour lire les octets, quelque chose comme ça:
While True:
bytesToRead = ser.inWaiting()
ser.read(bytesToRead)
Pourquoi ne pas utiliser readline()
dans ce cas de Docs:
Read a line which is terminated with end-of-line (eol) character (\n by default) or until timeout.
Vous attendez le délai d’attente à chaque lecture car il attend eol. l’entrée série Q reste la même qu’il reste beaucoup de temps pour arriver à la "fin" de la mémoire tampon, pour mieux la comprendre: vous écrivez sur l’entrée Q comme une voiture de course et lisez comme une vieille voiture :)
Vous devez définir le délai d'attente sur "Aucun" lorsque vous ouvrez le port série:
ser = serial.Serial(**bco_port**, timeout=None, baudrate=115000, xonxoff=False, rtscts=False, dsrdtr=False)
Ceci est une commande bloquante, vous attendez donc de recevoir à la fin des données contenant une nouvelle ligne (\ n ou\r\n): line = ser.readline ()
Une fois que vous avez les données, il retournera dès que possible.
De le manuel :
Valeurs possibles pour le paramètre timeout:… x définissez timeout sur x secondes
et
readlines (sizehint = None, eol = '\ n') Lit une liste de lignes jusqu'à l'expiration du délai. sizehint est ignoré et n'est présent que pour la compatibilité de l'API avec les objets File intégrés.
Notez que cette fonction ne renvoie que le délai d'expiration.
Ainsi, votre readlines
reviendra au maximum toutes les 2 secondes. Utilisez read()
comme suggéré par Tim.
Une très bonne solution à cela peut être trouvée ici :
Voici une classe qui sert de wrapper à un objet pyserial. Il vous permet de lire des lignes sans 100% de CPU. Il ne contient aucune logique de délai d'attente. En cas d'expiration du délai,
self.s.read(i)
renvoie une chaîne vide et vous pouvez générer une exception pour indiquer le délai.
Il est également supposé être rapide selon l'auteur:
Le code ci-dessous m'apporte 790 Ko/s lors du remplacement du code par la méthode readline de pyserial, ce qui ne m'apporte que 170 Ko/s.
class ReadLine:
def __init__(self, s):
self.buf = bytearray()
self.s = s
def readline(self):
i = self.buf.find(b"\n")
if i >= 0:
r = self.buf[:i+1]
self.buf = self.buf[i+1:]
return r
while True:
i = max(1, min(2048, self.s.in_waiting))
data = self.s.read(i)
i = data.find(b"\n")
if i >= 0:
r = self.buf + data[:i+1]
self.buf[0:] = data[i+1:]
return r
else:
self.buf.extend(data)
ser = serial.Serial('COM7', 9600)
rl = ReadLine(ser)
while True:
print(rl.readline())