最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - Pymodbus, TypeError ⸺ too many positional arguments - Stack Overflow

programmeradmin0浏览0评论

Hardware and Background Story

  • Python3 using VS-Code as my IDE

  • Linux Mint 21.3 (Cinnamon)

  • Attempting serial communication to query a PZEM-004T (v3) AC power
    module ("slave"), which uses RS-485 (Modbus) comms via a Prolific® PL2303 TTL-to-USB converter

  • I confirmed the device address = 0x01, by using Docklight® (run separately on Windows platform)

I began with Python code provided on Github as my starting point. But, as that Code was provided, it gave multiple errors associated with the "preamble". I replaced the preamble / setup of that Github-provided Code, which was originally

from pymodbus.client import ModbusSerialClient as ModbusClient

# configure the serial port
client = ModbusClient(method='rtu', port='/dev/ttyUSB0', baudrate=9600)

with the first 11 lines of code shown below. All else remains the same. My modified code will now start to run, but throws an error message (quoted after my faulty code)

{Faulty} Code

from pymodbus.client.sync import ModbusSerialClient
# configure the serial port
client = ModbusSerialClient(
    method='rtu',
    port='/dev/ttyUSB0',
    baudrate=9600,
    #timeout=3,
    parity='N',
    stopbits=1,
    bytesize=8
)


#device default address is 0x01. However you can change the address by modifying the register that sets the address
device_address = 0x01

# connect to the PZEM-004T
client.connect()


# read the voltage value
result = client.read_input_registers(0x0000, 1, device_address)
voltage = float(result.registers[0] / 10.0)

# read the current value
result = client.read_input_registers(0x0001, 2, device_address)
current = float(result.registers[0] / 1000.0)

# read the power value
result = client.read_input_registers(0x0003, 2, device_address)
power = float(result.registers[0])

# read energy value
result = client.read_input_registers(0x0005, 2, device_address)
energy = float(result.registers[0])

# read the frequency value
result = client.read_input_registers(0x0007, 1, device_address)
frequency = float(result.registers[0] / 10.0)

#read the power factor value
result = client.read_input_registers(0x0008, 1, device_address)
power_factor = float(result.registers[0] / 100.0)


# print the values
print(f"Voltage: {voltage} V")
print(f"Current: {current} A")
print(f"Power: {power} W")
print(f"Energy: {energy} Wh")
print(f"Frequency: {frequency} Hz")
print(f"Power Factor: {power_factor} ")


# disconnect from the PZEM-004T
client.close()

Error Thrown

Traceback (most recent call last):
  File "/home/username/Documents/Python_PZEM_Module/another_PZEM_test", line 22, in <module>
    result = client.read_input_registers(0x0000, 1, 0x01)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: ModbusClientMixin.read_input_registers() takes from 2 to 3 positional arguments but 4 were given

Can anybody throw some light on this problem?

I also posted a comment at the source of the Github code, but I am not expecting any results there.

Update, 2025-02-04

Following comment from Brits, I changed the "preamble" of my code to

import asyncio
import time
from pymodbus import FramerType
from pymodbus.client import AsyncModbusSerialClient, ModbusSerialClient

# configure the serial port
client = ModbusClient.ModbusSerialClient(
            "/dev/ttyUSB0",
            framer=FramerType.RTU,
            baudrate=9600,
            bytesize=8,
            parity="N",
            stopbits=1,
)

and now the error message being thrown is:

username@userplatform:~/Documents/Python_PZEM_Module$  cd /home/username/Documents/Python_PZEM_Module ; /usr/bin/env /bin/python3.11 /home/username/.vscode/extensions/ms-python.debugpy-2024.14.0-linux-x64/bundled/libs/debugpy/adapter/../../debugpy/launcher 39645 -- /home/username/Documents/Python_PZEM_Module/another_PZEM_test 
Traceback (most recent call last):
  File "/home/username/Documents/Python_PZEM_Module/another_PZEM_test", line 3, in <module>
    from pymodbus import FramerType
ImportError: cannot import name 'FramerType' from 'pymodbus' (/usr/lib/python3/dist-packages/pymodbus/__init__.py)

Update, 2025-02-05, after comment by Brits

