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 converterI 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 converterI 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 | Show 3 more comments1 Answer
Reset to default 0The 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.
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:53venv
was not on my radar-screen. However, before attempting to do that, would you please tell me this --- Doespip
always install in the directory currently in use when executingpip
commands from the terminal interface? or, Doespip
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