Modbus RS485 communication issues

Hi,

I’m using the LX60 gateway in order to monitor and control devices through its RS485 serial interface using Modbus RTU protocol. Right now, I 've just written a simple LUA application to test the Modbus functionnalities.

I’ve connected the gateway with a Modbus RTU slave device which manages relay output. I’m able to send Modbus requests in order to activate the relay output, so my Modbus configuration and test code seem to be OK. When I check the RS485 electrical signals everything is OK and if look on the device response I can see this one is a recopy of the request. This confirm that the request is correct and no error has been encountered on device side.

However, the response is badly interpreted or not seen by the AAF. After executing the following Modbus request, I’ve got an error:
– write to Coil register in order to activate the relay output
local write,error = sdevice:writeSingleCoil(sid,address,sdata)
print (‘Modbus write done’, write, error)

Result : write =‘nil’ , error=‘timeout’
So the device response has not been well received.

In the LX60 log file, I can catch the following message : warning ALEOS_APPS_AAF-APPS: <MODBUS> Failure while processing a request: timeout

Same behaviour occurs when trying to read a Mobus regsiter.

I would like to know how I can have more information about this issue and how to debug it. As the WriteSingleCoil is an API function, I’ve no visibilty on what happens inside it. May be I’ve just forget to initiatlize something in my test program or on the LX60 itself.

Can someone has any idea I could try, or any feedback using RS485 on Airlink gateways ?

Thanks

Best regards

Fred

Below is the test code used on LX60:

local devicetree = require ‘devicetree’
local system = require ‘system’
local sched = require “sched” – Lua scheduling and synchronization lib
local serial = require “serial” – Serial lib
local os = require “os” – Operating system lib
local modbus = require “modbus”


– Variable path to be used
local RESERVE_SER0 = “system.aleos.reserve.ser0”


– Check if the serial port has been reserved for our use
– if not, reserve it and reboot to gain control
function reserve_serial_port (asset)

local serial_available = assert (devicetree.get(RESERVE_SER0))
if serial_available ~= 1 then
    print ("WARNING","serial port not available, reserving it now...")
    local result = assert (devicetree.set(RESERVE_SER0, 1))
    print ("WARNING","Serial port Reserved ok, rebooting now...")
    system.reboot()
    sched.wait(30)  -- stop here until the reset occurs
end
print ("Serial Port has been reserved for our use.")

end


function open_modbus_port (asset)

local modbus_port = '/dev/ttymxc1'
local modbus_mode ='RTU'

  local modbus_config = 
  {
  baudRate = 19200, 
  flowControl = 'none',
  numDataBits = 8,
  parity = 'even',
  numStopBits = 2,
  timeout = 3
  }

local modbusdev = assert (modbus.new(modbus_port, modbus_config, modbus_mode))
print ("Init done",modbusdev)
return modbusdev

end


– Write to the modbus port
function write_to_modbus_port (sdevice, sid, address, sdata)
local write,error = sdevice:writeSingleCoil(sid,address,sdata)
print (‘Modbus write done’, write, error)
end


– Read to the modbus port
function read_from_modbus_port (sdevice,sid,address,length)
local read_data,error = sdevice:readCoils(sid, address,length)
print (“Modbus read done”,read_data,error)
return read_data
end


function main ()
assert(devicetree.init())
assert(system.init())
sched.wait(1)

reserve_serial_port ()
sched.wait(1)

local modbusmaster = open_modbus_port()

sched.wait(1)
write_to_modbus_port (modbusmaster, 130,1, true)
sched.wait(3)
write_to_modbus_port (modbusmaster, 130,1, false)   

sched.wait(1)
read_from_modbus_port (modbusmaster,130,1,1)  

print ('Exiting...')
--serdev:close()    -- close the serial instance
modbusmaster:close()    -- close the serial instance   
os.exit (0)

end


– Schedule the main function, and launch the scheduler main loop
sched.run(main)
sched.loop()

hi,

posting answer for record.
In order to have the traffic accessible to the AAF application, do disable following parameter in AceManager: Serial>RS485Configuration>RS485>RS485 Port

If this parameter is set to enable, traffic will be prehempter by Aleos (the operating sytem) and not available for the AAF application.

So, I was just looking into using Modbus also, we currently use the RV50. I take it I would either have to wire up an RS485 to Serial adapter or find a gateway that has an RS485 port built in? I see the RTU modbus library but am not sure if the RV50 would handle the DTR toggle for transmit / receive direction if we use an external adapter. I will have to look into the LX60.

Hi fboylot,
When you write a value to register , please make sure “sid” of slave device is correct.
I also modify your function read_from_modbus_port (sdevice,sid,address,length), please try the one below:

function read_from_modbus_port (sdevice,sid,address,length)
local read_data,error = sdevice:readCoils(sid, address,length)
if read_data==nil then
print (“sid is not correct”)
else
local _, bool = string.unpack(read_data,“x8”)
print (“Modbus read single coil”,bool,error)
return read_data
end
end

Thanks,