gavinying / modpoll

A New Command-line Tool for Modbus and MQTT
https://gavinying.github.io/modpoll
MIT License
84 stars 17 forks source link

polling coils with 2 bits possible #20

Closed jannemann70 closed 1 year ago

jannemann70 commented 1 year ago

Hello,

your examples show polling of coils with 8 or 16 bits (i.e. aligned to full bytes length):

poll,coil,1,16,BE_BE
ref,coil1-8,1,bool,rw,
ref,coil9-16,2,bool,rw,

Because our device only supports 2 bits coils, I tried polling only these 2 bits (instead of polling 8 bits), using this config lines:

poll,coil,20,2,BE_BE
ref,coil20,20,bool,rw,
ref,coil21,21,bool,rw,

Calling modpoll produces this error:

> modpoll --tcp 192.168.16.100 --config device.csv --rate 1 --interval 0.01 --timeout 0.2
modpoll - A New Command Line Tool for Modbus

2023-08-21 07:49:46,909 | I | modpoll.main | No MQTT host specified, skip MQTT setup.
2023-08-21 07:49:46,909 | I | modpoll.modbus_task | Loading config from: device.cfg
2023-08-21 07:49:46,912 | I | modpoll.modbus_task | Added new device mydevice
2023-08-21 07:49:46,912 | I | modpoll.modbus_task | Add poller (start_address=20, size=2) to device mydevice
2023-08-21 07:49:46,912 | W | modpoll.modbus_task | Reference coil20 failed to pass sanity check, ignoring it.
2023-08-21 07:49:46,913 | W | modpoll.modbus_task | Reference coil21 failed to pass sanity check, ignoring it.
2023-08-21 07:49:46,913 | I | modpoll.modbus_task | Add poller (start_address=10, size=1) to device mydevice
2023-08-21 07:49:46,913 | I | modpoll.main |  ====== modpoll polling at rate:1.0s, actual:1.0s ======
Traceback (most recent call last):
  File "c:\program files\python38\lib\runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "c:\program files\python38\lib\runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "c:\users\...\.local\bin\modpoll.exe\__main__.py", line 7, in <module>
  File "C:\Users\...\.local\pipx\venvs\modpoll\lib\site-packages\modpoll\main.py", line 75, in app
    modbus_poll()
  File "C:\Users\...\.local\pipx\venvs\modpoll\lib\site-packages\modpoll\modbus_task.py", line 384, in modbus_poll
    p.poll()
  File "C:\Users\...\.local\pipx\venvs\modpoll\lib\site-packages\modpoll\modbus_task.py", line 157, in poll
    self.device.update_reference(ref)
UnboundLocalError: local variable 'ref' referenced before assignment
>modpoll --version
modpoll - A New Command Line Tool for Modbus
modpoll v0.5.4

>python --version
Python 3.8.5

>ver
Microsoft Windows [Version 10.0.19045.3324]

Attached Wireshark output MODBUS/TCP shows the response containing 2 bits coded into 1 byte, but modpoll cannot handle this. Are there mistakes in config file (attached)? Did anyone try polling similar coils with single bits - not bytes? Do you have any examples to poll single bits (coils)?

Thanks in advance and best regards Jan

2bit-coils 2bit-coils.zip device.csv

gavinying commented 1 year ago

Modbus protocol defines Function Code 1 (read coils) to read coils (bits) by address of coils (16 bits), which means every read (1 address) will get you 16 bits of coil values. Refer to Modbus Wiki for more details.

For your case, in order to read 2 coils (2 bits), you will still need to read from 1 address (16 bits) and you need to parse the specific 2 bits from the response. If you want to read the 20th and 21st coils (bits), firstly check out which coil address to use (device document shall provide), here let's assume they are within the second coil address (address 1, because address 0 already occupies 16 bits/coils), you can try below config to read the value,

poll,coil,1,16,BE_BE
ref,coil17-32,16,bool16,rw

If it works, the received coil17-32 value shall contain the 20th and 21st coil values.

jannemann70 commented 1 year ago

I am aware that the answer contains more than two bits and I need to extract them from the appropriate place.

