MODBUS for Luci-Statistics

for those who might be interested here is MODBUS for Luci-Statistic
it supports both TCP and RTU MODBUS connection types

nano /usr/bin/stat-genconfig



function config_modbus( c )
 local str = ""


 for s in pairs(sections) do



     for key, type in pairs({ Mdata="collectd_modbus_data", Mhost="collectd_modbus_host" }) do
             if sections[s][".type"] == type then

                     dname = sections[s].dname
                    RegisterType = sections[s].RegisterType
                    RegisterBase  = sections[s].RegisterBase
                     RegisterCmd = sections[s].RegisterCmd
                     Type = sections[s].Type
                     Instance = sections[s].Instance
                     Baudrate = sections[s].Baudrate
                     Device = sections[s].Device
                     Address = sections[s].Address
                     Port = sections[s].Port
                      Interval = sections[s].Interval
                     slname = sections[s].slname
                     hname = sections[s].hname
                     Instance2 = sections[s].Instance2
                     Collect = sections[s].Collect
                    Collect2 = sections[s].Collect2
                    Collect3 = sections[s].Collect3
                    Collect4 = sections[s].Collect4
   --                 Collect5 = sections[s].Collect5
        --             Collect6 = sections[s].Collect6
        --             Collect7 = sections[s].Collect7
          --           Collect8 = sections[s].Collect8
            --         Collect9 = sections[s].Collect9
              --       Collect10 = sections[s].Collect10

                    if dname then
                           if RegisterCmd  then
                                     str = str .. "\t" .. "<Data " .."\"".. dname .."\"".." >" .. " \n"
                                    str = str .. "\t" .. "RegisterBase" .. " " .. RegisterBase .. "\n"
                                     str = str .. "\t" .. "RegisterCmd" .. " " .. RegisterCmd .. "\n"
                                    str = str .. "\t" .. "RegisterType" .. " " .. RegisterType .. "\n"
                                    str = str .. "\t" .. "Type" .. " " .. Type .. "\n"
                                    str = str .. "\t" .. "Instance" .. " \"" .. Instance .. "\"\n"
                                    str = str .. "\t" .. "</Data>" .. " \n"
--
--
                            else
                                    str = str .. "\t" .. "<Data " .."\"".. dname .."\"".." >" .. " \n"
                                    str = str .. "\t" .. "RegisterBase" .. " " .. RegisterBase .. "\n"
                                    str = str .. "\t" .. "RegisterType" .. " " .. RegisterType .. "\n"
                                     str = str .. "\t" .. "Type" .. " " .. Type .. "\n"
                                    str = str .. "\t" .. "Instance" .. " \"" .. Instance .. "\"\n"
                                   str = str .. "\t" .. "</Data>" .. " \n"
--
end
                          end

if hname then
                          --      else
                                    str = str .. "\t" .. "<Host " .."\"".. hname .."\"".." >" .. " \n"
if Device then
                                   str = str .. "\t" .. "Device" .. " \"" .. Device .. "\"\n"
end
if Address then
                                    str = str .. "\t" .. "Address" .. " \"" .. Address .. "\"\n"
end
if Baudrate then
                                    str = str .. "\t" .. "Baudrate" .. " " .. Baudrate .. "\n"
end
if Port then                                   
                                    str = str .. "\t" .. "Port" .. " " .. Port .. "\n"
end                                  
                                    str = str .. "\t" .. "Interval" .. " " .. Interval .. "\n"
                                    str = str .. "\t" .. "<Slave " .."".. slname .."".." >" .. " \n"
if Instance2 then
                                    str = str .. "\t" .. "Instance" .. " \"" .. Instance2 .. "\"\n"
end                 
                                    str = str .. "\t" .. "Collect" .. " \"" .. Collect .. "\"\n"
if Collect2 then
                                    str = str .. "\t" .. "Collect" .. " \"" .. Collect2 .. "\"\n"
end
if Collect3 then
                                    str = str .. "\t" .. "Collect" .. " \"" .. Collect3 .. "\"\n"
end 
if Collect4 then
                                    str = str .. "\t" .. "Collect" .. " \"" .. Collect4 .. "\"\n"
end
-- if Collect5 then
  --                                 str = str .. "\t" .. "Collect" .. " \"" .. Collect5 .. "\"\n"
--  end
 --if Collect6 then                                 
   --                                str = str .. "\t" .. "Collect" .. " \"" .. Collect6 .. "\"\n"
-- end
 --if Collect7 then                                  
   --                                str = str .. "\t" .. "Collect" .. " \"" .. Collect7 .. "\"\n"
