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.