8
votes

I have a PC Software (OS: Win 64bit) that communicates with a machine via physical serial port RS232 and I want to make a sniffer for that port using a python. Please note that I am beginner to serial ports.

I've read multiple documents and questions posted online but most of them asks to just use 3rd-party software, but I cannot do this way because raw bytes have to be decoded into string message (I have my way own of decode/encode method).

Currently I have setup like this:

///////////////////       Physical COM1        /////////////
// (PC) Software // <------------------------> // Machine //
///////////////////                            /////////////

And I want a python to output any bytes that went through COM1.

Desired Behavior diagram (Virtual serial port has a question mark because I'm not sure if that is the right approach):

///////////////////       Physical COM1        /////////////
// (PC) Software // <------------------------> // Machine //
///////////////////            | Virtual       /////////////
                               | serial port?
                               v
                        //////////////////
                        // (PC) Sniffer // (Python)
                        //////////////////
                               | 
                               v
                         (output bytes)

Those of who knows Advanced Serial Port Monitor, its "spymode" functionality is exactly what I am trying to achieve using python.

I've tried to use com0com and PortMon but I can't find a way to configure com0com to sniff physical port (as far as my observation goes, com0com only makes virtual ports) and PortMon does not support Windows 64-bit.

I've been stuck at this for days... any comments/links/answers are appreciated. Thank you,

3

3 Answers

6
votes

You should go through pySerial

Only one function can acquire the serial port at a time.

For one-way communication(from machine to PC software), the only way I can think of to sniff from a serial port is to read from a port1 and write to port2, where your machine is writing to port1 and PC software has been modified to read from port2.

import serial

baud_rate = 4800 #whatever baudrate you are listening to
com_port1 = '/dev/tty1' #replace with your first com port path
com_port2 = '/dev/tty2' #replace with your second com port path

listener = serial.Serial(com_port1, baudrate)
forwarder = serial.Serial(com_port2, baudrate)

while 1:
    serial_out = listener.read(size=1)
    print serial_out #or write it to a file 
    forwarder.write(serial_out)

To achieve full duplex(asynchronous two way communication), you need to have a two processes, one for each direction. You will need to synchronize these process in some way. One way to do it could be, while one process reads from port1, the other writes to port2, and vice-versa. Read this question

3
votes

Why not echo something like:

PC S/W <--> COMn(COM0COM)COMm <--> python monitor & forward <--> COM1 <--> Machine

Software wise you need 2 serial tasks one opens COMm and one opens COM1 and a central logger and anything that comes in on COMm gets logged then forwarded to COM1 and vice verca.

1
votes

We can use the code above without the need to go through threading to achieve a half duplex communication. we are going to use an infinite loop, and a variable which gonna specify in which port we are reading.

import serial
import time

baud_rate = 9600  # whatever baudrate you are listening to
com_port1 = '/dev/ttyUSB0'  # replace with your first com port path
com_port2 = '/dev/ttyUSB1'  # replace with your second com port path

ComRead_timeout = 0.1   # Read timeout to avoid waiting while there is no data on the buffer
ComWr_timeout = 0.1     # Write timeout to avoid waiting in case of write error on the serial port

log = open('log.txt', 'a+')     # Open our log file, to put read data

From_PC_To_Device = True    # this variable is used to specify which port we're gonna read from

listener = serial.Serial(port=com_port1, baudrate=baud_rate, timeout=ComRead_timeout,
                         write_timeout=ComWr_timeout)

forwarder = serial.Serial(port=com_port2, baudrate=baud_rate, timeout=ComRead_timeout,
                          write_timeout=ComWr_timeout)

while 1:
    while (listener.inWaiting()) and From_PC_To_Device:
        serial_out = listener.readline()
        localtime = time.asctime(time.localtime(time.time()))
        Msg = "PC " + localtime + " " + serial_out
        Msg += "\n"
        log.write(Msg)
        print(serial_out)  # or write it to a file
        forwarder.write(serial_out)
    else:
        From_PC_To_Device = False

    while (forwarder.inWaiting()) and not From_PC_To_Device:
        serial_out = forwarder.readline()
        localtime = time.asctime(time.localtime(time.time()))
        Msg = "DEVICE " + localtime + " " + serial_out + "\n"
        log.write(Msg)
        print(serial_out)  # or write it to a file
        listener.write(serial_out)
    else:
        From_PC_To_Device = True