Digital Loggers

MODBUS over Ethernet

 

MODBUS is supported in all DLI power controllers and DIN relays with WiFi.


For more information look at the Help/External APIs inthe power controller.

 

 

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.

As of firmware 1.10.21.0, discrete inputs, input and holding registers can be created on the External API page to serve as a common interaction medium between Modbus/TCP and scripting.
See the built-in Help for more information.


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}]'

 

Back To Top

 

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.