Wikipedia only say, the coil address and and the corresponding number of coils are of 16 bit length. (So, it's possible to request coil addresses above 256 and to request more than 255 coils at once). I have read again in the Modbus specification (page 11+12, Section 6.1, https://modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf). Here it looks like single coils, or in my case 2 coils, are allowed to be requested. The line 'poll,coil,20,2,BE_BE' does this correctly. The specification says that multiples of 8 bit are included in the response (not 16 bit). So when reading two coils, I expect only 1 byte in the response. In fact, as seen in the Wireshark log, the two bits are also correctly encoded into this response byte. Up to this point, everything is fine.

Let me briefly explain my problem:

What do you think about the suggestion to decouple the specifications of the bit-widths for the 'poll' and 'ref' commands, so that it becomes possible to query only two coils. Is this restriction by the 'ref' command absolutely necessary?

Make working something similar like this would be very nice:

poll,coil,20,2,BE_BE
ref,coil20-36,16,bool16,rw

or better

poll,coil,20,2,BE_BE
ref,coil20-28,8,bool8,rw

I can live well with the representation of whole bytes or words in the output. The output of single coils would be a future nice new feature, but is not necessary. But decoupling the hardcoded data widths is really a problem, when the device only allows requesting less than 8 coils.

gavinying commented 1 year ago

OK, I see what you mean. We shall allow the tool read only 2 coils and receive one byte which contains 2 bits with padding 6 zero bits at higher order. Let me figure it out.

gavinying commented 1 year ago

Hi @jannemann70 , please try using the latest version (v0.6.0) with the following config,

poll,coil,20,2,BE_BE
ref,coil20-21,20,bool8,rw

Just to point out, the <address> in ref line uses the MODBUS address, which is different from the coil no. So address 20 and coil 20 is totally different, the above config assumes your target coils are at address 20.

poll,<object_type>,<start_address>,<size>,<endian>
ref,<ref_name>,<address>,<dtype>,<rw>,<unit>,<scale>

Let me know if it works on your side.

Shaodong

jannemann70 commented 1 year ago

Installed successfully with both on windows (python 3.8.5) pip3 install modpoll==0.6.0 or pip3 install modpoll==0.6.1

Tried using tag 0.6.0 (and 0.6.1), running into similar error as described in #24.

C:\WINDOWS\system32>modpoll -v
Traceback (most recent call last):
  File "c:\program files\python38\lib\runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "c:\program files\python38\lib\runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "C:\Program Files\Python38\Scripts\modpoll.exe\__main__.py", line 4, in <module>
ImportError: cannot import name 'app' from 'modpoll' (c:\program files\python38\lib\site-packages\modpoll\__init__.py)

Are there any further steps to do?

gavinying commented 1 year ago

The issue was caused by poetry tool changes, which is fixed in v0.6.2, feel free to try again.

jannemann70 commented 1 year ago

RESOLVED Thanks a lot for immediate resposes, and feature enhancements! Great tool for testing purposes. Best regards, Jan

Here, my output for the device I'm testing:

device,mydevice,1,,
poll,coil,20,2,BE_BE
ref,coil20-21,20,bool8,rw
poll,input_register,10,1,BE_BE
ref,value,10,int16,r,myunit,0.1
> modpoll --tcp 192.168.16.100 --config device.cfg --rate 0.1 --interval 0.01 -p

modpoll - A New Command Line Tool for Modbus

2023-09-19 09:29:45,898 | I | modpoll.main | No MQTT host specified, skip MQTT setup.
2023-09-19 09:29:45,898 | I | modpoll.modbus_task | Loading config from: device.cfg
2023-09-19 09:29:45,901 | I | modpoll.modbus_task | Added new device mydevice
2023-09-19 09:29:45,902 | I | modpoll.modbus_task | Add poller (start_address=20, size=2) to device mydevice
2023-09-19 09:29:45,902 | I | modpoll.modbus_task | Add poller (start_address=10, size=1) to device mydevice
2023-09-19 09:29:45,903 | I | modpoll.main |  ====== modpoll polling at rate:0.1s, actual:0.1s ======
2023-09-19 09:29:45,960 | I | modpoll.modbus_task | Reading device:mydevice, FuncCode:1, Start_address:20, Size:2... SUCCESS
2023-09-19 09:29:45,978 | I | modpoll.modbus_task | Reading device:mydevice, FuncCode:4, Start_address:10, Size:1... SUCCESS
===== references from device: mydevice =====
+-----------+--------+---------+---------------------------------------------------------+
|    name   |  unit  | address |                          value                          |
+-----------+--------+---------+---------------------------------------------------------+
| coil20-21 |  None  |    20   | [False, False, False, False, False, False, True, False] |
|   value   | myunit |    10   |                            0                            |
+-----------+--------+---------+---------------------------------------------------------+
Done.

2023-09-19 09:29:46,004 | I | modpoll.main |  ====== modpoll polling at rate:0.1s, actual:0.100693s ======
2023-09-19 09:29:46,013 | I | modpoll.modbus_task | Reading device:mydevice, FuncCode:1, Start_address:20, Size:2... SUCCESS
2023-09-19 09:29:46,030 | I | modpoll.modbus_task | Reading device:mydevice, FuncCode:4, Start_address:10, Size:1... SUCCESS
===== references from device: mydevice =====
+-----------+--------+---------+---------------------------------------------------------+
|    name   |  unit  | address |                          value                          |
+-----------+--------+---------+---------------------------------------------------------+
| coil20-21 |  None  |    20   | [False, False, False, False, False, False, True, False] |
|   value   | myunit |    10   |                            0                            |
+-----------+--------+---------+---------------------------------------------------------+
Done.

...