Beginner question about updating registers

1,035 views
Skip to first unread message

jonty oxford

unread,
Sep 22, 2017, 7:15:01 PM9/22/17
to pymodbus
Hello,
I am trying to set up a simple TCP server with a self updating temperature measurement. At the moment I am taking the processor temp reading but will swap for an external temperature sensor once the hardware arrives. 
I plugged this into the updating server example, and the server starts without error, and debug gives periodic updated values (see below) but when I send a request using:

client.read_holding_registers(0,10,unit=0)

from another device, the register hasn't been updated:

ReadRegisterResponse (10)
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

I assume I am doing something very stupid but would be greatful for any pointers.
See code below.

Debug of running server:

INFO:pymodbus.server.async:Starting Modbus TCP Server on 192.168.1.87:5020
DEBUG:root:updating the context
DEBUG:root:new values: 33.6
DEBUG:pymodbus.datastore.context:setValues[1] 17:4
DEBUG:root:updating the context
DEBUG:root:new values: 32.6
DEBUG:pymodbus.datastore.context:setValues[1] 17:4
DEBUG:root:updating the context
DEBUG:root:new values: 32.6

server code:
#!/usr/bin/env python
'''
Pymodbus Server With Updating Thread
--------------------------------------------------------------------------

This is an example of having a background thread updating the
context while the server is operating. This can also be done with
a python thread::

    from threading import Thread

    thread = Thread(target=updating_writer, args=(context,))
    thread.start()
'''
#---------------------------------------------------------------------------#
# import the modbus libraries we need
#---------------------------------------------------------------------------#
import os
from pymodbus.server.async 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
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)

#---------------------------------------------------------------------------#
# define your callback process
#---------------------------------------------------------------------------#
def updating_writer(a):
    ''' A worker process that runs every so often and
    updates live values of the context. It should be noted
    that there is a race condition for the update.

    :param arguments: The input arguments to the call
    '''
    log.debug("updating the context")
    context  = a[0]
    register = 1
    slave_id = 0x00
    address  = 0x10
    CPUtemp  = os.popen('vcgencmd measure_temp').readline()
    values   = (CPUtemp.replace("temp=","").replace("'C\n",""))
    log.debug("new values: " + str(values))
    context[slave_id].setValues(register, address, values)

#---------------------------------------------------------------------------#
# initialize your data store
#---------------------------------------------------------------------------#
store = ModbusSlaveContext(
    di = ModbusSequentialDataBlock(0, [0]*100),
    co = ModbusSequentialDataBlock(0, [0]*100),
    hr = ModbusSequentialDataBlock(0, [0]*100),
    ir = ModbusSequentialDataBlock(0, [0]*100))
context = ModbusServerContext(slaves=store, single=True)

#---------------------------------------------------------------------------#
# initialize the server information
#---------------------------------------------------------------------------#
identity = ModbusDeviceIdentification()
identity.VendorName  = 'pymodbus'
identity.ProductCode = 'PM'
identity.VendorUrl   = 'http://github.com/bashwork/pymodbus/'
identity.ProductName = 'pymodbus Server'
identity.ModelName   = 'pymodbus Server'
identity.MajorMinorRevision = '1.0'

#---------------------------------------------------------------------------#
# run the server you want
#---------------------------------------------------------------------------#
time = 5 # 5 seconds delay
loop = LoopingCall(f=updating_writer, a=(context,))
loop.start(time, now=False) # initially delay by time
StartTcpServer(context, identity=identity, address=("192.168.1.87", 5020))

zuababa

unread,
Nov 23, 2017, 4:00:08 PM11/23/17
to pymodbus
Did you solve the issue? I was trying this now out and what I wonder is how the setValues function knows this should be stored to the holding registers. Maybe it stores it somewhere else? And you are reading just the 0s which are initialized in the context at the beginning.
Then I saw that the arguments are:
setValues(fx, address, values)
Your example has fx=1 which is for coils. I think fx=3 is for holding registers.
Does that help?

zuababa

unread,
Nov 24, 2017, 4:52:06 AM11/24/17
to pymodbus
First of all register should indeed be 3 for holding registers at the server.
    register = 1
Next, I noticed that in your code you have: 
    values   = (CPUtemp.replace("temp=","").replace("'C\n",""))
So, values is a string. Later when attempting to read that one with the client I used:
    client.read_holding_registers(0x10, 1, unit=0)
It was giving a 'ModbusIOException'. On the server side I saw that a struct function could not create an Integer from the obtained value. This meant that the value stored in the context was not right. I think the values _have to_ be an integer if you use the setValues() function. So I gave only the number via setValues and saw on the server side:
    _logger.debug("setValues[%d] %d:%d" % (fx, address, len(values)))
    exceptions.TypeError: object of type 'int' has no len()
So I changed the line 85 in datastore/context.py to 
    _logger.debug("setValues[%d] %d:%d" % (fx, address, len(str(values))))

now it works:)
Reply all
Reply to author
Forward
0 new messages