'[Input/Output] Modbus Error: [Invalid Message] Incomplete message received, expected at least 8 bytes (0 received)'

'[Input/Output] Modbus Error: [Invalid Message] Incomplete message received, expected at least 8 bytes (0 received)'

我正在尝试将我的 raspberry pi 用作 modbus 从站,并从我的常规 windows 机器(代表 modbus 主站)获取数据。现在从机和主机都在同一台 (windows) 机器上 运行,以预先尝试。

这是我根据pymodbus的需要改编的modbus slave updating server example:

    #!/usr/bin/env python
# --------------------------------------------------------------------------- #
# import the modbus libraries we need
# --------------------------------------------------------------------------- #
from pymodbus.server.asynchronous import StartTcpServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.transaction import ModbusRtuFramer, ModbusAsciiFramer

# --------------------------------------------------------------------------- #
# import the twisted libraries we need
# --------------------------------------------------------------------------- #
from twisted.internet.task import LoopingCall

# --------------------------------------------------------------------------- #
# configure the service logging
# --------------------------------------------------------------------------- #
import logging, random
logging.basicConfig(format="%(message)s")
log = logging.getLogger()
log.setLevel(logging.INFO)

# --------------------------------------------------------------------------- #
# define your callback process
# --------------------------------------------------------------------------- #
def update_temperature(kw):
    context = kw["context"]
    slave = kw["slave_info"] 
    functionCode_read = 3
    functionCode_write = 6

    # coils     :   0x0000  : fc=01 (read) fc=05 (write)
    # inputs    :   0x2710  : fc=02 (read)
    # inp.reg.  :   0x7530  : fc=04 (read)  
    # hold.reg. :   0x9C40  : fc=03 (read) fc=06 (write)

    values = context[slave["id"]].getValues(functionCode_read, slave["holding_registers"])
    log.info("old temperature values: " + str(values))

    for i in range(len(values)): # 
        if values[i] <= 16:
            values[i] = values[i]+1 if (random.random() >= 0.3) else values[i]-1
        elif values[i] >= 26:
            values[i] = values[i]+1 if (random.random() >= 0.7) else values[i]-1
        else:
            values[i] = values[i]+1 if (random.random() >= 0.5) else values[i]-1

    log.info("new temperature values: " + str(values))
    context[slave["id"]].setValues(functionCode_write, slave["holding_registers"], values)

def update_window_contact(kw):
    context = kw["context"]
    slave = kw["slave_info"] 
    functionCode_read = 1
    functionCode_write = 5

    # coils     :   0x0000  : fc=01 (read) fc=05 (write)
    # inputs    :   0x2710  : fc=02 (read)
    # inp.reg.  :   0x7530  : fc=04 (read)  
    # hold.reg. :   0x9C40  : fc=03 (read) fc=06 (write)

    values = context[slave["id"]].getValues(functionCode_read, slave["coils"])
    log.info("old contact values: " + str(values))

    for i in range(len(values)):
        if values[i] == 0:
            values[i] = 1 if (random.random() >= 0.95) else 0
        else: # value is 1
            values[i] = 0 if (random.random() >= 0.95) else 1

    log.info("new contact values: " + str(values))
    context[slave["id"]].setValues(functionCode_write, slave["coils"], values)


def run_updating_server():
    # ----------------------------------------------------------------------- # 
    # initializing devices
    # ----------------------------------------------------------------------- # 
    temperature_slave = { # temperature sensor
        "id": 0x01, 
        "coils": 0x0000,
        "inputs": 0x2710,
        "input_registers": 0x7530,
        "holding_registers": 0x9C40
    }
    contact_slave = { # window sensor
        "id": 0x02,
        "coils": 0x0000,
        "inputs": 0x2710,
        "input_registers": 0x7530,
        "holding_registers": 0x9C40
    } 

    store = {
        temperature_slave["id"]: ModbusSlaveContext( 
            co=ModbusSequentialDataBlock(address=temperature_slave["coils"], values=[None]),                  # coils  / discrete outp. (adress=0)
            di=ModbusSequentialDataBlock(address=temperature_slave["inputs"], values=[None]),              # inputs / discrete input (adress=10000)
            ir=ModbusSequentialDataBlock(address=temperature_slave["input_registers"], values=[None]),     # input register / analog input (adress=30000)
            hr=ModbusSequentialDataBlock(address=temperature_slave["holding_registers"], values=[16]),   # hold. register / analog outp. (adress=40000)
            zero_mode=True),
        contact_slave["id"]: ModbusSlaveContext( 
            co=ModbusSequentialDataBlock(address=temperature_slave["coils"], values=[1]),               # coils  / discrete outp. (adress=0)
            di=ModbusSequentialDataBlock(address=temperature_slave["inputs"], values=[None]),              # inputs / discrete input (adress=10000)
            ir=ModbusSequentialDataBlock(address=temperature_slave["input_registers"], values=[None]),     # input register / analog input (adress=30000)
            hr=ModbusSequentialDataBlock(address=temperature_slave["holding_registers"], values=[None]),     # hold. register / analog outp. (adress=40000)
            zero_mode=True)
    } 
    context = ModbusServerContext(slaves=store, single=False)

    # ----------------------------------------------------------------------- # 
    # initialize the server information
    # ----------------------------------------------------------------------- # 
    identity = ModbusDeviceIdentification()
    identity.VendorName = 'raspi2b'
    identity.ProductCode = 'r2b'
    identity.VendorUrl = 'http://hs-mannheim.de'
    identity.ProductName = 'Pymodbus Slave-Server'
    identity.ModelName = '2b'
    identity.MajorMinorRevision = '1.0.0'

    # ----------------------------------------------------------------------- # 
    # run the server you want
    # ----------------------------------------------------------------------- # 
    loop_temperature = LoopingCall(f=update_temperature, kw={"context":context, "slave_info": temperature_slave}) 
    loop_temperature.start(20, now=False) # initially delay by 1 sec

    loop_contact = LoopingCall(f=update_window_contact, kw={"context":context, "slave_info": contact_slave}) 
    loop_contact.start(20, now=False) # initially delay by 1 sec

    StartTcpServer(context, identity=identity, address=("localhost", 5020))