--end
 --if Collect8 then                                  
   ---                                str = str .. "\t" .. "Collect" .. " \"" .. Collect8 .. "\"\n"
 --end
 --if Collect9 then                                  
   --                                str = str .. "\t" .. "Collect" .. " \"" .. Collect9 .. "\"\n"
 --end
 --if Collect10 then                                  
 --                                  str = str .. "\t" .. "Collect" .. " \"" .. Collect10 .. "\"\n"
  --end                                  
                                   str = str .. "\t" .. "</Slave>" .. " \n"
                                     str = str .. "\t" .. "</Host>" .. " \n"

                             end
                  
                 end

                     end
     end
 return str

end



and add this to plugin section

  modbus = config_modbus,

then -- nano /usr/lib/lua/luci/model/cbi/luci_statistics/modbus.lua

-- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the mhostlic under the Apache License 2.0.

m = Map("luci_statistics",
        translate("Modbus Data"),
        translate(
                "MODBUS DATA" 
                
        ))



-- collectd_modbus config section
s = m:section( NamedSection, "collectd_modbus", "luci_statistics" )

-- collectd_modbus_data.enable
enable = s:option( Flag, "enable", translate("Enable this plugin") )
enable.default = 0


-- collectd_modbus_data config section (Mdata)
mdata = m:section( TypedSection, "collectd_modbus_data",
        translate("Modbus Data"),
        translate(
                "This section defines the Modbus Data " 
               
        ))
mdata.addremove = true
mdata.anonymous = true


-- collectd_modbus_data.dname
mdata_name = mdata:option( Value, "dname", translate("Modbus Data Name"))
mdata_name.default = "Data1"


-- collectd_modbus_data.RegisterBase
mdata_RegisterBase = mdata:option( Value, "RegisterBase", translate("RegisterBase"))
mdata_RegisterBase.default = "1234"


-- collectd_modbus_data.RegisterCmd
mdata_RegisterCmd = mdata:option( Value, "RegisterCmd", translate("RegisterCmd") )
---mdata_port.isinteger = true
mdata_RegisterCmd.default   = ""
mdata_RegisterCmd.optional  = true


-- collectd_modbus_data.RegisterType
mdata_RegisterType = mdata:option( Value, "RegisterType", translate("RegisterType") )
mdata_RegisterType.isinteger = true
mdata_RegisterType.default   = "float"


-- collectd_modbus_data.Type
mdata_Type = mdata:option( Value, "Type", translate("Type") )
--mdata_Type.isinteger = true
mdata_Type.default   = "voltage"


-- collectd_modbus_data.Instance
mdata_Instance = mdata:option( Value, "Instance", translate("Instance") )
--mdata_Instance.isinteger = true
mdata_Instance.default   = "..."


-- collectd_modbus_host config section (Mhost)
mhost = m:section( TypedSection, "collectd_modbus_host",
        translate("Modbus Hosts"),
        translate(
                "This section defines to Modbus Hosts "..
                " sellect Address/Port or Device/Baudrate" 
                
        ))
mhost.addremove = true
mhost.anonymous = true

-- collectd_modbus_host.hname
mhost_name = mhost:option( Value, "hname", translate("Modbus Host Name"))
mhost_name.default = "Name"


-- collectd_modbus_host.Address
mhost_Address = mhost:option( Value, "Address", translate("TCP - Host Address") )
mhost_Address.default = ""
mhost_Address.optional  = true

-- collectd_modbus_host.Port
mhost_Port = mhost:option( Value, "Port", translate("Port") )
mhost_Port.default   = ""
mhost_Port.isinteger = true
mhost_Port.optional  = true

-- collectd_modbus_host.Device
mhost_Device = mhost:option( Value, "Device", translate("RTU DEVICE - /dev/ttyUSB0") )
mhost_Device.default = ""
mhost_Device.optional  = true

-- collectd_modbus_host.Baudrate
mhost_Baudrate = mhost:option( Value, "Baudrate", translate("Baudrate") )
mhost_Baudrate.default   = ""
mhost_Baudrate.isinteger = true
mhost_Baudrate.optional  = true

-- collectd_modbus_host.Interval
mhost_Interval = mhost:option( Value, "Interval", translate("Interval") )
mhost_Interval.default = "60"
mhost_Interval.isinteger = true


mhost_name = mhost:option( Value, "slname", translate("Modbus Slave Number"))
mhost_name.default = "1"

-- collectd_modbus_slave.Instance
mhost_Instance2 = mhost:option( Value, "Instance2", translate("Instance") )
mhost_Instance2.default = ""
mhost_Instance2.isinteger = true
mhost_Instance2.optional  = true


-- collectd_modbus_slave.Collect
mhost_Collect = mhost:option( Value, "Collect", translate("Data Name") )
mhost_Collect.default = "data_name"