I have been using Python3 on my Linux MINT (v21.3) platform. Here, Python3 was installed using the Synaptic Package Manager (hereafter, SPM....i.e., official "system version" proffered by MINT) It seems that the ONLY version of Pymodbus available in the so-called Up to Date Python3 SPM package is as follows:

/var/cache/apt/archives/python3-pymodbus_2.1.0+dfsg-2_all.deb

and I assume that this is Pymodbus_2.1

As Pymodbus3.** is not available from the SPM, I uninstalled Pymodbus (2.1.0) via the SPM. I then installed Pymodbus3.8.3 using pip install pymodbus. Unfortunately, pip does NOT install in the same file structure used by the SPM version. In fact, pip installed in the directory /home/myusername/.local/lib/python3.10/site-packages/pymodbus-3.8.3.dist-info

Now...

While using VS-Code, if I try to run my program it throws this error

ModuleNotFoundError: No module named 'pymodbus'

which is (no doubt) related to errors in the environment variables of .bashrc etc. When attempting to correct the .bashrc (etc etc), I did not have success and I have un-done those changes before creating even more problems for myself !!!

Question:

Would it be best to completely removed ALL of the SPM installation of Python from my platform, and reinstall everything using ONLY pip ?

Update, 2025-02-08:

After getting further advice on the Mint forum, my ignorance regarding Python & "virtual environments" has been mostly resolved. So now... I have cleaned-up prior gaffs attempting to install Pymodbus v3 whilst using pip; and, I have Pymodbus v3.8.3 installed in such a virtual environment; and, I have made the necessary changes in VS-Code to point to the new virtual environment. But.....here is the kicker.... Although VS-Code does NOT report any "problems" (its terminology) when running the code, it still throws the same error, i.e.,

TypeError: ModbusClientMixin.read_input_registers() takes from 2 to 3 positional arguments but 4 were given
_________Questions:
  • Does this still look like a conflict due to the (incorrect) version of Pymodbus?
  • What specifically is meant by the phrase, "positional arguments" ?
Update 2025-02-08, re: Positional Arguments"

In the Github original code, "pzem.py" the syntax for reading from the register for a slave of address 0x0001 is given as

result = client.read_input_registers(0x0000, 1, 0x0001)

If using Pymodbus v3.8.3, the correct syntax is

result = client.read_input_registers(0x0000,count=1, slave=1)

i.e., you must explicitly use count= and slave= in the listing of the "positional arguments".

It is also necessary to replace the original preamble,

from pymodbus.client.sync import ModbusSerialClient # configure the serial port

with this preamble:

from pymodbus.client import ModbusSerialClient as ModbusClient

Of course (!) you must be running within a virtual environment which provides Pymodbus v3

Hardware and Background Story

  • Python3 using VS-Code as my IDE

  • Linux Mint 21.3 (Cinnamon)

  • Attempting serial communication to query a PZEM-004T (v3) AC power
    module ("slave"), which uses RS-485 (Modbus) comms via a Prolific® PL2303 TTL-to-USB converter

  • I confirmed the device address = 0x01, by using Docklight® (run separately on Windows platform)

I began with Python code provided on Github as my starting point. But, as that Code was provided, it gave multiple errors associated with the "preamble". I replaced the preamble / setup of that Github-provided Code, which was originally

from pymodbus.client import ModbusSerialClient as ModbusClient

# configure the serial port
client = ModbusClient(method='rtu', port='/dev/ttyUSB0', baudrate=9600)

with the first 11 lines of code shown below. All else remains the same. My modified code will now start to run, but throws an error message (quoted after my faulty code)

{Faulty} Code

from pymodbus.client.sync import ModbusSerialClient
# configure the serial port
client = ModbusSerialClient(
    method='rtu',
    port='/dev/ttyUSB0',
    baudrate=9600,
    #timeout=3,
    parity='N',
    stopbits=1,
    bytesize=8
)


#device default address is 0x01. However you can change the address by modifying the register that sets the address
device_address = 0x01

# connect to the PZEM-004T
client.connect()


# read the voltage value
result = client.read_input_registers(0x0000, 1, device_address)
voltage = float(result.registers[0] / 10.0)

# read the current value
result = client.read_input_registers(0x0001, 2, device_address)
current = float(result.registers[0] / 1000.0)

