Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow inversion of serial signals #74

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from

Conversation

sandyscott
Copy link

Add "invert" argument to Serial and ModbusRTU class constructors, and include the INV_RX and INV_TX constants as class variables to be used to fill this argument. This is passed to the UART constructor.

@brainelectronics brainelectronics added the enhancement New feature or request label Jul 1, 2023
@brainelectronics brainelectronics self-assigned this Jul 1, 2023
brainelectronics added a commit to sandyscott/micropython-modbus that referenced this pull request Jul 2, 2023
Copy link
Owner

@brainelectronics brainelectronics left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTMyself, maybe @beyonlo wants to perform some tests as well? 😄

@beyonlo
Copy link

beyonlo commented Jul 3, 2023

@brainelectronics I used the https://github.com/sandyscott/micropython-modbus/tree/develop to do this test, but I have error, maybe I do not used correctly:

I tried to use invert on the rtu_client_example.py example. Here part that I changed from that example:

client = ModbusRTU(
    addr=slave_addr,        # address on bus
    pins=rtu_pins,          # given as tuple (TX, RX)
    baudrate=baudrate,      # optional, default 9600
    # data_bits=8,          # optional, default 8
    # stop_bits=1,          # optional, default 1
    # parity=None,          # optional, default None
    ctrl_pin=15,          # optional, control DE/RE
    uart_id=uart_id,         # optional, default 1, see port specific docs
    invert=1
)

Added just line invert=1

I believe that param 0 on invert is to not invert (False) and 1 is to invert (True). Am I correct?

I have this error:

$ mpremote run rtu_client_example.py 
MicroPython infos: (sysname='esp32', nodename='esp32', release='1.20.0', version='v1.20.0 on 2023-04-26', machine='ESP32S3 module (spiram) with ESP32S3')
Used micropthon-modbus version: 0.0.0
Using pins (37, 38) with UART ID 1
Traceback (most recent call last):
  File "<stdin>", line 26, in <module>
  File "umodbus/serial.py", line 68, in __init__
  File "umodbus/serial.py", line 124, in __init__
ValueError: invalid inversion mask

Ps: When I use invert=0 I have no error

@sandyscott
Copy link
Author

@beyonlo
The idea was the that the invert argument works the same way as at does with the built-in machine.UART.init().

from
https://docs.micropython.org/en/latest/library/machine.UART.html

invert specifies which lines to invert.

  • 0 will not invert lines (idle state of both lines is logic high).
  • UART.INV_TX will invert TX line (idle state of TX line now logic low).
  • UART.INV_RX will invert RX line (idle state of RX line now logic low).
  • UART.INV_TX | UART.INV_RX will invert both lines (idle state at logic low).

As machine.UART is not in the namespace if you've only imported ModbusRTU or Serial, I added those two constants to the classes, so you can use them like this:

import ModbusRTU

# Create object with both signals inverted:
client = ModbusRTU(
    addr=slave_addr,        # address on bus
    pins=rtu_pins,          # given as tuple (TX, RX)
    baudrate=baudrate,      # optional, default 9600
    # data_bits=8,          # optional, default 8
    # stop_bits=1,          # optional, default 1
    # parity=None,          # optional, default None
    ctrl_pin=15,          # optional, control DE/RE
    uart_id=uart_id,         # optional, default 1, see port specific docs
    invert=ModbusRTU.INV_TX | ModbusRTU.INV_RX
)

There might be a more intuitive way to do it, maybe:

  • wrapping those constants in a minimal UART object within the ModbusRTU classes:
import ModbusRTU

client = ModbusRTU(
    ...
    invert=ModbusRTU.UART.INV_TX | ModbusRTU.UART.INV_RX
)
  • or just rely on the user to import the constants directly from machine.UART
import ModbusRTU
from machine import UART

client = ModbusRTU(
    ...
    invert=UART.INV_TX | UART.INV_RX
)

@beyonlo
Copy link

beyonlo commented Jul 3, 2023

@sandyscott I'm sorry, I was thinking that invert is just to configure tx pin change to be rx and rx pin change to be tx, in cases that user do not need to "invert" the wires when connect they inverted.

Anyway now that I know that is not about changes pins tx and rx, what is advantage to have a UART with lines inverted, from high to low and vice-versa?

Well, I did changes as you give me the example, but do not works. I think if all working in invert=0 and I invert lines TX and RX from Slave and Master, should be continue working right?

rtu_client_example.py:

client = ModbusRTU(
    addr=slave_addr,        # address on bus
    pins=rtu_pins,          # given as tuple (TX, RX)
    baudrate=baudrate,      # optional, default 9600
    # data_bits=8,          # optional, default 8
    # stop_bits=1,          # optional, default 1
    # parity=None,          # optional, default None
    ctrl_pin=15,          # optional, control DE/RE
    uart_id=uart_id,         # optional, default 1, see port specific docs
    invert=ModbusRTU.INV_TX | ModbusRTU.INV_RX
)