-- collectd_modbus_slave.Collect2
mhost_Collect2 = mhost:option( Value, "Collect2", translate("Data Name2") )
mhost_Collect2.default = ""
mhost_Collect2.isinteger = true
mhost_Collect2.optional  = true

-- collectd_modbus_slave.Collect3
mhost_Collect3 = mhost:option( Value, "Collect3", translate("Data Name3") )
mhost_Collect3.default = ""
mhost_Collect3.isinteger = true
mhost_Collect3.optional  = true

-- collectd_modbus_slave.Collect4
mhost_Collect4 = mhost:option( Value, "Collect4", translate("Data Name4") )
mhost_Collect4.default = ""
mhost_Collect4.isinteger = true
mhost_Collect4.optional  = true

-- collectd_modbus_slave.Collect5
--mhost_Collect5 = mhost:option( Value, "Collect5", translate("Data Name5") )
--mhost_Collect5.default = ""
--mhost_Collect5.isinteger = true
--mhost_Collect5.optional  = true

-- collectd_modbus_slave.Collect6
--mhost_Collect6 = mhost:option( Value, "Collect6", translate("Data Name6") )
--mhost_Collect6.default = ""
--mhost_Collect6.isinteger = true
--mhost_Collect6.optional  = true

-- collectd_modbus_slave.Collect7
--mhost_Collect7 = mhost:option( Value, "Collect7", translate("Data Name7") )
--mhost_Collect7.default = ""
--mhost_Collect7.isinteger = true
--mhost_Collect7.optional  = true

-- collectd_modbus_slave.Collect8
--mhost_Collect8 = mhost:option( Value, "Collect8", translate("Data Name8") )
--mhost_Collect8.default = ""
--mhost_Collect8.isinteger = true
--mhost_Collect8.optional  = true

-- collectd_modbus_slave.Collect9
--mhost_Collect9 = mhost:option( Value, "Collect9", translate("Data Name9") )
--mhost_Collect9.default = ""
--mhost_Collect9.isinteger = true
--mhost_Collect9.optional  = true

-- collectd_modbus_slave.Collect10
--mhost_Collect10 = mhost:option( Value, "Collect10", translate("Data Name 10") )
--mhost_Collect10.default = ""
--mhost_Collect10.isinteger = true
--mhost_Collect10.optional  = true

return m

then add these to nano /usr/lib/lua/luci/controller/luci_statistics/luci_statistics.lua

modbus            = _("MODBUS"),

add to network section

   network = { "conntrack", "dns", "interface", "iptables", "modbus",
                    "netlink", "olsrd", "openvpn", "ping",
                    "splash_leases", "tcpconns", "iwinfo" }

lastly edit nano /etc/config/luci_statistics

 config statistics 'collectd_modbus'
   option enable '1'

the Luci interface looks like this

Screenshot_20181230_111649

you have to create your own rrd deffinitions as I have not done that at the moment

here an update to modbus to keep collectd in proper order - first set up hosts first save and then add metric afterwards it will maintain a proper sequence..

--#########################################--


function config_modbus( c )
 local str = ""


 for s in pairs(sections) do



     for key, type in pairs({ Mdata="collectd_modbus_data" }) do
             if sections[s][".type"] == type then

                     dname = sections[s].dname
                     RegisterType = sections[s].RegisterType
                     RegisterBase  = sections[s].RegisterBase
                     RegisterCmd = sections[s].RegisterCmd
                     Type = sections[s].Type
                     Instance = sections[s].Instance
                    

                    if dname then
                           if RegisterCmd  then
                                    str = str .. "\t" .. "<Data " .."\"".. dname .."\"".." >" .. " \n"
                                    str = str .. "\t" .. "RegisterBase" .. " " .. RegisterBase .. "\n"
                                    str = str .. "\t" .. "RegisterCmd" .. " " .. RegisterCmd .. "\n"
                                    str = str .. "\t" .. "RegisterType" .. " " .. RegisterType .. "\n"
                                    str = str .. "\t" .. "Type" .. " " .. Type .. "\n"
                                    str = str .. "\t" .. "Instance" .. " \"" .. Instance .. "\"\n"
                                    str = str .. "\t" .. "</Data>" .. " \n"
--
--
                            else
                                    str = str .. "\t" .. "<Data " .."\"".. dname .."\"".." >" .. " \n"
                                    str = str .. "\t" .. "RegisterBase" .. " " .. RegisterBase .. "\n"
                                    str = str .. "\t" .. "RegisterType" .. " " .. RegisterType .. "\n"
                                    str = str .. "\t" .. "Type" .. " " .. Type .. "\n"
                                    str = str .. "\t" .. "Instance" .. " \"" .. Instance .. "\"\n"
                                    str = str .. "\t" .. "</Data>" .. " \n"