# read the power value
result = client.read_input_registers(0x0003, 2, device_address)
power = float(result.registers[0])

# read energy value
result = client.read_input_registers(0x0005, 2, device_address)
energy = float(result.registers[0])

# read the frequency value
result = client.read_input_registers(0x0007, 1, device_address)
frequency = float(result.registers[0] / 10.0)

#read the power factor value
result = client.read_input_registers(0x0008, 1, device_address)
power_factor = float(result.registers[0] / 100.0)


# print the values
print(f"Voltage: {voltage} V")
print(f"Current: {current} A")
print(f"Power: {power} W")
print(f"Energy: {energy} Wh")
print(f"Frequency: {frequency} Hz")
print(f"Power Factor: {power_factor} ")


# disconnect from the PZEM-004T
client.close()

Error Thrown

Traceback (most recent call last):
  File "/home/username/Documents/Python_PZEM_Module/another_PZEM_test", line 22, in <module>
    result = client.read_input_registers(0x0000, 1, 0x01)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: ModbusClientMixin.read_input_registers() takes from 2 to 3 positional arguments but 4 were given

Can anybody throw some light on this problem?

I also posted a comment at the source of the Github code, but I am not expecting any results there.

Update, 2025-02-04

Following comment from Brits, I changed the "preamble" of my code to

import asyncio
import time
from pymodbus import FramerType
from pymodbus.client import AsyncModbusSerialClient, ModbusSerialClient

# configure the serial port
client = ModbusClient.ModbusSerialClient(
            "/dev/ttyUSB0",
            framer=FramerType.RTU,
            baudrate=9600,
            bytesize=8,
            parity="N",
            stopbits=1,
)

and now the error message being thrown is:

username@userplatform:~/Documents/Python_PZEM_Module$  cd /home/username/Documents/Python_PZEM_Module ; /usr/bin/env /bin/python3.11 /home/username/.vscode/extensions/ms-python.debugpy-2024.14.0-linux-x64/bundled/libs/debugpy/adapter/../../debugpy/launcher 39645 -- /home/username/Documents/Python_PZEM_Module/another_PZEM_test 
Traceback (most recent call last):
  File "/home/username/Documents/Python_PZEM_Module/another_PZEM_test", line 3, in <module>
    from pymodbus import FramerType
ImportError: cannot import name 'FramerType' from 'pymodbus' (/usr/lib/python3/dist-packages/pymodbus/__init__.py)

Update, 2025-02-05, after comment by Brits

I have been using Python3 on my Linux MINT (v21.3) platform. Here, Python3 was installed using the Synaptic Package Manager (hereafter, SPM....i.e., official "system version" proffered by MINT) It seems that the ONLY version of Pymodbus available in the so-called Up to Date Python3 SPM package is as follows:

/var/cache/apt/archives/python3-pymodbus_2.1.0+dfsg-2_all.deb

and I assume that this is Pymodbus_2.1

As Pymodbus3.** is not available from the SPM, I uninstalled Pymodbus (2.1.0) via the SPM. I then installed Pymodbus3.8.3 using pip install pymodbus. Unfortunately, pip does NOT install in the same file structure used by the SPM version. In fact, pip installed in the directory /home/myusername/.local/lib/python3.10/site-packages/pymodbus-3.8.3.dist-info

Now...

While using VS-Code, if I try to run my program it throws this error

ModuleNotFoundError: No module named 'pymodbus'

which is (no doubt) related to errors in the environment variables of .bashrc etc. When attempting to correct the .bashrc (etc etc), I did not have success and I have un-done those changes before creating even more problems for myself !!!

Question:

Would it be best to completely removed ALL of the SPM installation of Python from my platform, and reinstall everything using ONLY pip ?

Update, 2025-02-08:

After getting further advice on the Mint forum, my ignorance regarding Python & "virtual environments" has been mostly resolved. So now... I have cleaned-up prior gaffs attempting to install Pymodbus v3 whilst using pip; and, I have Pymodbus v3.8.3 installed in such a virtual environment; and, I have made the necessary changes in VS-Code to point to the new virtual environment. But.....here is the kicker.... Although VS-Code does NOT report any "problems" (its terminology) when running the code, it still throws the same error, i.e.,

