python read multiple serial ports
<p>I am trying to read from multiple serial ports in python. But contrary to <a href="http://stackoverflow.com/questions/27484250/python-pyserial-read-data-form-multiple-serial-ports-at-same-time">this</a> thread I want to be able to change the number of ports dynamically (reading it via command line option).</p>

<p>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:</p>

<pre><code>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)]
</code></pre>

<p>"ports" looks the following (at the moment):</p>

<pre><code>'/dev/ttyUSB0',4800
</code></pre>

<p>The error: </p>

<pre><code>Traceback (most recent call last):
  File "&lt;stdin&gt;", line 1, in &lt;module&gt;
IndexError: 0-d arrays can't be indexed
</code></pre>

<p>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).</p>

<p>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.</p>

<p>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.</p>

<pre><code>serial_ports = []
with open('ports') as f:
    for line in f:
        port, baud = line.split(',')
        serial_ports.append(serial.Serial(port, int(baud)))
</code></pre>

<p>Or you could use the <a href="https://docs.python.org/3/library/csv.html#module-csv" rel="nofollow"><code>csv</code></a> module:</p>

<pre><code>import csv

with open('ports') as f:
    serial_ports = [serial.Serial(port, int(baud)) for port, baud in csv.reader(f)]
</code></pre>

<hr>

<p>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.</p>

<p>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 <a href="https://docs.python.org/3/library/select.html#module-select" rel="nofollow"><code>select()</code></a> module, or if you're using Python >= 3.4, the <a href="https://docs.python.org/3/library/selectors.html#module-selectors" rel="nofollow"><code>selectors</code></a> module. You do not require multiple processes to do this.</p>

<p>If the application is CPU bound then you could use <code>mutiprocessing.Process()</code> or <code>subprocess.Popen()</code>. 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.</p>

<p><strong>N.B. Untested - don't know if this will work with a serial port</strong>. 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.</p>

<pre><code>from subprocess import Popen, PIPE

s = serial.Serial(port, baud)
p = Popen(['python', 'port_reader.py'], stdin=s, stdout=PIPE, stderr=PIPE)
p.communicate()
</code></pre>

<p>If using <code>multiprocessing</code> you can pass the open serial port to the child as an argument. This might work... ?</p>

<pre><code>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()
</code></pre>

python serial-port readfile