pymodbus-dev / pymodbus

A full modbus protocol written in python
Other
2.16k stars 889 forks source link

Use pyserial RS485 - not serial_from_url #2204

Open samskiter opened 1 month ago

samskiter commented 1 month ago

Versions

Pymodbus Specific

Description

I'm trying to use pymodbus with the UART on the RPi 4B with a half duplex RS485 chip that needs nRE/DE input to correctly drive/receive over half duplex comms. I have been trying to set RS485 mode into the serial port opened and found this existing issue where someone else had the same problem

This is confounded by a bug on the RPi/with pyserial that causes the UART to be pernickity about how it is configured.

I'd like to change the RS485 mode on the serial port created by pymodbus, but instead it seems I need to swap the whole serial port out.

It seems that it would be sensible for pymodbus to open an RS485 serial port, rather than a plain RS232 UART.

BONUS would be to allow users to configure the RS485 port directly through PyModbus.

Code and Logs

I have tried to configure RS485 settings on the serial port after connection like this:

    def blah() -> None:
        self.async_client = ModbusClient.AsyncModbusSerialClient(
            port=self.protocol.port,
            baudrate=self.protocol.baud_rate,
            byte_size=self.protocol.byte_size,
            parity=self.protocol.parity,
            stopbits=self.protocol.stop_bits,
        )
        await self.connect()
        return self.protocol

    async def connect(self) -> None:
        """
        Connects to the Modbus device.
        """
        await self.async_client.connect()

        transport: SerialTransport = self.async_client.transport
        serial_object: Serial = transport.sync_serial
        serial_object.rtscts = True
        serial_object.rs485_mode = serial.rs485.RS485Settings(
            rts_level_for_tx=False,
            rts_level_for_rx=True,
            loopback=False,
            delay_before_tx=0.0000035,
            delay_before_rx=0.0000001,
        )
        self.connected = True

But I hit the bug mentioned above (https://github.com/raspberrypi/linux/issues/6189)

Proposed change. https://github.com/pymodbus-dev/pymodbus/blob/dev/pymodbus/transport/serialtransport.py#L23 Before:

def __init__(self, loop, protocol, *args, **kwargs) -> None:
        """Initialize."""
        super().__init__()
        self.async_loop = loop
        self.intern_protocol: asyncio.BaseProtocol = protocol
        self.sync_serial = serial.serial_for_url(*args, **kwargs)
        self.intern_write_buffer: list[bytes] = []
        self.poll_task: asyncio.Task | None = None
        self._poll_wait_time = 0.0005
        self.sync_serial.timeout = 0
        self.sync_serial.write_timeout = 0

After:

def __init__(self, loop, protocol, *args, **kwargs) -> None:
        """Initialize."""
        super().__init__()
        self.async_loop = loop
        self.intern_protocol: asyncio.BaseProtocol = protocol
        self.sync_serial = serial.rs485.RS485(*args, **kwargs) # This worked for me in my tests
        self.intern_write_buffer: list[bytes] = []
        self.poll_task: asyncio.Task | None = None
        self._poll_wait_time = 0.0005
        self.sync_serial.timeout = 0
        self.sync_serial.write_timeout = 0
janiversen commented 1 month ago

Feel free to submit a pull request.

github-actions[bot] commented 2 days ago

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.