TypeError: ModbusClientMixin.read_input_registers() takes from 2 to 3 positional arguments but 4 were given
_________Questions:
  • Does this still look like a conflict due to the (incorrect) version of Pymodbus?
  • What specifically is meant by the phrase, "positional arguments" ?
Update 2025-02-08, re: Positional Arguments"

In the Github original code, "pzem.py" the syntax for reading from the register for a slave of address 0x0001 is given as

result = client.read_input_registers(0x0000, 1, 0x0001)

If using Pymodbus v3.8.3, the correct syntax is

result = client.read_input_registers(0x0000,count=1, slave=1)

i.e., you must explicitly use count= and slave= in the listing of the "positional arguments".

It is also necessary to replace the original preamble,

from pymodbus.client.sync import ModbusSerialClient # configure the serial port

with this preamble:

from pymodbus.client import ModbusSerialClient as ModbusClient

Of course (!) you must be running within a virtual environment which provides Pymodbus v3

Share Improve this question edited Feb 8 at 21:47 Birdman asked Feb 2 at 19:25 BirdmanBirdman 95 bronze badges 8
  • I'm guessing the example was written for pymodbus v2, and you are using v3. This example (more detail) should point you in the right direction (the changes should be fairly straight forward). – Brits Commented Feb 2 at 22:47
  • Please confirm the version of pymodbus you have installed (the above comment was a guess based upon the error you received). – Brits Commented Feb 4 at 8:59
  • Please see additional information under "Update, 2025-02-05...." – Birdman Commented Feb 6 at 0:09
  • "... to completely removed" consider using venv; a virtual environment means you will have your "own independent set of installed Python packages in its site directories" (this enables you to experiment with different library versions etc). – Brits Commented Feb 6 at 0:53
  • @Brits : Thank you for that suggestion, venv was not on my radar-screen. However, before attempting to do that, would you please tell me this --- Does pip always install in the directory currently in use when executing pip commands from the terminal interface? or, Does pip install modules in a pip-defined default directory? Thanks for clarification, as I don't want to create additional problems for myself! – Birdman Commented Feb 6 at 18:29
 |  Show 3 more comments

1 Answer 1

Reset to default 0

The function definition can be found in the documentation for Pymodbus under the Client section. Here we can see the following arguments are required for the read_input function with a return type of T.

read_input_registers(address: int, *, count: int = 1, slave: int = 1, no_response_expected: bool = False)→ T

If you're just starting on Python this can sometime be hard to understand, I recommend starting your research with Python function definitions and calls. However, I will explain this one as best as possible.

First, in Python class function usually take in self as the first argument, this is not noted in the documentation. But in your use case the variable client which is an instance of ModbusClient.ModbusSerialClient is your first "positional argument". So weirdly, we start counting the class' positional arguments from two.

The second argument is address: int. We know this is a positional argument because there is no equals sign (=) following the variable name. According to the documentation, the address is the "Start address to read from." The next argument is *, which is special when in function declarations, but here can be thought of as the separator between positional and keyword arguments.

Thus it follows that everything after this is a keyword argument. count: int = 1 means to take in a keyword argument by the name of count with a default value of 1. These keyword arguments do not have to be included and will use the default values if not.

I believe that the correct syntax for the latest version of Pymodbus you're looking for is the following:

result = client.read_input_registers(0x0000, count=1)
result = client.read_input_registers(0x0001, count=1)
...
result = client.read_input_registers(0x0007, count=1)

Finally, I did some quick investigation into the previous source code. That's why open source is so awesome! It looks like the function definition used to be different in version 2.1.0.

def read_input_registers(self, address, count=1, **kwargs):
       '''

       :param address: The starting address to read from
       :param count: The number of registers to read
       :param unit: The slave unit this request is targeting
       :returns: A deferred response handle
       '''
       request = ReadInputRegistersRequest(address, count, **kwargs)
       return self.execute(request)

From these we can see that unit has been renamed to slave in the newer version, and a new argument has been introduced named no_response_expected. Also I would note that when you call the old function definition with a keyword that is also a positional argument, the keyword takes precedence.

发布评论

评论列表(0)

  1. 暂无评论