How can I set Xofflimit and clear RTS in pyserial?

33 Views Asked by At

I am trying to replicate what another program is doing to interface with some hardware, where there is next to no documentation, and the business that made the device no longer exists. I know that in pyserial, I can directly set the RTS pin with Serial.rts, but when I run the existing program, it does IOCTL_SERIAL_CLR_RTS and IOCTL_SERIAL_SET_DTR. Do set and clr mean setting the pins to high and low respectively?

pyro_serial = serial.Serial(
    port="COM1",
    baudrate=115200,
    timeout=2,
    bytesize=8,
    stopbits=serial.STOPBITS_ONE,
    parity=serial.PARITY_NONE,
)

Since the port is specified, it automatically opens the port. This is what happens:

UTC TIMESTAMP           | T |  LEN | DATA
2024-03-11 14:12:39.124 | C |   24 | IOCTL_SERIAL_SET_QUEUE_SIZE: InSize:4096, OutSize:4096
2024-03-11 14:12:39.124 | C |   24 | IOCTL_SERIAL_SET_TIMEOUTS: ReadIntervalTimeout:0, ReadTotalTimeoutMultiplier:0, ReadTotalTimeoutConstant:2000, WriteTotalTimeoutMultiplier:0, WriteTotalTimeoutConstant:0
2024-03-11 14:12:39.124 | C |   24 | IOCTL_SERIAL_SET_BAUD_RATE: 115200
2024-03-11 14:12:39.124 | C |   24 | IOCTL_SERIAL_SET_RTS
2024-03-11 14:12:39.124 | C |   24 | IOCTL_SERIAL_SET_DTR
2024-03-11 14:12:39.124 | C |   24 | IOCTL_SERIAL_SET_LINE_CONTROL: StopBits:STOP_BIT_1, Parity:NO_PARITY, WordLength:8
2024-03-11 14:12:39.124 | C |   24 | IOCTL_SERIAL_SET_HANDFLOW: ControlHandShake:SERIAL_DTR_CONTROL, FlowReplace:SERIAL_AUTO_TRANSMIT|SERIAL_AUTO_RECEIVE|SERIAL_RTS_CONTROL, XonLimit:2048, XoffLimit:512

But when I run the program that already exists, this happens:

UTC TIMESTAMP           | T |  LEN | DATA
2024-03-11 01:15:04.662 | C |   24 | IOCTL_SERIAL_SET_BAUD_RATE: 115200
2024-03-11 01:15:04.662 | C |   24 | IOCTL_SERIAL_CLR_RTS
2024-03-11 01:15:04.662 | C |   24 | IOCTL_SERIAL_SET_DTR
2024-03-11 01:15:04.662 | C |   24 | IOCTL_SERIAL_SET_LINE_CONTROL: StopBits:STOP_BIT_1, Parity:NO_PARITY, WordLength:8
2024-03-11 01:15:04.662 | C |   24 | IOCTL_SERIAL_SET_HANDFLOW: ControlHandShake:SERIAL_DTR_CONTROL, FlowReplace:, XonLimit:2048, XoffLimit:2048
2024-03-11 01:15:04.662 | C |   24 | IOCTL_SERIAL_SET_QUEUE_SIZE: InSize:8192, OutSize:2048
2024-03-11 01:15:04.662 | C |   24 | IOCTL_SERIAL_SET_TIMEOUTS: ReadIntervalTimeout:1, ReadTotalTimeoutMultiplier:0, ReadTotalTimeoutConstant:1, WriteTotalTimeoutMultiplier:0, WriteTotalTimeoutConstant:10

When I write the exact same bytes after this as the existing program does, I get an echo of the same bytes on reading the port. But when the existing program does this, it reads the ACK byte. Based on this, I think that the exact timing of the port is an issue.

The information that I do have from the manual reads as follows:

"One issue to be aware of when implementing MODBUS communication is that it requires a 16-bit CRC (this can be pre-computed and just provided as a literal constant or else calculated at run-time) and a multi-character delay in order to complete the command. A MODBUS command is considered finished ONLY after this delay takes place. Therefore, the host needs to have a method by which it can send out serial port characters and then impose a delay before more characters are sent. This can be done through a busy wait or an operating system sleep call or some feature built into the serial port driver (the timing is based on character-time delays, specifying at least 3.5 character-time delay to complete a command and a 1.5 character-time delay which should not be exceeded during the transmission of a command.) But it is necessary in order for the receiver of the command to consider it closed and finished."

Based on this, I suspect that there is an issue with timing. I have tried inserting a time.sleep(0.1) after commands, and this has not helped. So I have to imagine it's some detail about the difference in serial control commands and configuration. I've tried using MODBUS, but also the device only sometimes operates in MODBUS mode. The first thing the software does is send a command that's supposed to reboot the device, so it restarts in MODBUS mode, but that command is not a MODBUS command, which is why I'm using pyserial. What could be causing the issue here? How can I fix this?

Edit:

I've experimented and found that pyro_serial.rts = 1 is indeed equivalent to IOCTL_SERIAL_SET_RTS and pyro_serial.rts = 0 is equivalent to IOCTL_SERIAL_CLR_RTS

0

There are 0 best solutions below