--
end
                          end
end
end
     for key, type in pairs({ Mhost="collectd_modbus_host" }) do
             if sections[s][".type"] == type then

                     Baudrate = sections[s].Baudrate
                     Device = sections[s].Device
                     Address = sections[s].Address
                     Port = sections[s].Port
                     Interval = sections[s].Interval
                     slname = sections[s].slname
                     hname = sections[s].hname
                     Instance2 = sections[s].Instance2
                     Collect = sections[s].Collect
                     Collect2 = sections[s].Collect2
                     Collect3 = sections[s].Collect3
                     Collect4 = sections[s].Collect4


if hname then
                    
                                    str = str .. "\t" .. "<Host " .."\"".. hname .."\"".." >" .. " \n"
if Device then
                                   str = str .. "\t" .. "Device" .. " \"" .. Device .. "\"\n"
end
if Address then
                                    str = str .. "\t" .. "Address" .. " \"" .. Address .. "\"\n"
end
if Baudrate then
                                    str = str .. "\t" .. "Baudrate" .. " " .. Baudrate .. "\n"
end
if Port then                                   
                                    str = str .. "\t" .. "Port" .. " " .. Port .. "\n"
end                                  
                                    str = str .. "\t" .. "Interval" .. " " .. Interval .. "\n"
                                    str = str .. "\t" .. "<Slave " .."".. slname .."".." >" .. " \n"
if Instance2 then
                                    str = str .. "\t" .. "Instance" .. " \"" .. Instance2 .. "\"\n"
end                 
                                    str = str .. "\t" .. "Collect" .. " \"" .. Collect .. "\"\n"
if Collect2 then
                                    str = str .. "\t" .. "Collect" .. " \"" .. Collect2 .. "\"\n"
end
if Collect3 then
                                    str = str .. "\t" .. "Collect" .. " \"" .. Collect3 .. "\"\n"
end 
if Collect4 then
                                    str = str .. "\t" .. "Collect" .. " \"" .. Collect4 .. "\"\n"
end
                                 
                                   str = str .. "\t" .. "</Slave>" .. " \n"
                                     str = str .. "\t" .. "</Host>" .. " \n"

                             end
                        end
                    end                 
                 end

 return str

end



--########################################--

This is on which version OpenWRT and which modules are needed?

I suffer for a month and I can't get a schedule for Modbus TCP in any way

and I can't figure out where to enter: modbus = config_modbus,

the last version I installed was OpenWrt 18.06.1 I currently have not tried on anything newer- but I would think it works still

nano /usr/bin/stat-genconfig

then scroll down on that file you will see a section that starts

plugins = {

just paste

modbus = config_modbus,

I pasted mine just above netlink = { and after the close bracket for memory },

if you did everything correctly from above and installed collectd-mod-modbus, libmodbus
you should get a a window in luci-statistics that looks like this

then you can configure for your modbus setting

I'm trying to implement on OpenWrt 19.07.1
there are small differences in the configuration files
in this case, "modbus = config_modbus," insert will not work, there is no section plugins = {, and even without this menu to appear.

however, the interface will not start drawing graphics, so tell me how to create rrd deffinitions. I think this is the case and the fact that the database has not been created.

I'm a bit weak on linux, so I'm asking for help :slight_smile:

to see if it is working go to your RRD directory and see if an modbus.rrd was created - /tmp/rrd/ "hostname" -- but it needs input so set it up and see what happens - you can also see if it setup collectd config file nano /etc/collectd.conf and see if the settings are there.. if not then see if they are being written to /etc/config/luci_statistics and if they are all written correctly and still no rrd file just run collectd manually and see what error it is throwing up for you

to draw graphs you will need to create a definition file

/usr/lib/lua/luci/statistics/rrdtool/definitions/

see my mqtt or snmp example on that


lately I have not used RRD graphing but instead got statistic to upload data to influxdb and used chronogaf or grafana to draw my graphs so I am currently a little weak on knowledge to tell you how to do it exactly to create the definition file

Please, help. Can I use this plugin for transmit some data to another plugin? I think the luci-statistic will collect data and another plugin will use it

So, did you get the Modbus schedule?

I general now just use the collectD plugins to collect the data and then use the network plugin to send it to a influxd database. and in my case since I am using ARM or intel based routers I have influxd installed right on the router itself - as these routers are generally 1-8 gigs of memory, 2-4 core at +1.8 ghz and 100gig or more storage .. if you want the router and luci to do i you have to create the definition as mentioned above

Oh, thanks. I installed the collectd according to your example, but I have not get the requests to modbus tcp server (my PC). I cannot understand my mistake? May be it have to configure the Firewall? May be the firewall block my requests?