Modbus/TCP LUUP plugin

Hello all,

This thread is about my work on a LUUP plugin for controlling Modbus/TCP slaves. Right now I’m in the steep learning curve called Lua (“do more with less”) but I’ve successfully made a simple poll & respond script in Lua 5.1 using just these libraries:

  • LuaSocket (already included in current firmware)
  • VStruct [url=http://www.funkyhorror.net/toxicfrog/projects/vstruct]http://www.funkyhorror.net/toxicfrog/projects/vstruct[/url] (A library for packing and unpacking binary data)
  • ObjectLua (Object Oriented Programming for Lua, *why isn’t it already in the language ??? *)

Some first thoughts and questions for the community:

  1. Library dependencies: I know size is an issue in embedded systems, but what are the limitations? Do we have to count every byte used, both in terms of script size and runtime usage?
  2. Parallel tasking: the MIPS CPU inside this box seems quite powerful, but its power isn’t infinite. Since Modbus/TCP is a stateless protocol and you have to continuously poll the slaves, would it be better to write an external daemon to do all the heavy network I/O, or do we have the ability to run native threads inside the Luup engine?
  3. Licensing: Are there licensing issues we have to take into account? Do we have to use a licensing scheme compatible with Vera’s?

Awaiting instructions,
Elias

Elias,
Given what you’re looking to do, you’ll need the “raw” protocol driver. This is built into Vera, and is similar to the CRLF and CR Protocol handlers.

In this case though, you’ll get the incoming data “byte-by-byte” and you’ll need to buffer it to perform the processing and validations that Modbus/TCP requires.

Once you’ve worked out the pattern enough, writeup a short note covering the specific Modbus devices you’re trying to cover, and what they’ll extend Vera to cover, along with a short/technical description of the Modbus byte format (7byte header + data etc)… you might get the MCV team to add a value to more natively support it in order to avoid having the byte-by-byte processing.

Technically, you shouldn’t need LuaSocket. Instead you’d configure the IP/Port combo against your Luup Device in Vera and it will take care of the Socket connection. Instead you deal with the section in the Luup Plugin, along with the luup.io.write/read/intercept calls. Vera will handle maintaining the Socket connection.

See the I_GC100.xml for an example with low-level (ASCII, CRLF delimited) protocol semantics.

Size limits…
I haven’t run into any yet. My Luup Alarm plugin code is about 64k (17k compressed on Vera’s FS). Not sure what this equates to in terms of “runtime” memory footprint.

Parallel processing…
I’d assume you’d write one Luup Plugin Device per Modbus slave. Each would likely have some sort of Luup Timer in it to “poll” the slave at the required frequency… start off slow and work out how well that works. I’ve not had to poll yet, since the underlying protocols I’ve worked with are event based, but it sounds like Modbus doesn’t work that way.

Modbus is a very simple control protocol and its initial design goals are very different from what we are using it for. And, being poll-based it presents timing problems: if we have, say, 5 ADAM-6066 modules (30 digital inputs: wall switches alarm triggers, etc. and 30 relay outputs: lights, loads etc.), a typical scenario for a 200m2 residence) , we need to poll 5 different IPs for input changes at least 10 times a second.

This means that we have to have a controller process that does a constant 50 transactions/sec and that’s quite a burden for an embedded application like Luup. And if you need absolute responsiveness, you cannot depend on a single-threaded Lua app that does only cooperative multitasking. (anyone remember WfW?:slight_smile:

And there’s also the device discovery problem: Modbus doesn’t contain any device discovery commands, and you have to enter device info manually. I guess that we can create device classes and instantiate objects as needed.

So I’ll try to approach the problem from a different angle: a Lua Modbus-UPnP daemon which presents a UPnP interface (with events et al) using native pthreads. This daemon can even run on a different machine for performance (and stability) reasons.

I’m currently experimenting with luaproc (a concurrency addon for Lua) that uses native pthreads using nothing more than string messages for communication. The i386 .so library is just 27k so I think its worth the try.

Elias

Hello all,

Newbie progress report: I’ve got some multi-threaded (using luaproc) skeleton code in Lua that polls an ADAM-6066 at 20 times/sec and checks for changes. A simple state machine maps these changes to events (button clicked, button double-clicked etc.) and prints them out to stdout. Hopefully these can become UPnP events forwarded to Luup for further action. The only problem? being so newbie and unskilled I’ve managed to produce Lua code that keeps my Intel Core2 at 40% load which I guess is already too much for a Broadcom MIPS CPU.

I’ll take another approach today using plain C and see what happens…

Elias

Hello ebarak,
We’re glad to hear about your progress, you can try to compile your lua code this will speed it up a bit, else you can try to create and upnp compatible demon that will do the polling on another embedded box that runs linux, and connect it on the same network as Vera, then the communication will be made through upnp. You can check here: [url=http://oldwiki.openwrt.org/CompleteTableOfHardware.html]http://oldwiki.openwrt.org/CompleteTableOfHardware.html[/url] some linux supported routers.

This thread is listed in AP15e List of plugins…looks like it never was finished so I will add a little more to it in case someone is interested…

Here is lua code for reading an Adam 6017 eight channel ModBus Module:

local socket = require("socket") host = "192.168.81.176" c = assert(socket.connect(host, 502)) --modbus command "Read Registers" 8 registers starting at 0-- c:send(string.char(0,0,0,0,0,6,1,4,0,0,0,8)) --prefix returned plus 16 bytes , 8 16 bit words-- data = c:receive(25) Array = {} --convert from byte to word then convert to voltage based on a range of +-10VDC-- for x=1,8 do Array[x]=(data:byte(2*x+8)*256+data:byte(2*x+9)-32768)*10/32768 end luup.log (Array[1]) luup.log (Array[2]) luup.log (Array[3]) luup.log (Array[4]) luup.log (Array[5]) luup.log (Array[6]) luup.log (Array[7]) luup.log (Array[8]) c:close()

Just about every ModBus Module uses different register layouts, datatypes, etc. so it will take unique code for each one!
Regards
Tim Alls
AllSeas Yachts

could you explain the
“c:send(string.char(0,0,0,0,0,6,1,4,0,0,0,8))”
thanks
Gilles

What do you want explained: the Lua code or the Modbus Read Registers command?

If you want the format of the Modbus Command it is as follows:
Bytes 0,1,2,3,4,5 are the Command Head…all 0’s except byte 5 which is the length of the Command Body: (number of bytes =6)
Bytes 6, 7,8,9,10,11 are the Command Body… 6 bytes
6 = unit ID =1
7 =modbus command = 4… read registers
8 =high byte start address = 0…start reading at register 0 or 40001 in modbus terms
9 =low byte start address = 0
10 = request number of registers to read high byte = 0
11 = request number of registers to read low byte = 8 (read all 8 an inputs)
thus: 0,0,0,0,6,1,4,0,0,0,8
I hope that helps
Regards
Tim Alls
AllSeas Yachts

Hello ekarak

I realize this topic has been silent for some time, but I wanted to check where you ended up on this Project. I would be very interested in taking part of your results. I also have a bunch of Adam 60xx’s laying around that I thought I could potentially use. If you are now longer pursuing this, perhaps I could pick up where you left off?

Thanks

hi,
I see this thread has been idle for a while. it would be interesting to continue on this path. Did anybody else pick up from this thread?