Reputation: 673
I am trying to read from multiple serial ports in python. But contrary to this thread I want to be able to change the number of ports dynamically (reading it via command line option).
My idea was to put the ports into a file "ports", read this file and put the opened serial ports into a list, according to the number of lines in "ports". My minimal example:
import numpy as np
import serial
p = np.genfromtxt('ports',delimiter=',',dtype=None)
nser = p.size
ser = [serial.Serial(port=p[i][0], baudrate=p[i][1]) for i in xrange(nser)]
"ports" looks the following (at the moment):
'/dev/ttyUSB0',4800
The error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: 0-d arrays can't be indexed
Apparently the file is not correctly read to an array, and I already tried various different methods and ways (using pythons own methods or np.loadtxt).
Does anybody have an idea how to a) read the file correctly and b) solve the multiple port issue in a useful way? Thanks in advance.
Upvotes: 0
Views: 3573
Reputation: 87074
Your config file format is very simple and can easily be parsed without numpy. You can use simple string splitting to load each port definition.
serial_ports = []
with open('ports') as f:
for line in f:
port, baud = line.split(',')
serial_ports.append(serial.Serial(port, int(baud)))
Or you could use the csv
module:
import csv
with open('ports') as f:
serial_ports = [serial.Serial(port, int(baud)) for port, baud in csv.reader(f)]
The second part of your question is more difficult because you haven't provided many details about how the serial port readers will process the data received over the ports.
If the application is I/O bound, which is most likely the case, you can asynchronously check when a serial port has some data to read, then read it as required. That can be done with the select()
module, or if you're using Python >= 3.4, the selectors
module. You do not require multiple processes to do this.
If the application is CPU bound then you could use mutiprocessing.Process()
or subprocess.Popen()
. Instead of opening the serial ports in the parent, pass the serial port parameters to the child as arguments/command line arguments to the child function/process and let the child open the port, processes the data, and close the port.
N.B. Untested - don't know if this will work with a serial port. If you must open the ports in the parent, hook the stdin of the subprocess up to the serial port. You'll need to be careful with this as it's easy to deadlock processes where the parent and child are are mutually blocked on each other.
from subprocess import Popen, PIPE
s = serial.Serial(port, baud)
p = Popen(['python', 'port_reader.py'], stdin=s, stdout=PIPE, stderr=PIPE)
p.communicate()
If using multiprocessing
you can pass the open serial port to the child as an argument. This might work... ?
from multiprocessing import Process
def child(port):
while True:
line = port.readline()
if not line:
break
print('child(): read line: {!r}'.format(line))
port = serial.Serial(port, baud)
p = Process(target=child, args=(port,))
p.start()
p.join()
Upvotes: 2
Reputation: 12972
I didn't quite clearly understood what you are trying to do, but if I had a file like:
'/dev/ttyUSB0',4800
'/dev/ttyUSB1',4801,'/dev/ttyUSB3',4803
and want to read it and store as a list, a way to go would be:
with open('ports.txt') as f:
lines = f.read().replace('\n', ',')
print lines
which will give you:
>>> lines
'/dev/ttyUSB0',4800,'/dev/ttyUSB1',4801,'/dev/ttyUSB3',4803
and if you want to split the integers, you could do:
>>> l1 = [lines.pop(i) for i,j in enumerate(lines) if type(j)==int ]
>>> l1
[4800, 4801, 4803]
>>> lines
['/dev/ttyUSB0', '/dev/ttyUSB1', '/dev/ttyUSB3']
Now because you said that 'np.loadtxt' didn't work, a way to convert a python list to a numpy-array is:
>>> lines = ['/dev/ttyUSB0',4800,'/dev/ttyUSB1',4801,'/dev/ttyUSB3',4803]
>>>
>>> import numpy as np
>>> np.asarray(lines)
array(['/dev/ttyUSB0', '4800', '/dev/ttyUSB1', '4801', '/dev/ttyUSB3',
'4803'],
dtype='|S12')
But again I am not sure If that is what you are looking for.
Upvotes: 1