2

I am trying to access one serial port (/dev/ttyACM0) on my Rasbperry with two Python scripts, which obviously doesn't work because one blocks the other. The thing is that one script only needs to read from the serial port, while the other only writes to it. I can't combine the two scripts because the write-script only gets executed every so often to send some commands, and the read-script needs to log occasional messages from the other device (thus needs to be listening all the time).

Serial Communication

I tried to write a third Python program, which acted as a relay between the scripts and the single serial port. It would accomendate the serial port and write the incoming data from the device to a virtual port (/dev/ttyACM0_read), from which the read-script would read. The write-script would write to a second virtual port (/dev/ttyACM0_write), from where the relay programm reads the data and writes it to the serial port. This failed when I was unable to create the virtual ports ("could not open port, no such file or directory"). Searches on creating virtual serial ports brought up nothing useful.

While searching I came across socat, but I couldn't make out if it was of any use in this case, as it (as far as I understood) can only create a connection between two ports.

So, maybe someone can tell me how to create a virtual port so I can use it with my relay python program, or one comes up with a different solution on the one-port-two-scripts-dilemma I am in.

Platform info: Linux armv6l stretch v9


Update: To clarify my usecase, here are the python scripts I use:

write-script - when calling an URL (hostname.local/foo)on the Raspberry, the serial port is opened briefly and a command is send.

import web
import serial

urls = (
    '/foo', 'foo',
)

class foo:
    def GET(self):
        ser = serial.Serial('/dev/ttyACM0', 9600, timeout=1)
        ser.write("<311>")
        ser.close()
        return "Success"

if __name__ == "__main__":
    app = web.application(urls, globals())
    app.run()

read-script - Catches incoming data from the serial port and writes it to a file.

import serial

ser = serial.Serial('/dev/ttyACM0', 9600, timeout=1)

while 1
    #If serial data is available, write it to txt file

ser.close()

There is no specific reason for using an virtual port, I just thought that this would be the simpliest solution.

As @meuh pointed out in the comments, unsetting exclusive mode on the port would work, but I was unable to achive that using setserial:

pi@pizero:~ $ setserial /dev/ttyACM0 ^session_lockout
Cannot set serial info: Operation not supported
pi@pizero:~ $ sudo setserial /dev/ttyACM0 ^session_lockout
pi@pizero:~ $ 

After this, nothing changed and the second python script returned EBUSY (Device or resource busy) as before.

  • 1
    Is there any reason you need a "virtual port", or would a named pipe or a pseudo-tty also do? Do read-script or write-script do anything special except reading and writing, liking using an ioctl, setting baud rate etc.? Or even a normal pipe, along the lines of write-script | socat - /dev/ttyACM0 | read-script? – dirkt May 2 '18 at 4:51
  • 1
    You should be able to open a port twice if you unset exclusive mode, ioctl TIOCEXCL. See also setserial. Alternatively, open the port once read/write, then fork your 2 python programs from this process using the already opened fd. (In bash you can open a port r/w with <>/dev/ttyACM0). – meuh May 2 '18 at 18:47
  • @meuh I just read trough the manual of setserial and the option session_lockout sounds like the solution to my problem. I will test this tomorrow and keep you updated. – Philipp May 2 '18 at 21:43
  • @dirkt Thank you for comment, I added the scripts to my question. Sadly I don't know quite understand how socat helps in my situtation, would you mind clarifying what you meant with that command? – Philipp May 4 '18 at 12:53
  • What I meant is: Change the scripts to just write to stdout resp. read from stdin (and don't close them), then use socat - file:/dev/ttyACM0,b9600 as a command that sets the baud rate and bidirectionally forwards stdin/stdout to and from the serial tty. If you just want to catch the output, call like write-script | socat - file:/dev/ttyACM0,b9600 > output_file (untested), you don't even need a read-script for that. If your socat doesn't support b9600, use stty. – dirkt May 4 '18 at 13:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.