if __name__ == "__main__":
    run_updating_server()

它基本上只是更新温度值和 window 传感器的 on/off-values。 现在,当我尝试通过 modbus master 获取数据时,我再次从 pymodbus synchronous client example 改编而来,我收到一条错误消息:

'[Input/Output] Modbus Error: [Invalid Message] Incomplete message received, expected at least 8 bytes (0 received)' from the assert-statement in line 48 and 52

这是我的客户端实现:

#!/usr/bin/env python
# --------------------------------------------------------------------------- #
# import the various server implementations
# --------------------------------------------------------------------------- #
from pymodbus.client.sync import ModbusTcpClient as ModbusClient
from influxdb import InfluxDBClient
# --------------------------------------------------------------------------- #
# configure the client logging
# --------------------------------------------------------------------------- #
import logging, json, time
FORMAT = ('%(asctime)-15s %(threadName)-15s '
          '%(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.INFO)


def run_sync_client():
    # ------------------------------------------------------------------------#
    # choose the client you want
    # ------------------------------------------------------------------------#
    client = ModbusClient("localhost", port=5020) #'192.168.0.10'
    client.connect()

    # ------------------------------------------------------------------------#
    # specify slave to query
    # ------------------------------------------------------------------------#
    temperature_slave = { # temperature sensor
        "id": 0x01, 
        "coils": 0x0000,
        "inputs": 0x2710,
        "input_registers": 0x7530,
        "holding_registers": 0x9C40
    }
    contact_slave = { # window sensor
        "id": 0x02,
        "coils": 0x0000,
        "inputs": 0x2710,
        "input_registers": 0x7530,
        "holding_registers": 0x9C41
    } 

    # ----------------------------------------------------------------------- #
    # example requests
    # ----------------------------------------------------------------------- #
    log.debug("Read from a Coil")
    co_data = client.read_coils(address=contact_slave["coils"], count=1, SLAVE=contact_slave["id"])
    assert(not co_data.isError())     # test that we are not an error

    log.debug("Read holding registers")
    hr_data = client.read_holding_registers(address=temperature_slave["holding_registers"], count=1, SLAVE=temperature_slave["id"])
    assert(not hr_data.isError())     # test that we are not an error

    # ----------------------------------------------------------------------- #
    # close the client
    # ----------------------------------------------------------------------- #
    client.close()

if __name__ == "__main__":
    run_sync_client()

我不认为我完全理解了 modbus 协议,所以我很难弄清楚为什么会出现这个错误。如果有人能帮助我更好地理解它并理想地解决这个问题,我会非常高兴。

在阅读 register/coil 时使用 unit 关键字参数而不是 SLAVE 解决了这个问题:

所以而不是

co_data = client.read_coils(address=contact_slave["coils"], count=1, SLAVE=contact_slave["id"])
assert(not co_data.isError())

log.debug("Read holding registers")
hr_data = client.read_holding_registers(address=temperature_slave["holding_registers"], count=1, SLAVE=temperature_slave["id"])

co_data = client.read_coils(address=contact_slave["coils"], count=1, UNIT=contact_slave["id"])
assert(not co_data.isError())

log.debug("Read holding registers")
hr_data = client.read_holding_registers(address=temperature_slave["holding_registers"], count=1, UNIT=temperature_slave["id"])