rtu_host_example.py:

host = ModbusRTUMaster(
    pins=rtu_pins,          # given as tuple (TX, RX)
    baudrate=baudrate,      # optional, default 9600
    # data_bits=8,          # optional, default 8
    # stop_bits=1,          # optional, default 1
    # parity=None,          # optional, default None
    ctrl_pin=15,          # optional, control DE/RE
    uart_id=uart_id,         # optional, default 1, see port specific docs
    invert=ModbusRTUMaster.INV_TX | ModbusRTUMaster.INV_RX
)

Slave RTU:

$ mpremote run rtu_client_example.py 
MicroPython infos: (sysname='esp32', nodename='esp32', release='1.20.0', version='v1.20.0 on 2023-04-26', machine='ESP32S3 module (spiram) with ESP32S3')
Used micropthon-modbus version: 0.0.0
Using pins (37, 38) with UART ID 1
Setting up registers ...
Register setup done
Serving as RTU client on address 10 at 115200 baud

Master RTU:

$ mpremote run rtu_host_example.py 
MicroPython infos: (sysname='esp32', nodename='esp32', release='1.20.0', version='v1.20.0 on 2023-04-26', machine='ESP32S3 module (spiram) with ESP32S3')
Used micropthon-modbus version: 0.0.0
Using pins (17, 18) with UART ID 1
Requesting and updating data on RTU client at address 10 with 115200 baud

Traceback (most recent call last):
  File "<stdin>", line 54, in <module>
  File "examples/common/host_tests.py", line 130, in run_sync_host_tests
  File "umodbus/common.py", line 136, in read_coils
  File "umodbus/serial.py", line 331, in _send_receive
  File "umodbus/serial.py", line 355, in _validate_resp_hdr
OSError: no data received from slave

@sandyscott
Copy link
Author

sandyscott commented Jul 4, 2023

If you want to swap the RX & TX pins you can just reverse their order in the tuple you supply to the pins argument, but that will cause problems for hardware UARTs.

It's useful to be able to flip the polarity of the signals because not all serial transciever chips invert the signals internally. For example I'm using an ADM2483 RS485 transciever in a project. To produce a standard-compliant logic 1 at the output - ie. the B signal is a greater voltage than the A signal - you need drive the TxD pin low, and vice-versa. While you could flip them in hardware, the correct way of doing it is to invert the input signal to the transciever, which is apparently a convention that dates from how RS232 was implemented in the past. I assume that this is the reason that the built-in machine.UART provides this feature.

As for the example, I can't see why that wouldn't work, the only thing that's a little odd are the pins your example is using - both common/rtu_client_common and common/rtu_host_common specify pins 25 & 26 for the esp32, but that's not what's being used in your output. I'll try to make some time to investigate further, but it might not be possible for a little while.

@beyonlo
Copy link

beyonlo commented Jul 4, 2023

If you want to swap the RX & TX pins you can just reverse their order in the tuple you supply to the pins argument, but that will cause problems for hardware UARTs.

It's useful to be able to flip the polarity of the signals because not all serial transciever chips invert the signals internally. For example I'm using an ADM2483 RS485 transciever in a project. To produce a standard-compliant logic 1 at the output - ie. the B signal is a greater voltage than the A signal - you need drive the TxD pin low, and vice-versa. While you could flip them in hardware, the correct way of doing it is to invert the input signal to the transciever, which is apparently a convention that dates from how RS232 was implemented in the past. I assume that this is the reason that the built-in machine.UART provides this feature.

Hello @sandyscott thank you for the explanation!

As for the example, I can't see why that wouldn't work, the only thing that's a little odd are the pins your example is using - both common/rtu_client_common and common/rtu_host_common specify pins 25 & 26 for the esp32, but that's not what's being used in your output. I'll try to make some time to investigate further, but it might not be possible for a little while.

I changed that pins of examples to pins of my boards (Slave and Master), that's why is different. But if I remove the line invert=ModbusRTU.INV_TX | ModbusRTU.INV_RX on the Slave and line invert=ModbusRTUMaster.INV_TX | ModbusRTUMaster.INV_RX on the Master, all works.

sandyscott and others added 2 commits July 19, 2023 23:03
Add "invert" argument to Serial and ModbusRTU classes, and include the INV_RX and INV_TX constants as class variables to be used to fill this argument. This is passed to the UART constructor.
@hmaerki
Copy link

hmaerki commented Jan 24, 2024

Proposal:

  • Remove from the constructor ModbusRTU: ctrl_pin and invert.
  • Add to the constructor ModbusRTU: ctrl_pin_cb.

Now the developer may implement the ctrl_pin_cb function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants