Skip to content
Advertisement

Can someone help me understand how to use pyModbus to encode string data to a digital display board?

Using: python3, pyModbusTCP, Linux, Raspberry pi 4, ViewMarq by Automation Direct

I have a digital display board that can receive ModBusTCP ascii packets. The information below is similar to what I am trying to send as a test message.

<ID 30><CLR><WIN 0 0 287 31><POS 0 0><LJ><BL N><CS 0><GRN><T>Test</T>

I am new to python Modbus and am trying to use a pyModbus client to send data to its holding register of 411000 via port 502 which I believe is just 11000 by dropping the 4. I have set up a simple client script that can communicate with the board but I have to encode the message to send it to the register. In the world of PLCs this is much simpler but that isnt an option here.

Using the windows application to populate the board, my Wireshark modbus data was detected as follows:

Ethernet II, Src: Dell_63:da:e2 (34:48:ed:63:da:e2), Dst: FACTSEng_06:6c:fc (60:52:d0:06:6c:fc)
Internet Protocol Version 4, Src: 169.254.108.210, Dst: 169.254.108.223
Transmission Control Protocol, Src Port: 55053, Dst Port: 502, Seq: 1, Ack: 1, Len: 85
Modbus/TCP
    Transaction Identifier: 256
    Protocol Identifier: 0
    Length: 79
    Unit Identifier: 1
Modbus
    .001 0000 = Function Code: Write Multiple Registers (16)<nl>
    Reference Number: 10999
    Word Count: 36
    Byte Count: 72
    Register 10999 (UINT16): 18748
    Register 11000 (UINT16): 8260
    Register 11001 (UINT16): 12339
    Register 11002 (UINT16): 15422
    Register 11003 (UINT16): 19523
    Register 11004 (UINT16): 15954
    Register 11005 (UINT16): 22332
    Register 11006 (UINT16): 20041
    Register 11007 (UINT16): 12320
    Register 11008 (UINT16): 12320
    Register 11009 (UINT16): 12832
    Register 11010 (UINT16): 14136
    Register 11011 (UINT16): 13088
    Register 11012 (UINT16): 15921
    Register 11013 (UINT16): 20540
    Register 11014 (UINT16): 21327
    Register 11015 (UINT16): 12320
    Register 11016 (UINT16): 12320
    Register 11017 (UINT16): 15422
    Register 11018 (UINT16): 19020
    Register 11019 (UINT16): 15422
    Register 11020 (UINT16): 19522
    Register 11021 (UINT16): 20000
    Register 11022 (UINT16): 15422
    Register 11023 (UINT16): 21315
    Register 11024 (UINT16): 12320
    Register 11025 (UINT16): 15422
    Register 11026 (UINT16): 21063
    Register 11027 (UINT16): 15950
    Register 11028 (UINT16): 21564
    Register 11029 (UINT16): 21566
    Register 11030 (UINT16): 29541
    Register 11031 (UINT16): 15476
    Register 11032 (UINT16): 21551
    Register 11033 (UINT16): 3390
    Register 11034 (UINT16): 52237

From here, I am a bit confused as to what to do next. I tried reading those register addresses but saw nothing there.

from pyModbusTCP.client import ModbusClient

client = ModbusClient('169.254.108.223', port=502)
client.open()
print(client.read_holding_registers(10999,100))

and received…

[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,..... <all 100 zeros>]

Can someone help me understand if I’m moving in the right direction and what should I be doing next? I’m trying to keep this as simple as possible and I appreciate the help.

Thank you in advance.

Update:

After a suggestion I looked deeper into the documentation. The documentation of the display does these registers immediately go back to 0 when the execution is complete. From the docs *

c) Word Swap and Byte Swap should be checked assuming the selections are Off (default) in the ViewMarq display. d) Slave Modbus Memory Starting Address is the location of the Command Block within the ViewMarq display (400000+11000 = 411000). e) The Function Code should be set to 16 – Write Multiple Registers.

I have to admit. I have no idea how to write a payload. I rewrote the code and tried a payload construction approach.

from pymodbus.constants import Endian
from pymodbus.payload import BinaryPayloadDecoder
from pymodbus.payload import BinaryPayloadBuilder
from pymodbus.client.sync import ModbusTcpClient as ModbusClient
from pymodbus.compat import iteritems
from collections import OrderedDict

client =  ModbusClient('169.254.108.223', port=502)
client.connect()
builder = BinaryPayloadBuilder(byteorder=Endian.Little, wordorder=Endian.Little)
builder.add_string("<ID 30><CLR>""$0D") #$0D is the termination code in the docs
payload = builder.build()

client.write_registers(10999, payload, skip_encode=True, unit=1)

This didnt work. It ran but the board didn’t update. Suggestions are very welcome.

Advertisement

Answer

This is not a complete solution for your problem, but only a suggestion what to do next.

  • Read the documentation of the display.

    • Maybe the command buffer registers are write only?
    • Or maybe you will read all 0 when the display has processed the command?
  • Try to read the response buffer registers.

  • Convert the data from your Wireshark log to hex:
    18748=0x493c -> 0x49='I' 0x3c='<' -> little endian "<I"
    8260=0x2044 -> 0x20=' ' 0x44='D' -> little endian "D "
    12339=0x3033 -> 0x30='0' 0x33='3' -> little endian "30"

These 3 values written to registers correspond to "<ID 30" etc.

So every Modbus register seems to correspond to 2 bytes of the buffer in little-endian byte order.
Most probably it is the same for the response buffer.

Edit after question has been edited:

If your program does not work, compare the data sent from your program with the data sent from the Windows application, e.g. using Wireshark.

User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement