Digital Loggers

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

 

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.