logithack
logithack

Reputation: 17

Mixing multiprocessing and serial ports

I've written a class which inherits multiprocess.Process(). It holds a serial.Serial() object in a class attribute. The method self.loop() is supposed to read from and write to the serial port. When self.loop() is called, it is supposed to run as a separate process, which is a requirement of the person who asked me to write this. However, my code produces a strange error.

This is my code:

from multiprocessing import Process

import serial
import time


class MySerialManager(Process):
    def __init__(self, serial_port, baudrate=115200, timeout=1):
        super(MySerialManager, self).__init__(target=self.loop)
        # As soon as you uncomment this, you'll get an error.
        # self.ser = serial.Serial(serial_port, baudrate=baudrate, timeout=timeout)

    def loop(self):
        # Just some simple action for simplicity.
        for i in range(3):
            print("hi")
            time.sleep(1)


if __name__ == "__main__":
    msm = MySerialManager("COM14")
    try:
        msm.start()
    except KeyboardInterrupt:
        print("caught in main")
    finally:
        msm.join()

This is the error:

Traceback (most recent call last):
  File "test.py", line 22, in <module>
    msm.start()
  File "C:\Python\Python36\lib\multiprocessing\process.py", line 105, in start
    self._popen = self._Popen(self)
  File "C:\Python\Python36\lib\multiprocessing\context.py", line 223, in _Popen
    return _default_context.get_context().Process._Popen(process_obj)
  File "C:\Python\Python36\lib\multiprocessing\context.py", line 322, in _Popen
    return Popen(process_obj)
  File "C:\Python\Python36\lib\multiprocessing\popen_spawn_win32.py", line 65, in __init__
    reduction.dump(process_obj, to_child)
  File "C:\Python\Python36\lib\multiprocessing\reduction.py", line 60, in dump
    ForkingPickler(file, protocol).dump(obj)
ValueError: ctypes objects containing pointers cannot be pickled

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "test.py", line 26, in <module>
    msm.join()
  File "C:\Python\Python36\lib\multiprocessing\process.py", line 120, in join
    assert self._popen is not None, 'can only join a started process'
AssertionError: can only join a started process
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "C:\Python\Python36\lib\multiprocessing\spawn.py", line 105, in spawn_main
    exitcode = _main(fd)
  File "C:\Python\Python36\lib\multiprocessing\spawn.py", line 115, in _main
    self = reduction.pickle.load(from_parent)
EOFError: Ran out of input

I've also tried creating a serial port object outside of the class and passing it on to the constructor. Furthermore, I've tried not inheriting multiprocess.Process() but instead putting:

self.proc = Process(target=self.loop)

into the class and

try:
    msm.proc.start()
except KeyboardInterrupt:
    print("caught in main")
finally:
    msm.proc.join()

into the main block. Neither of them solved the problem.

Somebody pointed out that it seems like mixing multiprocessing and serial ports just doesn't work out. Is that true? If it is, could you please explain to me why this isn't working? Any help is greatly appreciated!

Upvotes: 1

Views: 3210

Answers (1)

saurabh jadhav
saurabh jadhav

Reputation: 61

In windows the serial object once created cannot be shared between two processes (ie. parent and child) so make the serial object in the child process and pass the reference of that as argument to other functions

try this:

from multiprocessing import Process

import serial
import time


class MySerialManager(Process):
    def __init__(self, serial_port, baudrate=115200, timeout=1):
        super(MySerialManager, self).__init__(target=self.loop_iterator,args=(serial_port, baudrate, timeout))
        # As soon as you uncomment this, you'll get an error.
        # self.ser = serial.Serial(serial_port, baudrate=baudrate, timeout=timeout)

    def loop_iterator(self,serial_port, baudrate,timeout):
        ser = serial.Serial(serial_port, baudrate=baudrate, timeout=timeout)
        self.loop(ser)

    def loop(self,ser):
        # Just some simple action for simplicity.
        # you can use ser here
        for i in range(3):
            print("hi")
            time.sleep(1)


if __name__ == "__main__":
    msm = MySerialManager("COM4")
    try:
        msm.start()
    except KeyboardInterrupt:
        print("caught in main")
    finally:
        msm.join()

Upvotes: 4

Related Questions