MODBUS over Ethernet

MODBUS is supported in all DLI power controllers and DIN relays with WiFi. It is not currently available in PLCs.
Unit ID:1
The relays are mapped to coils (1..8)
Standard MODBUS commands can be used to manipulate them, ie:
READ_COILS = 0x01
WRITE_SINGLE_COIL=0x05
WRITE_MULTIPLE_COILS = 0x0F
Use scripting and the REST API to configure custom registers to do whatever you please.
Here's a sample script snippet: (from default.modbus_demo)
function flip_all_modbus_discrete_inputs()
for i=1,#modbus.discrete_inputs do
modbus.discrete_inputs[i].value=not modbus.discrete_inputs[i].value
end
end
function allow_custom_meter_to_be_set_from_modbus()
-- You're expected to create a custom meter and specify its ID here
local custom_value=meter.values["custom_meter_id"]
if custom_value then
-- You're expected to create the registers manually and assign
-- sequential addresses to them and specify them here
local rlow,rhigh=modbus.holding_registers[1],modbus.holding_registers[2]
assert(rlow and rhigh)
custom_value.value=util.float32be.decode(util.uint16be.encode(rhigh.value)..util.uint16be.encode(rhigh.value))
for i,t,data in event.stream(event.change_listener(rhigh)) do
if data.key=="value" then
custom_value.value=util.float32be.decode(util.uint16be.encode(rhigh.value)..util.uint16be.encode(rhigh.value))
end
end
else
log.info("Please edit the script to specify a custom meter meter")
end
end
--Expose temperature to Modbus using 2 registers
-- to handle the floating point
function expose_temperature_to_modbus()
local temperature=meter.values["sensors.0.temperature"] or meter.values["environment.temperature"]
if temperature then
-- You're expected to create the registers manually, assign
-- sequential addresses to them and specify them here (they may be
-- input or holding registers).
local rlow,rhigh=modbus.input_registers[1],modbus.input_registers[2]
assert(rlow and rhigh)
local encoded_value=util.float32be.encode(temperature.value)
rlow.value=util.uint16be.decode(encoded_value:sub(3,4))
rhigh.value=util.uint16be.decode(encoded_value:sub(1,2))
for i,t,data in event.stream(event.change_listener(temperature)) do
if data.key=="value" then
encoded_value=util.float32be.encode(temperature.value)
rlow.value=util.uint16be.decode(encoded_value:sub(3,4))
rhigh.value=util.uint16be.decode(encoded_value:sub(1,2))
end
end
else
log.info("Please edit the script to choose an existing meter")
end
end
Other examples of exposing the ADC to Modbus registers and another temperature example.
-- Expose 10V ADC to Modbus
-- This is the actual voltage multiplied by 10
-- Divide by 10 to get the actual voltage to 1 decimal point
function expose_ACD_10_to_modbus()
local voltage0=meter.values["voltage.0"]
local register = 2 -- The input_register to use
if voltage0 then
local volt= voltage0.value -- enable the monitor
-- You're expected to create the registers manually, assign
-- sequential addresses to them and specify them here (they may be
-- input or holding registers).
for i,t,data in event.stream(event.change_listener(voltage0)) do
if data.key=="value" then
modbus.input_registers[register].value = math.floor(voltage0.value*10 + 0.5)
end
end
else
log.info("Please edit the script to choose an existing meter")
end
end
-- Expose 100V ADC to Modbus
-- This is the actual voltage multiplied by 10
-- Divide by 10 to get the actual voltage to 1 decimal point
function expose_ACD_100_to_modbus()
local voltage1=meter.values["voltage.1"]
local register = 3 -- The input_register to use
if voltage1 then
local volt= voltage1.value -- enable the monitor
-- You're expected to create the registers manually, assign
-- sequential addresses to them and specify them here (they may be
-- input or holding registers).
for i,t,data in event.stream(event.change_listener(voltage1)) do
if data.key=="value" then
modbus.input_registers[register].value = math.floor(voltage1.value*10 + 0.5)
end
end
else
log.info("Please edit the script to choose an existing meter")
end
end
-- Convert from Kelvin to Fahrenheit
function kelvin_to_fahrenheit(t)
if not tonumber(t) then
log.err("Invalid temperature")
return 0
end
return 9 / 5 * (t - 273.16) + 32
end
-- Temperature is Fahrenheit multiplied by 10.
--Divide by 10 to get the temperature to one decimal place
function expose_temperature_fahrenheit_to_modbus()
local temperature=meter.values["sensors.0.temperature"] -- or meter.values["environment.temperature"]
local register = 1 -- The input_register to use
if temperature then
-- You're expected to create the registers manually, assign
-- sequential addresses to them and specify them here (they may be
-- input or holding registers).
local temp = temperature.value
for i,t,data in event.stream(event.change_listener(temperature)) do
if data.key=="value" then
modbus.input_registers[1].value = math.floor(kelvin_to_fahrenheit(temperature.value) *10)
end
end
else
log.info("Please edit the script to choose an existing meter")
end
end
Here is an example which adds eight input_registers via SSH (all one line)
uom set modbus/input_registers '[{"allow_read":true,"index":0,"name":"IR1","value":0},{"allow_read":true,"index":1,"name":"IR2","value":0},{"allow_read":true,"index":2,"name":"IR3","value":0},{"allow_read":true,"index":3,"name":"IR4","value":0},{"allow_read":true,"index":4,"name":"IR5","value":0},{"allow_read":true,"index":5,"name":"IR6","value":0},{"allow_read":true,"index":6,"name":"IR7","value":0},{"allow_read":true,"index":7,"name":"IR8","value":0}]'
If we haven't answered your questions here, please call (408) 330-5599 or send us an email. We'll be glad to help.
© Digital Loggers, Inc. 2005-2020.
