├── LICENSE ├── Makefile ├── README.md ├── changelog ├── compat.h ├── config.ld ├── demo-rtu.lua ├── demo.lua ├── docs ├── examples │ └── demo.lua.html ├── index.html ├── ldoc.css └── source │ ├── demo.lua.html │ └── lua-libmodbus.c.html ├── lua-libmodbus.c ├── luarocks ├── lua-libmodbus-0.1-1.rockspec ├── lua-libmodbus-0.2-1.rockspec ├── lua-libmodbus-0.3-1.rockspec ├── lua-libmodbus-0.3.1-1.rockspec ├── lua-libmodbus-0.4-1.rockspec ├── lua-libmodbus-0.4.1-1.rockspec ├── lua-libmodbus-0.4.2-1.rockspec ├── lua-libmodbus-0.5-1.rockspec ├── lua-libmodbus-0.6-1.rockspec ├── lua-libmodbus-0.6.1-1.rockspec ├── lua-libmodbus-0.7-1.rockspec └── lua-libmodbus-0.8-1.rockspec └── spec ├── modbus_spec.lua └── number_helpers_spec.lua /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Karl Palsson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PKGC ?= pkg-config 2 | 3 | LUAPKG ?= lua lua5.1 lua5.2 lua5.3 4 | # lua's package config can be under various names 5 | LUAPKGC := $(shell for pc in $(LUAPKG); do \ 6 | $(PKGC) --exists $$pc && echo $$pc && break; \ 7 | done) 8 | 9 | LUA_VERSION := $(shell $(PKGC) --variable=V $(LUAPKGC)) 10 | LUA_LIBDIR := $(shell $(PKGC) --variable=libdir $(LUAPKGC)) 11 | LUA_CFLAGS := $(shell $(PKGC) --cflags $(LUAPKGC)) 12 | LUA_LDFLAGS := $(shell $(PKGC) --libs-only-L $(LUAPKGC)) 13 | 14 | CMOD = libmodbus.so 15 | OBJS = lua-libmodbus.o 16 | LIBS = -lmodbus 17 | CSTD = -std=c11 18 | 19 | OPT ?= -Os 20 | WARN = -Wall -pedantic 21 | CFLAGS += -g -fPIC $(CSTD) $(WARN) $(OPT) $(LUA_CFLAGS) $(EXTRA_CFLAGS) 22 | LDFLAGS += -shared $(CSTD) $(LIBS) $(LUA_LDFLAGS) $(EXTRA_LDFLAGS) 23 | 24 | ifeq ($(OPENWRT_BUILD),1) 25 | LUA_VERSION= 26 | endif 27 | 28 | all: $(CMOD) 29 | 30 | $(CMOD): $(OBJS) 31 | $(CC) $(LDFLAGS) $(OBJS) $(LIBS) -o $@ 32 | 33 | .c.o: 34 | $(CC) -c $(CFLAGS) -o $@ $< 35 | 36 | install: 37 | mkdir -p $(DESTDIR)$(LUA_LIBDIR)/lua/$(LUA_VERSION) 38 | cp $(CMOD) $(DESTDIR)$(LUA_LIBDIR)/lua/$(LUA_VERSION) 39 | 40 | clean: 41 | $(RM) $(CMOD) $(OBJS) 42 | 43 | test: 44 | busted --exclude-tags real 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | lua-libmodbus 2 | ============= 3 | 4 | Lua bindings to [libmodbus](http://www.libmodbus.org/) 5 | 6 | Parameters are mostly as per libmodbus documentation, but values are returned 7 | directly, in tables, rather than in pointers and return codes. Instead of 8 | a return code, you would get a lua style "nil, error_msg" return pair. 9 | 10 | Of particular relevance to modbus, is that tables are addressed with lua 11 | style 1 based counting, but converted to zero based internally. ie: 12 | 13 | ``` 14 | res = dev:read_registers(0x2000, 2) 15 | print(#res) -- prints 2 16 | print(res[1]) - prints register address 0x2000, _not_ 0x2001 17 | ``` 18 | 19 | [API documentation](http://etactica.github.io/lua-libmodbus/) (Generated from last release) 20 | 21 | Status 22 | ------ 23 | 24 | * Client bindings for RTU/TCP and almost all operations. 25 | * Some helpers for working with 16/32bit signed/unsigned and floats in multiple registers 26 | (API is not necessarily nailed down, comments welcome) 27 | * Server side limited to receive and reply exception. 28 | Needs thoughts on how to handle the mapping objects. 29 | * Compatible with both 3.0.x and 3.1.x but you must run with the version you compiled with. 30 | 31 | Compile 32 | ------- 33 | You need Lua and libmodbus development packages (headers and libs) to 34 | build lua-libmodbus. 35 | 36 | Compile with 37 | 38 | make 39 | 40 | You can override the pkg-config package name to set a specific Lua version. 41 | For example: 42 | 43 | make LUAPKG=lua-5.1 44 | 45 | Example usage 46 | ------------- 47 | 48 | See demo.lua and other sample files. 49 | 50 | Here is a simple example printing out 10 registers at 0x2000, 51 | then writing 5 registers back to that address 52 | 53 | ```Lua 54 | mb = require("libmodbus") 55 | local dev = mb.new_tcp_pi("192.168.255.74", 1502) 56 | local base_address = 0x2000 57 | dev:connect() 58 | local regs, err = dev:read_registers(base_address, 10) 59 | if not regs then error("read failed: " .. err) end 60 | for r,v in ipairs(regs) do 61 | print(string.format("register (offset %d) %d: %d (%#x): %#x (%d)", 62 | r, r, r + base_address - 1, r + base_address -1, v, v)) 63 | end 64 | ``` 65 | 66 | Another example writing registers instead 67 | 68 | ```Lua 69 | mb = require("libmodbus") 70 | local dev = mb.new_tcp_pi("192.168.255.74", 1502) 71 | dev:connect() 72 | 73 | -- will autoround 32.98 to 32. no magic support for writing floats yet :| 74 | -- numbers are cast to uint16_t internally. 75 | local res, err = dev:write_registers(0x2000, { 0xabcd, 32.98, 0xfffe, 0xabcd, -1 }) 76 | if not res then print(err) end 77 | ``` 78 | 79 | 80 | Testing 81 | ------- 82 | Some spec files for use with busted are provided. Some of them expect to have 83 | real hardware to talk to, so you may wish to disable them. 84 | 85 | ``` 86 | busted --exclude-tags=real 87 | ``` 88 | -------------------------------------------------------------------------------- /changelog: -------------------------------------------------------------------------------- 1 | 0.8 2022 November 2 | Add modbus_rtu_{get,set}_rts 3 | Add modbus_rtu_{get,set}_rts_delay 4 | 5 | 0.7 2021 February 6 | Fix helpers.set_s32 for "lnum" patch environments. #12 7 | 8 | 0.6.1 2020 July 9 | No functional change: tag acrobatics for luarocks+releases at the same time 10 | 11 | 0.6 2020 July 12 | add modbus_rtu_{set,get}_serial_mode 13 | 14 | 0.5 2019 August 15 | Fix compilation under windows/mingw 16 | 17 | 0.4.2: 2018 November 18 | Fix bad free when closing RTU contexts. #5 19 | Fix compat with lua 5.3 when backwards compat is turned off #6 20 | 21 | 0.4.1: 2018 June 22 | Drop unintended debug 23 | Fix documentation 24 | 25 | 0.4: 2018 June 26 | Add number helpers for dealing with 16/32bit signed/unsigned 27 | Add tostring() functionality 28 | Add documentation 29 | 30 | 0.3.1: 2017 March 31 | Support building with musl libc 32 | 33 | 0.3: 2017 March 34 | Add support for send_raw_request 35 | 36 | 0.2: 2016-09-09 37 | Add explicit connection close() method 38 | 39 | 0.1: Initial release 40 | Basic overage of libmodbus api 41 | -------------------------------------------------------------------------------- /compat.h: -------------------------------------------------------------------------------- 1 | 2 | #if LUA_VERSION_NUM < 502 3 | # define luaL_newlib(L,l) (lua_newtable(L), luaL_register(L,NULL,l)) 4 | # define luaL_setfuncs(L,l,n) (assert(n==0), luaL_register(L,NULL,l)) 5 | # define lua_rawlen(L,i) lua_objlen((L),(i)) 6 | #endif 7 | 8 | -------------------------------------------------------------------------------- /config.ld: -------------------------------------------------------------------------------- 1 | -- Config file for ldoc 2 | project = "lua-libmodbus" 3 | description = "Bindings to libmodbus" 4 | dir = "docs" -- Keeps github pages happy. 5 | 6 | file = { 7 | "lua-libmodbus.c" 8 | } 9 | 10 | examples = { "demo.lua" } 11 | 12 | prettify_files = "show" 13 | 14 | -------------------------------------------------------------------------------- /demo-rtu.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Very simple demo of using client/master APIs 3 | Karl Palsson, April 2016 4 | --]] 5 | 6 | mb = require("libmodbus") 7 | print("using libmodbus runtime version: ", mb.version()) 8 | print("using lua-libmodbus compiled against libmodbus: ", mb.VERSION_STRING) 9 | 10 | -- everything but device will default to modbus recommendations. 11 | local dev = mb.new_rtu("/dev/ttyUSB0", 19200, "even", 8, 1) 12 | print("byte timeout:", dev:get_byte_timeout()) 13 | print("response timeout", dev:get_response_timeout()) 14 | dev:set_debug() 15 | --dev:rtu_set_serial_mode(mb.RTU_RS232) 16 | --dev:set_error_recovery(mb.ERROR_RECOVERY_LINK, mb.ERROR_RECOVERY_PROTOCOL) 17 | --dev:set_error_recovery(mb.ERROR_RECOVERY_NONE) 18 | ok, err = dev:connect() 19 | if not ok then error("Couldn't connect: " .. err) end 20 | 21 | function simple_read(dev) 22 | dev:set_slave(0x88) 23 | local base_address = 0x2000 24 | local regs, err 25 | regs, err = dev:read_registers(base_address, 10) 26 | if not regs then error("read failed: " .. err) end 27 | 28 | for r,v in ipairs(regs) do 29 | print(string.format("register (offset %d) %d: %d (%#x): %#x (%d)", 30 | r, r, r + base_address - 1, r + base_address -1, v, v)) 31 | end 32 | 33 | --report = dev:report_slave_id() 34 | --print("report slaveid: ", report:byte(1), report:byte(2), report:sub(3)) 35 | 36 | end 37 | 38 | simple_read(dev) 39 | 40 | -------------------------------------------------------------------------------- /demo.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Very simple demo of using client/master APIs 3 | Karl Palsson, April 2016 4 | --]] 5 | 6 | mb = require("libmodbus") 7 | print("using libmodbus runtime version: ", mb.version()) 8 | print("using lua-libmodbus compiled against libmodbus: ", mb.VERSION_STRING) 9 | 10 | local dev = mb.new_tcp_pi("localhost", 1502) 11 | print(dev:get_byte_timeout()) 12 | print(dev:get_response_timeout()) 13 | print(dev) 14 | dev:set_debug() 15 | --dev:set_error_recovery(mb.ERROR_RECOVERY_LINK, mb.ERROR_RECOVERY_PROTOCOL) 16 | --dev:set_error_recovery(mb.ERROR_RECOVERY_NONE) 17 | ok, err = dev:connect() 18 | if not ok then error("Couldn't connect: " .. err) end 19 | 20 | function lmb_unit_test(dev) 21 | dev:set_slave(17) 22 | local base_address = 0x16b 23 | local regs, err 24 | regs, err = dev:read_registers(base_address, 3) 25 | if not regs then error("read failed: " .. err) end 26 | 27 | print("read 3 from 0x16b: ") 28 | for r,v in ipairs(regs) do 29 | print(string.format("register (offset %d) %d: %d (%#x): %#x (%d)", 30 | r, r, r + base_address - 1, r + base_address -1, v, v)) 31 | end 32 | 33 | print("write single 0x16c -> 0xabcd ", dev:write_register(0x16c, 0xabcd)) 34 | regs, err = dev:read_registers(0x16c, 1) 35 | if not regs then error("read1 failed: ", err) end 36 | print("reading back: single 0x16c: ", string.format("%x", regs[1])) 37 | 38 | regs, err = dev:read_input_registers(0x108, 1) 39 | if not regs then print("read input regs failed", err) end 40 | print("should be 0xa: ", string.format("%x", regs[1])) 41 | 42 | bits, err = dev:read_bits(0x130, 0x25) 43 | if not bits then print("read bits failed", err) end 44 | for r,v in ipairs(bits) do 45 | print(string.format("bit (offset %d) %d: %d (%#x): %#x (%d)", 46 | r, r, r + 0x130 - 1, r + 0x130 -1, v, v)) 47 | end 48 | 49 | bits, err = dev:write_bits(0x130, { 0xffff,0, true,0,false,1,0,99 }) 50 | if not bits then print("write bits failed", err) end 51 | bits, err = dev:read_bits(0x130, 8) 52 | print("reading back 8 bits, should be 0xa5") 53 | for r,v in ipairs(bits) do 54 | print(string.format("bit (offset %d) %d: %d (%#x): %#x (%d)", 55 | r, r, r + 0x130 - 1, r + 0x130 -1, v, v)) 56 | end 57 | 58 | err = dev:write_bit(0x131, 1); 59 | if err then print("write bit returned:", err) end 60 | bits, err = dev:read_bits(0x131, 1) 61 | if not bits then print("re-reading bits failed", err) end 62 | print("rereading bits should be 1 now: ", bits[1]) 63 | 64 | bits, err = dev:read_input_bits(0x1c4, 8) 65 | if not bits then print("read bits failed", err) end 66 | for r,v in ipairs(bits) do 67 | print(string.format("bit (offset %d) %d: %d (%#x): %#x (%d)", 68 | r, r, r + 0x1c4 - 1, r + 0x1c4 -1, v, v)) 69 | end 70 | 71 | report = dev:report_slave_id() 72 | print("report slaveid: ", report:byte(1), report:byte(2), report:sub(3)) 73 | 74 | end 75 | 76 | 77 | function write_test(dev) 78 | -- will autoround 32.98 to 32. no magic support for writing floats yet :| 79 | -- numbers are cast to uint16_t internally. 80 | local res, err = dev:write_registers(0x2000, { 0xabcd, 32.98, 0xfffe, 0xabcd, -1 }) 81 | if not res then print(err) end 82 | regs, err = dev:read_registers(base_address, 5) 83 | print("so, no magic casting 16bit tosigned!", regs[5], -1) 84 | 85 | print(dev:write_register(0x2006, 2)) 86 | end 87 | 88 | lmb_unit_test(dev) 89 | -------------------------------------------------------------------------------- /docs/examples/demo.lua.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Reference 7 | 8 | 9 | 10 | 11 |
12 | 13 |
14 | 15 |
16 |
17 |
18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 47 | 48 |
49 | 50 |

demo.lua

51 |
 52 | --[[
 53 | Very simple demo of using client/master APIs
 54 | Karl Palsson, April 2016
 55 | --]]
 56 | 
 57 | mb = require("libmodbus")
 58 | print("using libmodbus runtime version: ", mb.version())
 59 | print("using lua-libmodbus compiled against libmodbus: ", mb.VERSION_STRING)
 60 | 
 61 | local dev = mb.new_tcp_pi("localhost", 1502)
 62 | print(dev:get_byte_timeout())
 63 | print(dev:get_response_timeout())
 64 | print(dev)
 65 | dev:set_debug()
 66 | --dev:set_error_recovery(mb.ERROR_RECOVERY_LINK, mb.ERROR_RECOVERY_PROTOCOL)
 67 | --dev:set_error_recovery(mb.ERROR_RECOVERY_NONE)
 68 | ok, err = dev:connect()
 69 | if not ok then error("Couldn't connect: " .. err) end
 70 | 
 71 | function lmb_unit_test(dev)
 72 | 	dev:set_slave(17)
 73 | 	local base_address = 0x16b
 74 | 	local regs, err
 75 | 	regs, err = dev:read_registers(base_address, 3)
 76 | 	if not regs then error("read failed: " .. err) end
 77 | 
 78 | 	print("read 3 from 0x16b: ")
 79 | 	for r,v in ipairs(regs) do
 80 | 		print(string.format("register (offset %d) %d: %d (%#x): %#x (%d)",
 81 | 			r, r, r + base_address - 1, r + base_address -1, v, v))
 82 | 	end
 83 | 
 84 | 	print("write single 0x16c -> 0xabcd ", dev:write_register(0x16c, 0xabcd))
 85 | 	regs, err = dev:read_registers(0x16c, 1)
 86 | 	if not regs then error("read1 failed: ", err) end
 87 | 	print("reading back:  single 0x16c: ", string.format("%x", regs[1]))
 88 | 
 89 | 	regs, err = dev:read_input_registers(0x108, 1)
 90 | 	if not regs then print("read input regs failed", err) end
 91 | 	print("should be 0xa: ", string.format("%x", regs[1]))
 92 | 
 93 | 	bits, err = dev:read_bits(0x130, 0x25)
 94 | 	if not bits then print("read bits failed", err) end
 95 | 	for r,v in ipairs(bits) do
 96 | 		print(string.format("bit (offset %d) %d: %d (%#x): %#x (%d)",
 97 | 			r, r, r + 0x130 - 1, r + 0x130 -1, v, v))
 98 | 	end
 99 | 
100 | 	bits, err = dev:write_bits(0x130, { 0xffff,0, true,0,false,1,0,99 })
101 | 	if not bits then print("write bits failed", err) end
102 | 	bits, err = dev:read_bits(0x130, 8)
103 | 	print("reading back 8 bits, should be 0xa5")
104 | 	for r,v in ipairs(bits) do
105 | 		print(string.format("bit (offset %d) %d: %d (%#x): %#x (%d)",
106 | 			r, r, r + 0x130 - 1, r + 0x130 -1, v, v))
107 | 	end
108 | 
109 | 	err = dev:write_bit(0x131, 1);
110 | 	if err then print("write bit returned:", err) end
111 | 	bits, err = dev:read_bits(0x131, 1)
112 | 	if not bits then print("re-reading bits failed", err) end
113 | 	print("rereading bits should be 1 now: ", bits[1])
114 | 
115 | 	bits, err = dev:read_input_bits(0x1c4, 8)
116 | 	if not bits then print("read bits failed", err) end
117 | 	for r,v in ipairs(bits) do
118 | 		print(string.format("bit (offset %d) %d: %d (%#x): %#x (%d)",
119 | 			r, r, r + 0x1c4 - 1, r + 0x1c4 -1, v, v))
120 | 	end
121 | 
122 | 	report = dev:report_slave_id()
123 | 	print("report slaveid: ", report:byte(1), report:byte(2), report:sub(3))
124 | 
125 | end
126 | 
127 | 
128 | function write_test(dev)
129 | -- will autoround 32.98 to 32.  no magic support for writing floats yet :|
130 | -- numbers are cast to uint16_t internally.
131 | 	local res, err = dev:write_registers(0x2000, { 0xabcd, 32.98, 0xfffe, 0xabcd, -1 })
132 | 	if not res then print(err) end
133 | 	regs, err = dev:read_registers(base_address, 5)
134 | 	print("so, no magic casting 16bit tosigned!", regs[5], -1)
135 | 
136 | 	print(dev:write_register(0x2006, 2))
137 | end
138 | 
139 | lmb_unit_test(dev)
140 | 141 | 142 |
143 |
144 |
145 | generated by LDoc 1.4.6 146 | Last updated 2022-12-02 11:44:08 147 |
148 |
149 | 150 | 151 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Reference 7 | 8 | 9 | 10 | 11 |
12 | 13 |
14 | 15 |
16 |
17 |
18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 53 | 54 |
55 | 56 |

Module libmodbus

57 |

lua bindings to libmodbus.

58 |

59 |

Generally, this provides a very thin layer over libmodbus. Instead of 60 | passing the context around to all your modbus_xxx functions, you simply 61 | call them as member functions on the context returned by the new() functions. 62 |

63 |

Info:

64 |
    65 |
  • License: 66 | Permission is hereby granted, free of charge, to any person obtaining 67 | a copy of this software and associated documentation files (the 68 | "Software"), to deal in the Software without restriction, including 69 | without limitation the rights to use, copy, modify, merge, publish, 70 | distribute, sublicense, and/or sell copies of the Software, and to 71 | permit persons to whom the Software is furnished to do so, subject to 72 | the following conditions: 73 |

    The above copyright notice and this permission notice shall be 74 | included in all copies or substantial portions of the Software. 75 |

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 76 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 77 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 78 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 79 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 80 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 81 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

  • 82 |
  • Author: Karl Palsson 2016-2020
  • 83 |
84 | 85 | 86 |

Functions

87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 |
version ()Returns the runtime linked version of libmodbus as a string.
new_rtu (device, baud, parity, databits, stopbits)Create a Modbus/RTU context
new_tcp_pi (host, service)Create a Modbus/TCP context
set_s32 (num)Write a 32bit (u)int to 2x16bit registers
set_f32 (num)Write a bit float to 2x16bit registers
get_s16 (1)16bit register as number to signed 16bit
get_s32 (1, 2)2x16bit registers as number to signed 32 bit
get_s32le (1, 2)2x16bit registers as number to signed 32 bit (reverse order)
get_u32 (1, 2)2x16bit registers as number to unsigned 32 bit
get_u32le (1, 2)2x16bit registers as number to unsigned 32 bit (reversed order)
get_f32 (1, 2)2x16bit registers as number to 32 bit float
get_f32le (1, 2)2x16bit registers as number to 32 bit float (Reversed order)
get_s64 (1, 2, 3, 4)4x16bit registers as number to signed 64 bit
get_u64 (1, 2, 3, 4)4x16bit registers as number to unsigned 64 bit
145 |

Context Methods

146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 |
ctx:connect ()Connect, see modbus_connect()
ctx:close ()Close, see modbus_close()
ctx:set_debug (enable)Set debug
ctx:set_error_recovery (a, b)Set error recovery, see modbus_set_error_recovery.
ctx:set_byte_timeout (seconds, microseconds)See also modbus_set_byte_timeout
ctx:get_byte_timeout ()
ctx:set_response_timeout (seconds, microseconds)
ctx:get_response_timeout ()
ctx:get_socket ()
ctx:set_socket (sock)
ctx:rtu_get_serial_mode ()
ctx:rtu_set_serial_mode (mode)Sets the mode of a serial port.
ctx:rtu_get_rts ()
ctx:rtu_set_rts (mode)Sets the RTS handling of a serial port.
ctx:rtu_get_rts_delay ()
ctx:rtu_set_rts_delay (usecs)Sets the RTS delay of a serial port.
ctx:get_header_length ()Returns the header length of the transport
ctx:set_slave (unitid)
ctx:read_input_bits (address, count)
ctx:read_bits (address, count)
ctx:read_input_registers (address, count)
ctx:read_registers (address, count)
ctx:report_slave_id ()
ctx:write_bit (address, value)
ctx:write_register (address, value)
ctx:write_bits (address, value)
ctx:write_registers (address, value)
ctx:tcp_pi_listen (conns)
ctx:tcp_pi_accept (sock)
ctx:receive ()Receives a request from a remote.
ctx:reply_exception (initial, exc)Reply to a request with an Exception.
272 |

Constants provided for use

273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 290 | 291 | 292 | 293 | 294 | 295 |
rtu_constantsRTU Mode Constants
rtu_rts_constantsRTU RTS Consants
error_recovery_constantsError Recovery Constants
exception_codesException codes 289 | These are all MODBUS_xxxx upstream in libmodbus.
other_constantsOther constants
296 | 297 |
298 |
299 | 300 | 301 |

Functions

302 | 303 |
304 |
305 | 306 | version () 307 | line 83 308 |
309 |
310 | Returns the runtime linked version of libmodbus as a string. 311 | The compile time version is available as a constant VERSION. 312 | 313 | 314 | 315 |

Returns:

316 |
    317 | 318 | eg "3.0.6" 319 |
320 | 321 | 322 |

See also:

323 | 326 | 327 | 328 |
329 |
330 | 331 | new_rtu (device, baud, parity, databits, stopbits) 332 | line 103 333 |
334 |
335 | Create a Modbus/RTU context 336 | 337 | 338 |

Parameters:

339 |
    340 |
  • device 341 | (required) 342 |
  • 343 |
  • baud 344 | rate, defaults to 19200 345 |
  • 346 |
  • parity 347 | defaults to EVEN 348 |
  • 349 |
  • databits 350 | defaults to 8 351 |
  • 352 |
  • stopbits 353 | defaults to 1 354 |
  • 355 |
356 | 357 |

Returns:

358 |
    359 | 360 | a modbus context ready for use 361 |
362 | 363 | 364 | 365 | 366 |
367 |
368 | 369 | new_tcp_pi (host, service) 370 | line 168 371 |
372 |
373 | Create a Modbus/TCP context 374 | 375 | 376 |

Parameters:

377 |
    378 |
  • host 379 | eg "192.168.1.100" or "modbus.example.org" 380 |
  • 381 |
  • service 382 | eg "502" or "mbap" 383 |
  • 384 |
385 | 386 |

Returns:

387 |
    388 | 389 | a modbus context ready for use 390 |
391 | 392 | 393 | 394 | 395 |
396 |
397 | 398 | set_s32 (num) 399 | line 204 400 |
401 |
402 | Write a 32bit (u)int to 2x16bit registers 403 | 404 | 405 |

Parameters:

406 |
    407 |
  • num 408 | 32bit number 409 |
  • 410 |
411 | 412 |

Returns:

413 |
    414 |
  1. 415 | reg1 upper 16bits
  2. 416 |
  3. 417 | reg2 lower 16bits
  4. 418 |
419 | 420 | 421 | 422 | 423 |
424 |
425 | 426 | set_f32 (num) 427 | line 221 428 |
429 |
430 | Write a bit float to 2x16bit registers 431 | 432 | 433 |

Parameters:

434 |
    435 |
  • num 436 | floating point number 437 |
  • 438 |
439 | 440 |

Returns:

441 |
    442 |
  1. 443 | reg1 upper 16bits of a 32bit float
  2. 444 |
  3. 445 | reg2 lower 16bits of a 32bit float
  4. 446 |
447 | 448 | 449 | 450 | 451 |
452 |
453 | 454 | get_s16 (1) 455 | line 239 456 |
457 |
458 | 16bit register as number to signed 16bit 459 | 460 | 461 |

Parameters:

462 |
    463 |
  • 1 464 | 16bit register 465 |
  • 466 |
467 | 468 |

Returns:

469 |
    470 | 471 | signed 16bit number 472 |
473 | 474 | 475 | 476 | 477 |
478 |
479 | 480 | get_s32 (1, 2) 481 | line 253 482 |
483 |
484 | 2x16bit registers as number to signed 32 bit 485 | 486 | 487 |

Parameters:

488 |
    489 |
  • 1 490 | 16bit register 491 |
  • 492 |
  • 2 493 | 16bit register 494 |
  • 495 |
496 | 497 |

Returns:

498 |
    499 | 500 | 32bit number 501 |
502 | 503 | 504 | 505 | 506 |
507 |
508 | 509 | get_s32le (1, 2) 510 | line 269 511 |
512 |
513 | 2x16bit registers as number to signed 32 bit (reverse order) 514 | 515 | 516 |

Parameters:

517 |
    518 |
  • 1 519 | 16bit register 520 |
  • 521 |
  • 2 522 | 16bit register 523 |
  • 524 |
525 | 526 |

Returns:

527 |
    528 | 529 | 32bit number 530 |
531 | 532 | 533 | 534 | 535 |
536 |
537 | 538 | get_u32 (1, 2) 539 | line 286 540 |
541 |
542 | 2x16bit registers as number to unsigned 32 bit 543 | 544 | 545 |

Parameters:

546 |
    547 |
  • 1 548 | 16bit register 549 |
  • 550 |
  • 2 551 | 16bit register 552 |
  • 553 |
554 | 555 |

Returns:

556 |
    557 | 558 | 32bit number 559 |
560 | 561 | 562 | 563 | 564 |
565 |
566 | 567 | get_u32le (1, 2) 568 | line 302 569 |
570 |
571 | 2x16bit registers as number to unsigned 32 bit (reversed order) 572 | 573 | 574 |

Parameters:

575 |
    576 |
  • 1 577 | 16bit register 578 |
  • 579 |
  • 2 580 | 16bit register 581 |
  • 582 |
583 | 584 |

Returns:

585 |
    586 | 587 | 32bit number 588 |
589 | 590 | 591 | 592 | 593 |
594 |
595 | 596 | get_f32 (1, 2) 597 | line 318 598 |
599 |
600 | 2x16bit registers as number to 32 bit float 601 | 602 | 603 |

Parameters:

604 |
    605 |
  • 1 606 | 16bit register 607 |
  • 608 |
  • 2 609 | 16bit register 610 |
  • 611 |
612 | 613 |

Returns:

614 |
    615 | 616 | 32bit float 617 |
618 | 619 | 620 | 621 | 622 |
623 |
624 | 625 | get_f32le (1, 2) 626 | line 338 627 |
628 |
629 | 2x16bit registers as number to 32 bit float (Reversed order) 630 | 631 | 632 |

Parameters:

633 |
    634 |
  • 1 635 | 16bit register 636 |
  • 637 |
  • 2 638 | 16bit register 639 |
  • 640 |
641 | 642 |

Returns:

643 |
    644 | 645 | 32bit float 646 |
647 | 648 | 649 | 650 | 651 |
652 |
653 | 654 | get_s64 (1, 2, 3, 4) 655 | line 360 656 |
657 |
658 | 4x16bit registers as number to signed 64 bit 659 | 660 | 661 |

Parameters:

662 |
    663 |
  • 1 664 | 16bit register 665 |
  • 666 |
  • 2 667 | 16bit register 668 |
  • 669 |
  • 3 670 | 16bit register 671 |
  • 672 |
  • 4 673 | 16bit register 674 |
  • 675 |
676 | 677 |

Returns:

678 |
    679 | 680 | 64bit number 681 |
682 | 683 | 684 | 685 | 686 |
687 |
688 | 689 | get_u64 (1, 2, 3, 4) 690 | line 383 691 |
692 |
693 | 4x16bit registers as number to unsigned 64 bit 694 | 695 | 696 |

Parameters:

697 |
    698 |
  • 1 699 | 16bit register 700 |
  • 701 |
  • 2 702 | 16bit register 703 |
  • 704 |
  • 3 705 | 16bit register 706 |
  • 707 |
  • 4 708 | 16bit register 709 |
  • 710 |
711 | 712 |

Returns:

713 |
    714 | 715 | 64bit number 716 |
717 | 718 | 719 | 720 | 721 |
722 |
723 |

Context Methods

724 | 725 |
726 | 727 | These functions are members of a modbus context, from either new_rtu() or new_tcp_pi() 728 |
729 |
730 |
731 | 732 | ctx:connect () 733 | line 441 734 |
735 |
736 | Connect, see modbus_connect() 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 |
745 |
746 | 747 | ctx:close () 748 | line 454 749 |
750 |
751 | Close, see modbus_close() 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 |
760 |
761 | 762 | ctx:set_debug (enable) 763 | line 468 764 |
765 |
766 | Set debug 767 | 768 | 769 |

Parameters:

770 |
    771 |
  • enable 772 | optional bool, defaults to true 773 |
  • 774 |
775 | 776 | 777 | 778 | 779 | 780 |
781 |
782 | 783 | ctx:set_error_recovery (a, b) 784 | line 488 785 |
786 |
787 | Set error recovery, see modbus_set_error_recovery. 788 | The arguments will be or'd together. 789 | 790 | 791 |

Parameters:

792 | 800 | 801 | 802 | 803 | 804 | 805 |
806 |
807 | 808 | ctx:set_byte_timeout (seconds, microseconds) 809 | line 505 810 |
811 |
812 | See also modbus_set_byte_timeout 813 | 814 | 815 |

Parameters:

816 |
    817 |
  • seconds 818 | (required) 819 |
  • 820 |
  • microseconds 821 | (optional, defaults to 0) 822 |
  • 823 |
824 | 825 | 826 | 827 | 828 | 829 |
830 |
831 | 832 | ctx:get_byte_timeout () 833 | line 523 834 |
835 |
836 | 837 | 838 | 839 | 840 |

Returns:

841 |
    842 |
  1. 843 | seconds
  2. 844 |
  3. 845 | microseconds
  4. 846 |
847 | 848 | 849 | 850 | 851 |
852 |
853 | 854 | ctx:set_response_timeout (seconds, microseconds) 855 | line 544 856 |
857 |
858 | 859 | 860 | 861 |

Parameters:

862 |
    863 |
  • seconds 864 | (required) 865 |
  • 866 |
  • microseconds 867 | (optional, defaults to 0) 868 |
  • 869 |
870 | 871 | 872 | 873 | 874 | 875 |
876 |
877 | 878 | ctx:get_response_timeout () 879 | line 562 880 |
881 |
882 | 883 | 884 | 885 | 886 |

Returns:

887 |
    888 |
  1. 889 | seconds
  2. 890 |
  3. 891 | microseconds
  4. 892 |
893 | 894 | 895 | 896 | 897 |
898 |
899 | 900 | ctx:get_socket () 901 | line 582 902 |
903 |
904 | 905 | 906 | 907 | 908 |

Returns:

909 |
    910 | 911 | the socket number 912 |
913 | 914 | 915 | 916 | 917 |
918 |
919 | 920 | ctx:set_socket (sock) 921 | line 595 922 |
923 |
924 | 925 | 926 | 927 |

Parameters:

928 |
    929 |
  • sock 930 | integer socket number to set 931 |
  • 932 |
933 | 934 | 935 | 936 | 937 | 938 |
939 |
940 | 941 | ctx:rtu_get_serial_mode () 942 | line 609 943 |
944 |
945 | 946 | 947 | 948 | 949 |

Returns:

950 |
    951 | 952 | rtu_constants the serial mode, either RTU_RS232 or RTU_RS485 953 |
954 | 955 | 956 | 957 | 958 |
959 |
960 | 961 | ctx:rtu_set_serial_mode (mode) 962 | line 628 963 |
964 |
965 | Sets the mode of a serial port. 966 | Remember, this is only required if your kernel is handling rs485 natively. 967 | If you are using a USB adapter, you do NOT need this. 968 | 969 | 970 |

Parameters:

971 |
    972 |
  • mode 973 | The selected serial mode from rtu_constants, either RTU_RS232 or RTU_RS485. 974 |
  • 975 |
976 | 977 | 978 | 979 | 980 | 981 |
982 |
983 | 984 | ctx:rtu_get_rts () 985 | line 645 986 |
987 |
988 | 989 | 990 | 991 | 992 |

Returns:

993 |
    994 | 995 | rtu_rts_constants the RTS handling mode, up/down/none 996 |
997 | 998 | 999 | 1000 | 1001 |
1002 |
1003 | 1004 | ctx:rtu_set_rts (mode) 1005 | line 662 1006 |
1007 |
1008 | Sets the RTS handling of a serial port. 1009 | 1010 | 1011 |

Parameters:

1012 |
    1013 |
  • mode 1014 | The selected RTS handling mode from rtu_rts_constants, up/down/none. 1015 |
  • 1016 |
1017 | 1018 | 1019 | 1020 | 1021 | 1022 |
1023 |
1024 | 1025 | ctx:rtu_get_rts_delay () 1026 | line 679 1027 |
1028 |
1029 | 1030 | 1031 | 1032 | 1033 |

Returns:

1034 |
    1035 | 1036 | usecs 1037 |
1038 | 1039 | 1040 | 1041 | 1042 |
1043 |
1044 | 1045 | ctx:rtu_set_rts_delay (usecs) 1046 | line 696 1047 |
1048 |
1049 | Sets the RTS delay of a serial port. 1050 | 1051 | 1052 |

Parameters:

1053 |
    1054 |
  • usecs 1055 | 1056 |
  • 1057 |
1058 | 1059 | 1060 | 1061 | 1062 | 1063 |
1064 |
1065 | 1066 | ctx:get_header_length () 1067 | line 714 1068 |
1069 |
1070 | Returns the header length of the transport 1071 | 1072 | 1073 | 1074 |

Returns:

1075 |
    1076 | 1077 | the length in bytes 1078 |
1079 | 1080 | 1081 | 1082 | 1083 |
1084 |
1085 | 1086 | ctx:set_slave (unitid) 1087 | line 727 1088 |
1089 |
1090 | 1091 | 1092 | 1093 |

Parameters:

1094 |
    1095 |
  • unitid 1096 | the unit address / slave id to use 1097 |
  • 1098 |
1099 | 1100 | 1101 | 1102 | 1103 | 1104 |
1105 |
1106 | 1107 | ctx:read_input_bits (address, count) 1108 | line 781 1109 |
1110 |
1111 | 1112 | 1113 | 1114 |

Parameters:

1115 |
    1116 |
  • address 1117 | 1118 |
  • 1119 |
  • count 1120 | 1121 |
  • 1122 |
1123 | 1124 |

Returns:

1125 |
    1126 | 1127 | an array of results 1128 |
1129 | 1130 | 1131 | 1132 | 1133 |
1134 |
1135 | 1136 | ctx:read_bits (address, count) 1137 | line 792 1138 |
1139 |
1140 | 1141 | 1142 | 1143 |

Parameters:

1144 |
    1145 |
  • address 1146 | 1147 |
  • 1148 |
  • count 1149 | 1150 |
  • 1151 |
1152 | 1153 |

Returns:

1154 |
    1155 | 1156 | an array of results 1157 |
1158 | 1159 | 1160 | 1161 | 1162 |
1163 |
1164 | 1165 | ctx:read_input_registers (address, count) 1166 | line 840 1167 |
1168 |
1169 | 1170 | 1171 | 1172 |

Parameters:

1173 |
    1174 |
  • address 1175 | 1176 |
  • 1177 |
  • count 1178 | 1179 |
  • 1180 |
1181 | 1182 |

Returns:

1183 |
    1184 | 1185 | an array of results 1186 |
1187 | 1188 | 1189 | 1190 | 1191 |
1192 |
1193 | 1194 | ctx:read_registers (address, count) 1195 | line 851 1196 |
1197 |
1198 | 1199 | 1200 | 1201 |

Parameters:

1202 |
    1203 |
  • address 1204 | 1205 |
  • 1206 |
  • count 1207 | 1208 |
  • 1209 |
1210 | 1211 |

Returns:

1212 |
    1213 | 1214 | an array of results 1215 |
1216 | 1217 | 1218 | 1219 | 1220 |
1221 |
1222 | 1223 | ctx:report_slave_id () 1224 | line 860 1225 |
1226 |
1227 | 1228 | 1229 | 1230 | 1231 |

Returns:

1232 |
    1233 | 1234 | a luastring with the raw result (lua strings can contain nulls) 1235 |
1236 | 1237 | 1238 | 1239 | 1240 |
1241 |
1242 | 1243 | ctx:write_bit (address, value) 1244 | line 881 1245 |
1246 |
1247 | 1248 | 1249 | 1250 |

Parameters:

1251 |
    1252 |
  • address 1253 | 1254 |
  • 1255 |
  • value 1256 | either a number or a boolean 1257 |
  • 1258 |
1259 | 1260 | 1261 | 1262 | 1263 | 1264 |
1265 |
1266 | 1267 | ctx:write_register (address, value) 1268 | line 905 1269 |
1270 |
1271 | 1272 | 1273 | 1274 |

Parameters:

1275 |
    1276 |
  • address 1277 | 1278 |
  • 1279 |
  • value 1280 | 1281 |
  • 1282 |
1283 | 1284 | 1285 | 1286 | 1287 | 1288 |
1289 |
1290 | 1291 | ctx:write_bits (address, value) 1292 | line 922 1293 |
1294 |
1295 | 1296 | 1297 | 1298 |

Parameters:

1299 |
    1300 |
  • address 1301 | 1302 |
  • 1303 |
  • value 1304 | as a lua array table 1305 |
  • 1306 |
1307 | 1308 | 1309 | 1310 | 1311 | 1312 |
1313 |
1314 | 1315 | ctx:write_registers (address, value) 1316 | line 984 1317 |
1318 |
1319 | 1320 | 1321 | 1322 |

Parameters:

1323 |
    1324 |
  • address 1325 | base address to write to 1326 |
  • 1327 |
  • value 1328 | as a lua array table, or a sequence of values. 1329 |
  • 1330 |
1331 | 1332 | 1333 | 1334 | 1335 |

Usage:

1336 |
    1337 |
    either
    1338 |  ctx:write_registers(0x2000, {1,2,3})
    1339 |  ctx:write_registers(0x2000, 1, 2, 3)
    1340 |
1341 | 1342 |
1343 |
1344 | 1345 | ctx:tcp_pi_listen (conns) 1346 | line 1095 1347 |
1348 |
1349 | 1350 | 1351 | 1352 |

Parameters:

1353 |
    1354 |
  • conns 1355 | max connections to listen to, defaults to 1 1356 |
  • 1357 |
1358 | 1359 |

Returns:

1360 |
    1361 | 1362 | the new socket number or normal error 1363 |
1364 | 1365 | 1366 | 1367 | 1368 |
1369 |
1370 | 1371 | ctx:tcp_pi_accept (sock) 1372 | line 1117 1373 |
1374 |
1375 | 1376 | 1377 | 1378 |

Parameters:

1379 |
    1380 |
  • sock 1381 | the socket we're accepting on 1382 |
  • 1383 |
1384 | 1385 |

Returns:

1386 |
    1387 | 1388 | the new socket we've accepted or normal error 1389 |
1390 | 1391 | 1392 | 1393 | 1394 |
1395 |
1396 | 1397 | ctx:receive () 1398 | line 1141 1399 |
1400 |
1401 | Receives a request from a remote. 1402 | WARNING this might not be complete, documented that it exists 1403 | 1404 | 1405 | 1406 |

Returns:

1407 |
    1408 |
  1. 1409 | the length of the data
  2. 1410 |
  3. 1411 | the data received
  4. 1412 |
1413 | 1414 | 1415 | 1416 | 1417 |
1418 |
1419 | 1420 | ctx:reply_exception (initial, exc) 1421 | line 1183 1422 |
1423 |
1424 | Reply to a request with an Exception. 1425 | 1426 | 1427 |

Parameters:

1428 |
    1429 |
  • initial 1430 | the initial request to build into an exception 1431 |
  • 1432 |
  • exc 1433 | the exception code to use from exception_codes 1434 |
  • 1435 |
1436 | 1437 |

Returns:

1438 |
    1439 | 1440 | the new socket number or normal error 1441 |
1442 | 1443 | 1444 | 1445 | 1446 |
1447 |
1448 |

Constants provided for use

1449 | 1450 |
1451 | 1452 | Constants used in various functions, either as arguments or returns 1453 |
1454 |
1455 |
1456 | 1457 | rtu_constants 1458 | line 1230 1459 |
1460 |
1461 | RTU Mode Constants 1462 | 1463 | 1464 |

Fields:

1465 |
    1466 |
  • RTU_RS232 1467 | 1468 |
  • 1469 |
  • RTU_RS485 1470 | 1471 |
  • 1472 |
1473 | 1474 | 1475 | 1476 |

See also:

1477 | 1481 | 1482 | 1483 |
1484 |
1485 | 1486 | rtu_rts_constants 1487 | line 1238 1488 |
1489 |
1490 | RTU RTS Consants 1491 | 1492 | 1493 |

Fields:

1494 |
    1495 |
  • RTU_RTS_NONE 1496 | 1497 |
  • 1498 |
  • RTU_RTS_UP 1499 | 1500 |
  • 1501 |
  • RTU_RTS_DOWN 1502 | 1503 |
  • 1504 |
1505 | 1506 | 1507 | 1508 |

See also:

1509 | 1513 | 1514 | 1515 |
1516 |
1517 | 1518 | error_recovery_constants 1519 | line 1253 1520 |
1521 |
1522 | Error Recovery Constants 1523 | 1524 | 1525 |

Fields:

1526 |
    1527 |
  • ERROR_RECOVERY_NONE 1528 | 1529 |
  • 1530 |
  • ERROR_RECOVERY_LINK 1531 | 1532 |
  • 1533 |
  • ERROR_RECOVERY_PROTOCOL 1534 | 1535 |
  • 1536 |
1537 | 1538 | 1539 | 1540 |

See also:

1541 | 1544 | 1545 | 1546 |
1547 |
1548 | 1549 | exception_codes 1550 | line 1254 1551 |
1552 |
1553 | Exception codes 1554 | These are all MODBUS_xxxx upstream in libmodbus. 1555 | 1556 | 1557 |

Fields:

1558 |
    1559 |
  • EXCEPTION_ILLEGAL_FUNCTION 1560 | 1561 |
  • 1562 |
  • EXCEPTION_ILLEGAL_DATA_ADDRESS 1563 | 1564 |
  • 1565 |
  • EXCEPTION_ILLEGAL_DATA_VALUE 1566 | 1567 |
  • 1568 |
  • EXCEPTION_SLAVE_OR_SERVER_FAILURE 1569 | 1570 |
  • 1571 |
  • EXCEPTION_ACKNOWLEDGE 1572 | 1573 |
  • 1574 |
  • EXCEPTION_SLAVE_OR_SERVER_BUSY 1575 | 1576 |
  • 1577 |
  • EXCEPTION_NEGATIVE_ACKNOWLEDGE 1578 | 1579 |
  • 1580 |
  • EXCEPTION_MEMORY_PARITY 1581 | 1582 |
  • 1583 |
  • EXCEPTION_NOT_DEFINED 1584 | 1585 |
  • 1586 |
  • EXCEPTION_GATEWAY_PATH 1587 | 1588 |
  • 1589 |
1590 | 1591 | 1592 | 1593 | 1594 | 1595 |
1596 |
1597 | 1598 | other_constants 1599 | line 1285 1600 |
1601 |
1602 | Other constants 1603 | 1604 | 1605 |

Fields:

1606 |
    1607 |
  • VERSION_STRING 1608 | the compile time version, see also version 1609 |
  • 1610 |
  • BROADCAST_ADDRESS 1611 | used in set_slave 1612 |
  • 1613 |
  • TCP_SLAVE 1614 | used in set_slave 1615 |
  • 1616 |
1617 | 1618 | 1619 | 1620 | 1621 | 1622 |
1623 |
1624 | 1625 | 1626 |
1627 |
1628 |
1629 | generated by LDoc 1.4.6 1630 | Last updated 2022-12-02 11:44:08 1631 |
1632 |
1633 | 1634 | 1635 | -------------------------------------------------------------------------------- /docs/ldoc.css: -------------------------------------------------------------------------------- 1 | /* BEGIN RESET 2 | 3 | Copyright (c) 2010, Yahoo! Inc. All rights reserved. 4 | Code licensed under the BSD License: 5 | http://developer.yahoo.com/yui/license.html 6 | version: 2.8.2r1 7 | */ 8 | html { 9 | color: #000; 10 | background: #FFF; 11 | } 12 | body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td { 13 | margin: 0; 14 | padding: 0; 15 | } 16 | table { 17 | border-collapse: collapse; 18 | border-spacing: 0; 19 | } 20 | fieldset,img { 21 | border: 0; 22 | } 23 | address,caption,cite,code,dfn,em,strong,th,var,optgroup { 24 | font-style: inherit; 25 | font-weight: inherit; 26 | } 27 | del,ins { 28 | text-decoration: none; 29 | } 30 | li { 31 | margin-left: 20px; 32 | } 33 | caption,th { 34 | text-align: left; 35 | } 36 | h1,h2,h3,h4,h5,h6 { 37 | font-size: 100%; 38 | font-weight: bold; 39 | } 40 | q:before,q:after { 41 | content: ''; 42 | } 43 | abbr,acronym { 44 | border: 0; 45 | font-variant: normal; 46 | } 47 | sup { 48 | vertical-align: baseline; 49 | } 50 | sub { 51 | vertical-align: baseline; 52 | } 53 | legend { 54 | color: #000; 55 | } 56 | input,button,textarea,select,optgroup,option { 57 | font-family: inherit; 58 | font-size: inherit; 59 | font-style: inherit; 60 | font-weight: inherit; 61 | } 62 | input,button,textarea,select {*font-size:100%; 63 | } 64 | /* END RESET */ 65 | 66 | body { 67 | margin-left: 1em; 68 | margin-right: 1em; 69 | font-family: arial, helvetica, geneva, sans-serif; 70 | background-color: #ffffff; margin: 0px; 71 | } 72 | 73 | code, tt { font-family: monospace; font-size: 1.1em; } 74 | span.parameter { font-family:monospace; } 75 | span.parameter:after { content:":"; } 76 | span.types:before { content:"("; } 77 | span.types:after { content:")"; } 78 | .type { font-weight: bold; font-style:italic } 79 | 80 | body, p, td, th { font-size: .95em; line-height: 1.2em;} 81 | 82 | p, ul { margin: 10px 0 0 0px;} 83 | 84 | strong { font-weight: bold;} 85 | 86 | em { font-style: italic;} 87 | 88 | h1 { 89 | font-size: 1.5em; 90 | margin: 20px 0 20px 0; 91 | } 92 | h2, h3, h4 { margin: 15px 0 10px 0; } 93 | h2 { font-size: 1.25em; } 94 | h3 { font-size: 1.15em; } 95 | h4 { font-size: 1.06em; } 96 | 97 | a:link { font-weight: bold; color: #004080; text-decoration: none; } 98 | a:visited { font-weight: bold; color: #006699; text-decoration: none; } 99 | a:link:hover { text-decoration: underline; } 100 | 101 | hr { 102 | color:#cccccc; 103 | background: #00007f; 104 | height: 1px; 105 | } 106 | 107 | blockquote { margin-left: 3em; } 108 | 109 | ul { list-style-type: disc; } 110 | 111 | p.name { 112 | font-family: "Andale Mono", monospace; 113 | padding-top: 1em; 114 | } 115 | 116 | pre { 117 | background-color: rgb(245, 245, 245); 118 | border: 1px solid #C0C0C0; /* silver */ 119 | padding: 10px; 120 | margin: 10px 0 10px 0; 121 | overflow: auto; 122 | font-family: "Andale Mono", monospace; 123 | } 124 | 125 | pre.example { 126 | font-size: .85em; 127 | } 128 | 129 | table.index { border: 1px #00007f; } 130 | table.index td { text-align: left; vertical-align: top; } 131 | 132 | #container { 133 | margin-left: 1em; 134 | margin-right: 1em; 135 | background-color: #f0f0f0; 136 | } 137 | 138 | #product { 139 | text-align: center; 140 | border-bottom: 1px solid #cccccc; 141 | background-color: #ffffff; 142 | } 143 | 144 | #product big { 145 | font-size: 2em; 146 | } 147 | 148 | #main { 149 | background-color: #f0f0f0; 150 | border-left: 2px solid #cccccc; 151 | } 152 | 153 | #navigation { 154 | float: left; 155 | width: 14em; 156 | vertical-align: top; 157 | background-color: #f0f0f0; 158 | overflow: visible; 159 | } 160 | 161 | #navigation h2 { 162 | background-color:#e7e7e7; 163 | font-size:1.1em; 164 | color:#000000; 165 | text-align: left; 166 | padding:0.2em; 167 | border-top:1px solid #dddddd; 168 | border-bottom:1px solid #dddddd; 169 | } 170 | 171 | #navigation ul 172 | { 173 | font-size:1em; 174 | list-style-type: none; 175 | margin: 1px 1px 10px 1px; 176 | } 177 | 178 | #navigation li { 179 | text-indent: -1em; 180 | display: block; 181 | margin: 3px 0px 0px 22px; 182 | } 183 | 184 | #navigation li li a { 185 | margin: 0px 3px 0px -1em; 186 | } 187 | 188 | #content { 189 | margin-left: 14em; 190 | padding: 1em; 191 | width: 700px; 192 | border-left: 2px solid #cccccc; 193 | border-right: 2px solid #cccccc; 194 | background-color: #ffffff; 195 | } 196 | 197 | #about { 198 | clear: both; 199 | padding: 5px; 200 | border-top: 2px solid #cccccc; 201 | background-color: #ffffff; 202 | } 203 | 204 | @media print { 205 | body { 206 | font: 12pt "Times New Roman", "TimeNR", Times, serif; 207 | } 208 | a { font-weight: bold; color: #004080; text-decoration: underline; } 209 | 210 | #main { 211 | background-color: #ffffff; 212 | border-left: 0px; 213 | } 214 | 215 | #container { 216 | margin-left: 2%; 217 | margin-right: 2%; 218 | background-color: #ffffff; 219 | } 220 | 221 | #content { 222 | padding: 1em; 223 | background-color: #ffffff; 224 | } 225 | 226 | #navigation { 227 | display: none; 228 | } 229 | pre.example { 230 | font-family: "Andale Mono", monospace; 231 | font-size: 10pt; 232 | page-break-inside: avoid; 233 | } 234 | } 235 | 236 | table.module_list { 237 | border-width: 1px; 238 | border-style: solid; 239 | border-color: #cccccc; 240 | border-collapse: collapse; 241 | } 242 | table.module_list td { 243 | border-width: 1px; 244 | padding: 3px; 245 | border-style: solid; 246 | border-color: #cccccc; 247 | } 248 | table.module_list td.name { background-color: #f0f0f0; min-width: 200px; } 249 | table.module_list td.summary { width: 100%; } 250 | 251 | 252 | table.function_list { 253 | border-width: 1px; 254 | border-style: solid; 255 | border-color: #cccccc; 256 | border-collapse: collapse; 257 | } 258 | table.function_list td { 259 | border-width: 1px; 260 | padding: 3px; 261 | border-style: solid; 262 | border-color: #cccccc; 263 | } 264 | table.function_list td.name { background-color: #f0f0f0; min-width: 200px; } 265 | table.function_list td.summary { width: 100%; } 266 | 267 | ul.nowrap { 268 | overflow:auto; 269 | white-space:nowrap; 270 | } 271 | 272 | dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;} 273 | dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;} 274 | dl.table h3, dl.function h3 {font-size: .95em;} 275 | 276 | /* stop sublists from having initial vertical space */ 277 | ul ul { margin-top: 0px; } 278 | ol ul { margin-top: 0px; } 279 | ol ol { margin-top: 0px; } 280 | ul ol { margin-top: 0px; } 281 | 282 | /* make the target distinct; helps when we're navigating to a function */ 283 | a:target + * { 284 | background-color: #FF9; 285 | } 286 | 287 | 288 | /* styles for prettification of source */ 289 | pre .comment { color: #558817; } 290 | pre .constant { color: #a8660d; } 291 | pre .escape { color: #844631; } 292 | pre .keyword { color: #aa5050; font-weight: bold; } 293 | pre .library { color: #0e7c6b; } 294 | pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; } 295 | pre .string { color: #8080ff; } 296 | pre .number { color: #f8660d; } 297 | pre .operator { color: #2239a8; font-weight: bold; } 298 | pre .preprocessor, pre .prepro { color: #a33243; } 299 | pre .global { color: #800080; } 300 | pre .user-keyword { color: #800080; } 301 | pre .prompt { color: #558817; } 302 | pre .url { color: #272fc2; text-decoration: underline; } 303 | 304 | -------------------------------------------------------------------------------- /docs/source/demo.lua.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Reference 7 | 8 | 9 | 10 | 11 |
12 | 13 |
14 | 15 |
16 |
17 |
18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 47 | 48 |
49 | 50 |

demo.lua

51 |
 52 | --[[
 53 | Very simple demo of using client/master APIs
 54 | Karl Palsson, April 2016
 55 | --]]
 56 | 
 57 | mb = require("libmodbus")
 58 | print("using libmodbus runtime version: ", mb.version())
 59 | print("using lua-libmodbus compiled against libmodbus: ", mb.VERSION_STRING)
 60 | 
 61 | local dev = mb.new_tcp_pi("localhost", 1502)
 62 | print(dev:get_byte_timeout())
 63 | print(dev:get_response_timeout())
 64 | print(dev)
 65 | dev:set_debug()
 66 | --dev:set_error_recovery(mb.ERROR_RECOVERY_LINK, mb.ERROR_RECOVERY_PROTOCOL)
 67 | --dev:set_error_recovery(mb.ERROR_RECOVERY_NONE)
 68 | ok, err = dev:connect()
 69 | if not ok then error("Couldn't connect: " .. err) end
 70 | 
 71 | function lmb_unit_test(dev)
 72 | 	dev:set_slave(17)
 73 | 	local base_address = 0x16b
 74 | 	local regs, err
 75 | 	regs, err = dev:read_registers(base_address, 3)
 76 | 	if not regs then error("read failed: " .. err) end
 77 | 
 78 | 	print("read 3 from 0x16b: ")
 79 | 	for r,v in ipairs(regs) do
 80 | 		print(string.format("register (offset %d) %d: %d (%#x): %#x (%d)",
 81 | 			r, r, r + base_address - 1, r + base_address -1, v, v))
 82 | 	end
 83 | 
 84 | 	print("write single 0x16c -> 0xabcd ", dev:write_register(0x16c, 0xabcd))
 85 | 	regs, err = dev:read_registers(0x16c, 1)
 86 | 	if not regs then error("read1 failed: ", err) end
 87 | 	print("reading back:  single 0x16c: ", string.format("%x", regs[1]))
 88 | 
 89 | 	regs, err = dev:read_input_registers(0x108, 1)
 90 | 	if not regs then print("read input regs failed", err) end
 91 | 	print("should be 0xa: ", string.format("%x", regs[1]))
 92 | 
 93 | 	bits, err = dev:read_bits(0x130, 0x25)
 94 | 	if not bits then print("read bits failed", err) end
 95 | 	for r,v in ipairs(bits) do
 96 | 		print(string.format("bit (offset %d) %d: %d (%#x): %#x (%d)",
 97 | 			r, r, r + 0x130 - 1, r + 0x130 -1, v, v))
 98 | 	end
 99 | 
100 | 	bits, err = dev:write_bits(0x130, { 0xffff,0, true,0,false,1,0,99 })
101 | 	if not bits then print("write bits failed", err) end
102 | 	bits, err = dev:read_bits(0x130, 8)
103 | 	print("reading back 8 bits, should be 0xa5")
104 | 	for r,v in ipairs(bits) do
105 | 		print(string.format("bit (offset %d) %d: %d (%#x): %#x (%d)",
106 | 			r, r, r + 0x130 - 1, r + 0x130 -1, v, v))
107 | 	end
108 | 
109 | 	err = dev:write_bit(0x131, 1);
110 | 	if err then print("write bit returned:", err) end
111 | 	bits, err = dev:read_bits(0x131, 1)
112 | 	if not bits then print("re-reading bits failed", err) end
113 | 	print("rereading bits should be 1 now: ", bits[1])
114 | 
115 | 	bits, err = dev:read_input_bits(0x1c4, 8)
116 | 	if not bits then print("read bits failed", err) end
117 | 	for r,v in ipairs(bits) do
118 | 		print(string.format("bit (offset %d) %d: %d (%#x): %#x (%d)",
119 | 			r, r, r + 0x1c4 - 1, r + 0x1c4 -1, v, v))
120 | 	end
121 | 
122 | 	report = dev:report_slave_id()
123 | 	print("report slaveid: ", report:byte(1), report:byte(2), report:sub(3))
124 | 
125 | end
126 | 
127 | 
128 | function write_test(dev)
129 | -- will autoround 32.98 to 32.  no magic support for writing floats yet :|
130 | -- numbers are cast to uint16_t internally.
131 | 	local res, err = dev:write_registers(0x2000, { 0xabcd, 32.98, 0xfffe, 0xabcd, -1 })
132 | 	if not res then print(err) end
133 | 	regs, err = dev:read_registers(base_address, 5)
134 | 	print("so, no magic casting 16bit tosigned!", regs[5], -1)
135 | 
136 | 	print(dev:write_register(0x2006, 2))
137 | end
138 | 
139 | lmb_unit_test(dev)
140 | 141 | 142 |
143 |
144 |
145 | generated by LDoc 1.4.6 146 | Last updated 2022-12-02 11:44:08 147 |
148 |
149 | 150 | 151 | -------------------------------------------------------------------------------- /docs/source/lua-libmodbus.c.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Reference 7 | 8 | 9 | 10 | 11 |
12 | 13 |
14 | 15 |
16 |
17 |
18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 47 | 48 |
49 | 50 |

lua-libmodbus.c

51 |
  52 | /*** lua bindings to libmodbus.
  53 | <p>Generally, this provides a very thin layer over libmodbus.  Instead of
  54 | passing the context around to all your modbus_xxx functions, you simply
  55 | call them as member functions on the context returned by the new() functions.
  56 | 
  57 | @module libmodbus
  58 | @author Karl Palsson <karlp@etactica.com> 2016-2020
  59 | 
  60 | @license
  61 |   Permission is hereby granted, free of charge, to any person obtaining
  62 |   a copy of this software and associated documentation files (the
  63 |   "Software"), to deal in the Software without restriction, including
  64 |   without limitation the rights to use, copy, modify, merge, publish,
  65 |   distribute, sublicense, and/or sell copies of the Software, and to
  66 |   permit persons to whom the Software is furnished to do so, subject to
  67 |   the following conditions:
  68 | 
  69 |   The above copyright notice and this permission notice shall be
  70 |   included in all copies or substantial portions of the Software.
  71 | 
  72 |   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  73 |   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  74 |   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  75 |   IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  76 |   CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  77 |   TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  78 |   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  79 | 
  80 | */
  81 | 
  82 | #define _POSIX_C_SOURCE 200809L
  83 | 
  84 | #include <stdbool.h>
  85 | #include <stdio.h>
  86 | #include <stdlib.h>
  87 | #include <string.h>
  88 | #include <errno.h>
  89 | #include <assert.h>
  90 | #include <sys/time.h>
  91 | 
  92 | #if defined(WIN32)
  93 | #include <winsock2.h>
  94 | #endif
  95 | 
  96 | #include <lua.h>
  97 | #include <lualib.h>
  98 | #include <lauxlib.h>
  99 | 
 100 | #include <modbus/modbus.h>
 101 | 
 102 | #include "compat.h"
 103 | 
 104 | /* unique naming for userdata metatables */
 105 | #define MODBUS_META_CTX	"modbus.ctx"
 106 | 
 107 | typedef struct {
 108 | 	lua_State *L;
 109 | 	modbus_t *modbus;
 110 | 	size_t max_len;
 111 | 
 112 | 	/* only used for making tostring */
 113 | 	char *dev_host;
 114 | 	char *service;
 115 | 	int baud;
 116 | 	char parity;
 117 | 	int databits;
 118 | 	int stopbits;
 119 | 
 120 | 	/* used to prevent using tcp methods on a rtu context */
 121 | 	bool is_rtu;
 122 | } ctx_t;
 123 | 
 124 | /*
 125 |  * Pushes either "true" or "nil, errormessage"
 126 |  * @param L
 127 |  * @param rc rc from modbus_xxxx function call
 128 |  * @param expected what rc was meant to be
 129 |  * @return the count of stack elements pushed
 130 |  */
 131 | static int libmodbus_rc_to_nil_error(lua_State *L, int rc, int expected)
 132 | {
 133 | 	if (rc == expected) {
 134 | 		lua_pushboolean(L, true);
 135 | 		return 1;
 136 | 	} else {
 137 | 		lua_pushnil(L);
 138 | 		// TODO - insert the integer errno code here too?
 139 | 		lua_pushstring(L, modbus_strerror(errno));
 140 | 		return 2;
 141 | 	}
 142 | }
 143 | 
 144 | /**
 145 |  * Returns the runtime linked version of libmodbus as a string.
 146 |  * The compile time version is available as a constant VERSION.
 147 |  * @function version
 148 |  * @return eg "3.0.6"
 149 |  * @see other_constants
 150 |  */
 151 | static int libmodbus_version(lua_State *L)
 152 | {
 153 | 	char version[16];
 154 | 
 155 | 	snprintf(version, sizeof(version) - 1, "%i.%i.%i",
 156 | 		libmodbus_version_major, libmodbus_version_minor, libmodbus_version_micro);
 157 | 	lua_pushstring(L, version);
 158 | 	return 1;
 159 | }
 160 | 
 161 | /**
 162 |  * Create a Modbus/RTU context
 163 |  * @function new_rtu
 164 |  * @param device (required)
 165 |  * @param baud rate, defaults to 19200
 166 |  * @param parity defaults to EVEN
 167 |  * @param databits defaults to 8
 168 |  * @param stopbits defaults to 1
 169 |  * @return a modbus context ready for use
 170 |  */
 171 | static int libmodbus_new_rtu(lua_State *L)
 172 | {
 173 | 	const char *device = luaL_checkstring(L, 1);
 174 | 	int baud = luaL_optnumber(L, 2, 19200);
 175 | 	const char *parityin = luaL_optstring(L, 3, "EVEN");
 176 | 	int databits = luaL_optnumber(L, 4, 8);
 177 | 	int stopbits = luaL_optnumber(L, 5, 1);
 178 | 
 179 | 	/* just accept baud as is */
 180 | 	/* parity must be one of a few things... */
 181 | 	char parity;
 182 | 	switch (parityin[0]) {
 183 | 	case 'e':
 184 | 	case 'E':
 185 | 		parity = 'E';
 186 | 		break;
 187 | 	case 'n':
 188 | 	case 'N':
 189 | 		parity = 'N';
 190 | 		break;
 191 | 	case 'o':
 192 | 	case 'O':
 193 | 		parity = 'O';
 194 | 		break;
 195 | 	default:
 196 | 		return luaL_argerror(L, 3, "Unrecognised parity");
 197 | 	}
 198 | 
 199 | 	ctx_t *ctx = (ctx_t *) lua_newuserdata(L, sizeof(ctx_t));
 200 | 
 201 | 	ctx->modbus = modbus_new_rtu(device, baud, parity, databits, stopbits);
 202 | 	ctx->max_len = MODBUS_RTU_MAX_ADU_LENGTH;
 203 | 	ctx->is_rtu = true;
 204 | 
 205 | 	if (ctx->modbus == NULL) {
 206 | 		return luaL_error(L, modbus_strerror(errno));
 207 | 	}
 208 | 
 209 | 	ctx->L = L;
 210 | 
 211 | 	/* save data for nice string representations */
 212 | 	ctx->baud = baud;
 213 | 	ctx->databits = databits;
 214 | 	ctx->dev_host = strdup(device);
 215 | 	ctx->parity = parity;
 216 | 	ctx->stopbits = stopbits;
 217 | 
 218 | 	/* Make sure unused fields are zeroed */
 219 | 	ctx->service = NULL;
 220 | 
 221 | 	luaL_getmetatable(L, MODBUS_META_CTX);
 222 | 	// Can I put more functions in for rtu here? maybe?
 223 | 	lua_setmetatable(L, -2);
 224 | 
 225 | 	return 1;
 226 | }
 227 | 
 228 | 
 229 | /**
 230 |  * Create a Modbus/TCP context
 231 |  * @function new_tcp_pi
 232 |  * @param host eg "192.168.1.100" or "modbus.example.org"
 233 |  * @param service eg "502" or "mbap"
 234 |  * @return a modbus context ready for use
 235 |  */
 236 | static int libmodbus_new_tcp_pi(lua_State *L)
 237 | {
 238 | 	const char *host = luaL_checkstring(L, 1);
 239 | 	const char *service = luaL_checkstring(L, 2);
 240 | 
 241 | 	// Do we really need any of this? no callbacks in libmodbus?
 242 | 	ctx_t *ctx = (ctx_t *) lua_newuserdata(L, sizeof(ctx_t));
 243 | 
 244 | 	ctx->modbus = modbus_new_tcp_pi(host, service);
 245 | 	ctx->max_len = MODBUS_TCP_MAX_ADU_LENGTH;
 246 | 	ctx->is_rtu = false;
 247 | 
 248 | 	if (ctx->modbus == NULL) {
 249 | 		return luaL_error(L, modbus_strerror(errno));
 250 | 	}
 251 | 
 252 | 	ctx->L = L;
 253 | 
 254 | 	/* save data for nice string representations */
 255 | 	ctx->dev_host = strdup(host);
 256 | 	ctx->service = strdup(service);
 257 | 	ctx->databits = 0;
 258 | 
 259 | 	luaL_getmetatable(L, MODBUS_META_CTX);
 260 | 	// Can I put more functions in for tcp here? maybe?
 261 | 	lua_setmetatable(L, -2);
 262 | 
 263 | 	return 1;
 264 | }
 265 | 
 266 | /** Write a 32bit (u)int to 2x16bit registers
 267 |  * @function set_s32
 268 |  * @param num 32bit number
 269 |  * @return reg1 upper 16bits
 270 |  * @return reg2 lower 16bits
 271 |  */
 272 | static int helper_set_s32(lua_State *L)
 273 | {
 274 | 	/* truncate as necessary */
 275 | 	//const uint32_t toval = (uint32_t)luaL_checknumber(L, 1);
 276 | 	lua_Number n = luaL_checknumber(L, 1);
 277 | 	int32_t toval = (int32_t)n;
 278 | 	lua_pushinteger(L, toval >> 16);
 279 | 	lua_pushinteger(L, toval & 0xffff);
 280 | 	return 2;
 281 | }
 282 | 
 283 | /** Write a bit float to 2x16bit registers
 284 |  * @function set_f32
 285 |  * @param num floating point number
 286 |  * @return reg1 upper 16bits of a 32bit float
 287 |  * @return reg2 lower 16bits of a 32bit float
 288 |  */
 289 | static int helper_set_f32(lua_State *L)
 290 | {
 291 | 	/* truncate to float if necessary */
 292 | 	const float in = (float)luaL_checknumber(L, 1);
 293 | 	uint32_t out;
 294 | 	memcpy(&out, &in, sizeof(out));
 295 | 	lua_pushinteger(L, out >> 16);
 296 | 	lua_pushinteger(L, out & 0xffff);
 297 | 	return 2;
 298 | }
 299 | 
 300 | 
 301 | /**
 302 |  * 16bit register as number to signed 16bit
 303 |  * @function get_s16
 304 |  * @param 1 16bit register
 305 |  * @return signed 16bit number
 306 |  */
 307 | static int helper_get_s16(lua_State *L)
 308 | {
 309 | 	const int16_t in = luaL_checknumber(L, 1);
 310 | 	lua_pushinteger(L, in);
 311 | 	return 1;
 312 | }
 313 | 
 314 | /**
 315 |  * 2x16bit registers as number to signed 32 bit
 316 |  * @function get_s32
 317 |  * @param 1 16bit register
 318 |  * @param 2 16bit register
 319 |  * @return 32bit number
 320 |  */
 321 | static int helper_get_s32(lua_State *L)
 322 | {
 323 | 	const uint16_t in1 = luaL_checknumber(L, 1);
 324 | 	const uint16_t in2 = luaL_checknumber(L, 2);
 325 | 	int32_t out = in1 << 16 | in2;
 326 | 	lua_pushinteger(L, out);
 327 | 	return 1;
 328 | }
 329 | 
 330 | /**
 331 |  * 2x16bit registers as number to signed 32 bit (reverse order)
 332 |  * @function get_s32le
 333 |  * @param 1 16bit register
 334 |  * @param 2 16bit register
 335 |  * @return 32bit number
 336 |  */
 337 | static int helper_get_s32le(lua_State *L)
 338 | {
 339 | 	const uint16_t in2 = luaL_checknumber(L, 1);
 340 | 	const uint16_t in1 = luaL_checknumber(L, 2);
 341 | 	int32_t out = in1 << 16 | in2;
 342 | 	lua_pushinteger(L, out);
 343 | 	return 1;
 344 | }
 345 | 
 346 | 
 347 | /**
 348 |  * 2x16bit registers as number to unsigned 32 bit
 349 |  * @function get_u32
 350 |  * @param 1 16bit register
 351 |  * @param 2 16bit register
 352 |  * @return 32bit number
 353 |  */
 354 | static int helper_get_u32(lua_State *L)
 355 | {
 356 | 	const uint16_t in1 = luaL_checknumber(L, 1);
 357 | 	const uint16_t in2 = luaL_checknumber(L, 2);
 358 | 	uint32_t out = in1 << 16 | in2;
 359 | 	lua_pushnumber(L, out);
 360 | 	return 1;
 361 | }
 362 | 
 363 | /**
 364 |  * 2x16bit registers as number to unsigned 32 bit (reversed order)
 365 |  * @function get_u32le
 366 |  * @param 1 16bit register
 367 |  * @param 2 16bit register
 368 |  * @return 32bit number
 369 |  */
 370 | static int helper_get_u32le(lua_State *L)
 371 | {
 372 | 	const uint16_t in2 = luaL_checknumber(L, 1);
 373 | 	const uint16_t in1 = luaL_checknumber(L, 2);
 374 | 	uint32_t out = in1 << 16 | in2;
 375 | 	lua_pushnumber(L, out);
 376 | 	return 1;
 377 | }
 378 | 
 379 | /**
 380 |  * 2x16bit registers as number to 32 bit float
 381 |  * @function get_f32
 382 |  * @param 1 16bit register
 383 |  * @param 2 16bit register
 384 |  * @return 32bit float
 385 |  */
 386 | static int helper_get_f32(lua_State *L)
 387 | {
 388 | 	const uint16_t in1 = luaL_checknumber(L, 1);
 389 | 	const uint16_t in2 = luaL_checknumber(L, 2);
 390 | 
 391 | 	uint32_t inval = in1<<16 | in2;
 392 | 	float f;
 393 | 	memcpy(&f, &inval, sizeof(f));
 394 | 
 395 | 	lua_pushnumber(L, f);
 396 | 	return 1;
 397 | }
 398 | 
 399 | /**
 400 |  * 2x16bit registers as number to 32 bit float (Reversed order)
 401 |  * @function get_f32le
 402 |  * @param 1 16bit register
 403 |  * @param 2 16bit register
 404 |  * @return 32bit float
 405 |  */
 406 | static int helper_get_f32le(lua_State *L)
 407 | {
 408 | 	const uint16_t in2 = luaL_checknumber(L, 1);
 409 | 	const uint16_t in1 = luaL_checknumber(L, 2);
 410 | 
 411 | 	uint32_t inval = in1<<16 | in2;
 412 | 	float f;
 413 | 	memcpy(&f, &inval, sizeof(f));
 414 | 
 415 | 	lua_pushnumber(L, f);
 416 | 	return 1;
 417 | }
 418 | 
 419 | /**
 420 |  * 4x16bit registers as number to signed 64 bit
 421 |  * @function get_s64
 422 |  * @param 1 16bit register
 423 |  * @param 2 16bit register
 424 |  * @param 3 16bit register
 425 |  * @param 4 16bit register
 426 |  * @return 64bit number
 427 |  */
 428 | static int helper_get_s64(lua_State *L)
 429 | {
 430 | 	const uint16_t in1 = luaL_checknumber(L, 1);
 431 | 	const uint16_t in2 = luaL_checknumber(L, 2);
 432 | 	const uint16_t in3 = luaL_checknumber(L, 3);
 433 | 	const uint16_t in4 = luaL_checknumber(L, 4);
 434 | 	int64_t out = in1;
 435 | 	out = out << 16 | in2;
 436 | 	out = out << 16 | in3;
 437 | 	out = out << 16 | in4;
 438 | 	lua_pushnumber(L, out);
 439 | 	return 1;
 440 | }
 441 | 
 442 | /**
 443 |  * 4x16bit registers as number to unsigned 64 bit
 444 |  * @function get_u64
 445 |  * @param 1 16bit register
 446 |  * @param 2 16bit register
 447 |  * @param 3 16bit register
 448 |  * @param 4 16bit register
 449 |  * @return 64bit number
 450 |  */
 451 | static int helper_get_u64(lua_State *L)
 452 | {
 453 | 	const uint16_t in1 = luaL_checknumber(L, 1);
 454 | 	const uint16_t in2 = luaL_checknumber(L, 2);
 455 | 	const uint16_t in3 = luaL_checknumber(L, 3);
 456 | 	const uint16_t in4 = luaL_checknumber(L, 4);
 457 | 	uint64_t out = (uint64_t)in1 << 48 | (uint64_t)in2 << 32 | (uint64_t)in3 << 16 | in4;
 458 | 	lua_pushnumber(L, out);
 459 | 	return 1;
 460 | }
 461 | 
 462 | 
 463 | static ctx_t * ctx_check(lua_State *L, int i)
 464 | {
 465 | 	return (ctx_t *) luaL_checkudata(L, i, MODBUS_META_CTX);
 466 | }
 467 | 
 468 | static int ctx_destroy(lua_State *L)
 469 | {
 470 | 	ctx_t *ctx = ctx_check(L, 1);
 471 | 	modbus_close(ctx->modbus);
 472 | 	modbus_free(ctx->modbus);
 473 | 	if (ctx->dev_host) {
 474 | 		free(ctx->dev_host);
 475 | 	}
 476 | 	if (ctx->service) {
 477 | 		free(ctx->service);
 478 | 	}
 479 | 
 480 | 	/* remove all methods operating on ctx */
 481 | 	lua_newtable(L);
 482 | 	lua_setmetatable(L, -2);
 483 | 
 484 | 	/* Nothing to return on stack */
 485 | 	return 0;
 486 | }
 487 | 
 488 | static int ctx_tostring(lua_State *L)
 489 | {
 490 | 	ctx_t *ctx = ctx_check(L, 1);
 491 | 
 492 | 	if (ctx->databits) {
 493 | 		lua_pushfstring(L, "ModbusRTU<%s@%d/%c%d>", ctx->dev_host, ctx->databits, ctx->parity, ctx->stopbits);
 494 | 	} else {
 495 | 		lua_pushfstring(L, "ModbusTCP<%s@%s>", ctx->dev_host, ctx->service);
 496 | 	}
 497 | 	return 1;
 498 | }
 499 | 
 500 | /** Context Methods.
 501 |  * These functions are members of a modbus context, from either new_rtu() or new_tcp_pi()
 502 |  * @section context_methods
 503 |  */
 504 | 
 505 | /**
 506 |  * Connect, see modbus_connect()
 507 |  * @function ctx:connect
 508 |  */
 509 | static int ctx_connect(lua_State *L)
 510 | {
 511 | 	ctx_t *ctx = ctx_check(L, 1);
 512 | 
 513 | 	int rc = modbus_connect(ctx->modbus);
 514 | 
 515 | 	return libmodbus_rc_to_nil_error(L, rc, 0);
 516 | }
 517 | 
 518 | /**
 519 |  * Close, see modbus_close()
 520 |  * @function ctx:close
 521 |  */
 522 | static int ctx_close(lua_State *L)
 523 | {
 524 | 	ctx_t *ctx = ctx_check(L, 1);
 525 | 
 526 | 	modbus_close(ctx->modbus);
 527 | 
 528 | 	return 0;
 529 | }
 530 | 
 531 | /**
 532 |  * Set debug
 533 |  * @function ctx:set_debug
 534 |  * @param enable optional bool, defaults to true
 535 |  */
 536 | static int ctx_set_debug(lua_State *L)
 537 | {
 538 | 	ctx_t *ctx = ctx_check(L, 1);
 539 | 	bool opt;
 540 | 	if (lua_isnil(L, -1)) {
 541 | 		opt = true;
 542 | 	} else {
 543 | 		opt = lua_toboolean(L, -1);
 544 | 	}
 545 | 	modbus_set_debug(ctx->modbus, opt);
 546 | 	return 0;
 547 | }
 548 | 
 549 | /**
 550 |  * Set error recovery, see modbus_set_error_recovery.
 551 |  * The arguments will be or'd together.
 552 |  * @function ctx:set_error_recovery
 553 |  * @param a one of error_recovery_constants
 554 |  * @param b one of error_recovery_constants
 555 |  */
 556 | static int ctx_set_error_recovery(lua_State *L)
 557 | {
 558 | 	ctx_t *ctx = ctx_check(L, 1);
 559 | 	int opt = luaL_checkinteger(L, 2);
 560 | 	int opt2 = luaL_optinteger(L, 3, 0);
 561 | 
 562 | 	int rc = modbus_set_error_recovery(ctx->modbus, opt | opt2);
 563 | 
 564 | 	return libmodbus_rc_to_nil_error(L, rc, 0);
 565 | }
 566 | 
 567 | /**
 568 |  * See also modbus_set_byte_timeout
 569 |  * @function ctx:set_byte_timeout
 570 |  * @param seconds (required)
 571 |  * @param microseconds (optional, defaults to 0)
 572 |  */
 573 | static int ctx_set_byte_timeout(lua_State *L)
 574 | {
 575 | 	ctx_t *ctx = ctx_check(L, 1);
 576 | 	int opt = luaL_checkinteger(L, 2);
 577 | 	int opt2 = luaL_optinteger(L, 3, 0);
 578 | 
 579 | #if LIBMODBUS_VERSION_CHECK(3,1,0)
 580 | 	modbus_set_byte_timeout(ctx->modbus, opt, opt2);
 581 | #else
 582 | 	struct timeval t = { opt, opt2 };
 583 | 	modbus_set_byte_timeout(ctx->modbus, &t);
 584 | #endif
 585 | 
 586 | 	return 0;
 587 | }
 588 | 
 589 | /**
 590 |  * @function ctx:get_byte_timeout
 591 |  * @return[1] seconds
 592 |  * @return[1] microseconds
 593 |  */
 594 | static int ctx_get_byte_timeout(lua_State *L)
 595 | {
 596 | 	ctx_t *ctx = ctx_check(L, 1);
 597 | 	uint32_t opt1, opt2;
 598 | 
 599 | #if LIBMODBUS_VERSION_CHECK(3,1,0)
 600 | 	modbus_get_byte_timeout(ctx->modbus, &opt1, &opt2);
 601 | #else
 602 | 	struct timeval t;
 603 | 	modbus_get_byte_timeout(ctx->modbus, &t);
 604 | 	opt1 = t.tv_sec;
 605 | 	opt2 = t.tv_usec;
 606 | #endif
 607 | 	lua_pushnumber(L, opt1);
 608 | 	lua_pushnumber(L, opt2);
 609 | 
 610 | 	return 2;
 611 | }
 612 | 
 613 | /**
 614 |  * @function ctx:set_response_timeout
 615 |  * @param seconds (required)
 616 |  * @param microseconds (optional, defaults to 0)
 617 |  */
 618 | static int ctx_set_response_timeout(lua_State *L)
 619 | {
 620 | 	ctx_t *ctx = ctx_check(L, 1);
 621 | 	int opt = luaL_checkinteger(L, 2);
 622 | 	int opt2 = luaL_optinteger(L, 3, 0);
 623 | 
 624 | #if LIBMODBUS_VERSION_CHECK(3,1,0)
 625 | 	modbus_set_response_timeout(ctx->modbus, opt, opt2);
 626 | #else
 627 | 	struct timeval t = { opt, opt2 };
 628 | 	modbus_set_response_timeout(ctx->modbus, &t);
 629 | #endif
 630 | 
 631 | 	return 0;
 632 | }
 633 | 
 634 | /**
 635 |  * @function ctx:get_response_timeout
 636 |  * @return[1] seconds
 637 |  * @return[1] microseconds
 638 |  */
 639 | static int ctx_get_response_timeout(lua_State *L)
 640 | {
 641 | 	ctx_t *ctx = ctx_check(L, 1);
 642 | 	uint32_t opt1, opt2;
 643 | 
 644 | #if LIBMODBUS_VERSION_CHECK(3,1,0)
 645 | 	modbus_get_response_timeout(ctx->modbus, &opt1, &opt2);
 646 | #else
 647 | 	struct timeval t;
 648 | 	modbus_get_response_timeout(ctx->modbus, &t);
 649 | 	opt1 = t.tv_sec;
 650 | 	opt2 = t.tv_usec;
 651 | #endif
 652 | 	lua_pushnumber(L, opt1);
 653 | 	lua_pushnumber(L, opt2);
 654 | 
 655 | 	return 2;
 656 | }
 657 | 
 658 | /**
 659 |  * @function ctx:get_socket
 660 |  * @return the socket number
 661 |  */
 662 | static int ctx_get_socket(lua_State *L)
 663 | {
 664 | 	ctx_t *ctx = ctx_check(L, 1);
 665 | 
 666 | 	lua_pushinteger(L, modbus_get_socket(ctx->modbus));
 667 | 
 668 | 	return 1;
 669 | }
 670 | 
 671 | /**
 672 |  * @function ctx:set_socket
 673 |  * @param sock integer socket number to set
 674 |  */
 675 | static int ctx_set_socket(lua_State *L)
 676 | {
 677 | 	ctx_t *ctx = ctx_check(L, 1);
 678 | 	int newfd = luaL_checknumber(L, 2);
 679 | 
 680 | 	modbus_set_socket(ctx->modbus, newfd);
 681 | 
 682 | 	return 0;
 683 | }
 684 | 
 685 | /**
 686 |  * @function ctx:rtu_get_serial_mode
 687 |  * @return rtu_constants the serial mode, either RTU_RS232 or RTU_RS485
 688 |  */
 689 | static int ctx_rtu_get_serial_mode(lua_State *L)
 690 | {
 691 | 	ctx_t *ctx = ctx_check(L, 1);
 692 | 	if (!ctx->is_rtu) {
 693 | 		return luaL_error(L, "Cannot call RTU methods on an TCP context");
 694 | 	}
 695 | 
 696 | 	lua_pushinteger(L, modbus_rtu_get_serial_mode(ctx->modbus));
 697 | 
 698 | 	return 1;
 699 | }
 700 | 
 701 | /**
 702 |  * Sets the mode of a serial port.
 703 |  * Remember, this is only required if your kernel is handling rs485 natively.
 704 |  * If you are using a USB adapter, you do NOT need this.
 705 |  * @function ctx:rtu_set_serial_mode
 706 |  * @param mode The selected serial mode from rtu_constants, either RTU_RS232 or RTU_RS485.
 707 |  */
 708 | static int ctx_rtu_set_serial_mode(lua_State *L)
 709 | {
 710 | 	ctx_t *ctx = ctx_check(L, 1);
 711 | 	if (!ctx->is_rtu) {
 712 | 		return luaL_error(L, "Cannot call RTU methods on an TCP context");
 713 | 	}
 714 | 	int mode = luaL_checknumber(L, 2);
 715 | 
 716 | 	int rc = modbus_rtu_set_serial_mode(ctx->modbus, mode);
 717 | 
 718 | 	return libmodbus_rc_to_nil_error(L, rc, 0);
 719 | }
 720 | 
 721 | /**
 722 |  * @function ctx:rtu_get_rts
 723 |  * @return rtu_rts_constants the RTS handling mode, up/down/none
 724 |  */
 725 | static int ctx_rtu_get_rts(lua_State *L)
 726 | {
 727 | 	ctx_t *ctx = ctx_check(L, 1);
 728 | 	if (!ctx->is_rtu) {
 729 | 		return luaL_error(L, "Cannot call RTU methods on an TCP context");
 730 | 	}
 731 | 
 732 | 	lua_pushinteger(L, modbus_rtu_get_rts(ctx->modbus));
 733 | 
 734 | 	return 1;
 735 | }
 736 | 
 737 | /**
 738 |  * Sets the RTS handling of a serial port.
 739 |  * @function ctx:rtu_set_rts
 740 |  * @param mode The selected RTS handling mode from rtu_rts_constants, up/down/none.
 741 |  */
 742 | static int ctx_rtu_set_rts(lua_State *L)
 743 | {
 744 | 	ctx_t *ctx = ctx_check(L, 1);
 745 | 	if (!ctx->is_rtu) {
 746 | 		return luaL_error(L, "Cannot call RTU methods on an TCP context");
 747 | 	}
 748 | 	int mode = luaL_checknumber(L, 2);
 749 | 
 750 | 	int rc = modbus_rtu_set_rts(ctx->modbus, mode);
 751 | 
 752 | 	return libmodbus_rc_to_nil_error(L, rc, 0);
 753 | }
 754 | 
 755 | /**
 756 |  * @function ctx:rtu_get_rts_delay
 757 |  * @return usecs
 758 |  */
 759 | static int ctx_rtu_get_rts_delay(lua_State *L)
 760 | {
 761 | 	ctx_t *ctx = ctx_check(L, 1);
 762 | 	if (!ctx->is_rtu) {
 763 | 		return luaL_error(L, "Cannot call RTU methods on an TCP context");
 764 | 	}
 765 | 
 766 | 	lua_pushinteger(L, modbus_rtu_get_rts_delay(ctx->modbus));
 767 | 
 768 | 	return 1;
 769 | }
 770 | 
 771 | /**
 772 |  * Sets the RTS delay of a serial port.
 773 |  * @function ctx:rtu_set_rts_delay
 774 |  * @param usecs
 775 |  */
 776 | static int ctx_rtu_set_rts_delay(lua_State *L)
 777 | {
 778 | 	ctx_t *ctx = ctx_check(L, 1);
 779 | 	if (!ctx->is_rtu) {
 780 | 		return luaL_error(L, "Cannot call RTU methods on an TCP context");
 781 | 	}
 782 | 	int usecs = luaL_checknumber(L, 2);
 783 | 
 784 | 	int rc = modbus_rtu_set_rts_delay(ctx->modbus, usecs);
 785 | 
 786 | 	return libmodbus_rc_to_nil_error(L, rc, 0);
 787 | }
 788 | 
 789 | /**
 790 |  * Returns the header length of the transport
 791 |  * @function ctx:get_header_length
 792 |  * @return the length in bytes
 793 |  */
 794 | static int ctx_get_header_length(lua_State *L)
 795 | {
 796 | 	ctx_t *ctx = ctx_check(L, 1);
 797 | 
 798 | 	lua_pushinteger(L, modbus_get_header_length(ctx->modbus));
 799 | 
 800 | 	return 1;
 801 | }
 802 | 
 803 | /**
 804 |  * @function ctx:set_slave
 805 |  * @param unitid the unit address / slave id to use
 806 |  */
 807 | static int ctx_set_slave(lua_State *L)
 808 | {
 809 | 	ctx_t *ctx = ctx_check(L, 1);
 810 | 	int slave = luaL_checknumber(L, 2);
 811 | 
 812 | 	int rc = modbus_set_slave(ctx->modbus, slave);
 813 | 
 814 | 	return libmodbus_rc_to_nil_error(L, rc, 0);
 815 | }
 816 | 
 817 | static int _ctx_read_bits(lua_State *L, bool input)
 818 | {
 819 | 	ctx_t *ctx = ctx_check(L, 1);
 820 | 	int addr = luaL_checknumber(L, 2);
 821 | 	int count = luaL_checknumber(L, 3);
 822 | 	int rcount = 0;
 823 | 	int rc;
 824 | 
 825 | 	if (count > MODBUS_MAX_READ_BITS) {
 826 | 		return luaL_argerror(L, 3, "requested too many bits");
 827 | 	}
 828 | 
 829 | 	uint8_t *buf = malloc(count * sizeof(uint8_t));
 830 | 	assert(buf);
 831 | 	if (input) {
 832 | 		rc = modbus_read_input_bits(ctx->modbus, addr, count, buf);
 833 | 	} else {
 834 | 		rc = modbus_read_bits(ctx->modbus, addr, count, buf);
 835 | 	}
 836 | 
 837 | 	if (rc == count) {
 838 | 		lua_newtable(L);
 839 | 		/* nota bene, lua style offsets! */
 840 | 		for (int i = 1; i <= rc; i++) {
 841 | 			lua_pushnumber(L, i);
 842 | 			/* TODO - push number or push bool? what's a better lua api? */
 843 | 			lua_pushnumber(L, buf[i-1]);
 844 | 			lua_settable(L, -3);
 845 | 		}
 846 | 		rcount = 1;
 847 | 	} else {
 848 | 		rcount = libmodbus_rc_to_nil_error(L, rc, count);
 849 | 	}
 850 | 
 851 | 	free(buf);
 852 | 	return rcount;
 853 | }
 854 | 
 855 | /**
 856 |  * @function ctx:read_input_bits
 857 |  * @param address
 858 |  * @param count
 859 |  * @return an array of results
 860 |  */
 861 | static int ctx_read_input_bits(lua_State *L)
 862 | {
 863 | 	return _ctx_read_bits(L, true);
 864 | }
 865 | 
 866 | /**
 867 |  * @function ctx:read_bits
 868 |  * @param address
 869 |  * @param count
 870 |  * @return an array of results
 871 |  */
 872 | static int ctx_read_bits(lua_State *L)
 873 | {
 874 | 	return _ctx_read_bits(L, false);
 875 | }
 876 | 
 877 | static int _ctx_read_regs(lua_State *L, bool input)
 878 | {
 879 | 	ctx_t *ctx = ctx_check(L, 1);
 880 | 	int addr = luaL_checknumber(L, 2);
 881 | 	int count = luaL_checknumber(L, 3);
 882 | 	int rcount = 0;
 883 | 	int rc;
 884 | 
 885 | 	if (count > MODBUS_MAX_READ_REGISTERS) {
 886 | 		return luaL_argerror(L, 3, "requested too many registers");
 887 | 	}
 888 | 
 889 | 	// better malloc as much space as we need to return data here...
 890 | 	uint16_t *buf = malloc(count * sizeof(uint16_t));
 891 | 	assert(buf);
 892 | 	if (input) {
 893 | 		rc = modbus_read_input_registers(ctx->modbus, addr, count, buf);
 894 | 	} else {
 895 | 		rc = modbus_read_registers(ctx->modbus, addr, count, buf);
 896 | 	}
 897 | 	if (rc == count) {
 898 | 		lua_newtable(L);
 899 | 		/* nota bene, lua style offsets! */
 900 | 		for (int i = 1; i <= rc; i++) {
 901 | 			lua_pushnumber(L, i);
 902 | 			lua_pushnumber(L, buf[i-1]);
 903 | 			lua_settable(L, -3);
 904 | 		}
 905 | 		rcount = 1;
 906 | 	} else {
 907 | 		rcount = libmodbus_rc_to_nil_error(L, rc, count);
 908 | 	}
 909 | 
 910 | 	free(buf);
 911 | 	return rcount;
 912 | }
 913 | 
 914 | /**
 915 |  * @function ctx:read_input_registers
 916 |  * @param address
 917 |  * @param count
 918 |  * @return an array of results
 919 |  */
 920 | static int ctx_read_input_registers(lua_State *L)
 921 | {
 922 | 	return _ctx_read_regs(L, true);
 923 | }
 924 | 
 925 | /**
 926 |  * @function ctx:read_registers
 927 |  * @param address
 928 |  * @param count
 929 |  * @return an array of results
 930 |  */
 931 | static int ctx_read_registers(lua_State *L)
 932 | {
 933 | 	return _ctx_read_regs(L, false);
 934 | }
 935 | 
 936 | /**
 937 |  * @function ctx:report_slave_id
 938 |  * @return a luastring with the raw result (lua strings can contain nulls)
 939 |  */
 940 | static int ctx_report_slave_id(lua_State *L)
 941 | {
 942 | 	ctx_t *ctx = ctx_check(L, 1);
 943 | 
 944 | 	uint8_t *buf = malloc(ctx->max_len);
 945 | 	assert(buf);
 946 | #if LIBMODBUS_VERSION_CHECK(3,1,0)
 947 | 	int rc = modbus_report_slave_id(ctx->modbus, ctx->max_len, buf);
 948 | #else
 949 | 	int rc = modbus_report_slave_id(ctx->modbus, buf);
 950 | #endif
 951 | 	if (rc < 0) {
 952 | 		return libmodbus_rc_to_nil_error(L, rc, 0);
 953 | 	}
 954 | 
 955 | 	lua_pushlstring(L, (char *)buf, rc);
 956 | 	return 1;
 957 | }
 958 | 
 959 | /**
 960 |  * @function ctx:write_bit
 961 |  * @param address
 962 |  * @param value either a number or a boolean
 963 |  */
 964 | static int ctx_write_bit(lua_State *L)
 965 | {
 966 | 	ctx_t *ctx = ctx_check(L, 1);
 967 | 	int addr = luaL_checknumber(L, 2);
 968 | 	int val;
 969 | 
 970 | 	if (lua_type(L, 3) == LUA_TNUMBER) {
 971 | 		val = lua_tonumber(L, 3);
 972 | 	} else if (lua_type(L, 3) == LUA_TBOOLEAN) {
 973 | 		val = lua_toboolean(L, 3);
 974 | 	} else {
 975 | 		return luaL_argerror(L, 3, "bit must be numeric or boolean");
 976 | 	}
 977 | 
 978 | 	int rc = modbus_write_bit(ctx->modbus, addr, val);
 979 | 
 980 | 	return libmodbus_rc_to_nil_error(L, rc, 1);
 981 | }
 982 | 
 983 | /**
 984 |  * @function ctx:write_register
 985 |  * @param address
 986 |  * @param value
 987 |  */
 988 | static int ctx_write_register(lua_State *L)
 989 | {
 990 | 	ctx_t *ctx = ctx_check(L, 1);
 991 | 	int addr = luaL_checknumber(L, 2);
 992 | 	int val = luaL_checknumber(L, 3);
 993 | 
 994 | 	int rc = modbus_write_register(ctx->modbus, addr, val);
 995 | 
 996 | 	return libmodbus_rc_to_nil_error(L, rc, 1);
 997 | }
 998 | 
 999 | 
1000 | /**
1001 |  * @function ctx:write_bits
1002 |  * @param address
1003 |  * @param value as a lua array table
1004 |  */
1005 | static int ctx_write_bits(lua_State *L)
1006 | {
1007 | 	ctx_t *ctx = ctx_check(L, 1);
1008 | 	int addr = luaL_checknumber(L, 2);
1009 | 	int rc;
1010 | 	int rcount;
1011 | 
1012 | 	/*
1013 | 	 * TODO - could allow just a series of arguments too? easier for
1014 | 	 * smaller sets?"?)
1015 | 	 */
1016 | 	luaL_checktype(L, 3, LUA_TTABLE);
1017 | 	/* array style table only! */
1018 | 	int count = lua_rawlen(L, 3);
1019 | 
1020 | 	if (count > MODBUS_MAX_WRITE_BITS) {
1021 | 		return luaL_argerror(L, 3, "requested too many bits");
1022 | 	}
1023 | 
1024 | 	/* Convert table to uint8_t array */
1025 | 	uint8_t *buf = malloc(count * sizeof(uint8_t));
1026 | 	assert(buf);
1027 | 	for (int i = 1; i <= count; i++) {
1028 | 		bool ok = false;
1029 | 		lua_rawgeti(L, 3, i);
1030 | 		if (lua_type(L, -1) == LUA_TNUMBER) {
1031 | 			buf[i-1] = lua_tonumber(L, -1);
1032 | 			ok = true;
1033 | 		}
1034 | 		if (lua_type(L, -1) == LUA_TBOOLEAN) {
1035 | 			buf[i-1] = lua_toboolean(L, -1);
1036 | 			ok = true;
1037 | 		}
1038 | 
1039 | 		if (ok) {
1040 | 			lua_pop(L, 1);
1041 | 		} else {
1042 | 			free(buf);
1043 | 			return luaL_argerror(L, 3, "table values must be numeric or bool");
1044 | 		}
1045 | 	}
1046 | 	rc = modbus_write_bits(ctx->modbus, addr, count, buf);
1047 | 	if (rc == count) {
1048 | 		rcount = 1;
1049 | 		lua_pushboolean(L, true);
1050 | 	} else {
1051 | 		rcount = libmodbus_rc_to_nil_error(L, rc, count);
1052 | 	}
1053 | 
1054 | 	free(buf);
1055 | 	return rcount;
1056 | }
1057 | 
1058 | 
1059 | /**
1060 |  * @function ctx:write_registers
1061 |  * @param address base address to write to
1062 |  * @param value as a lua array table, or a sequence of values.
1063 |  * @usage either
1064 |  *  ctx:write_registers(0x2000, {1,2,3})
1065 |  *  ctx:write_registers(0x2000, 1, 2, 3)
1066 |  */
1067 | static int ctx_write_registers(lua_State *L)
1068 | {
1069 | 	ctx_t *ctx = ctx_check(L, 1);
1070 | 	int addr = luaL_checknumber(L, 2);
1071 | 	int rc;
1072 | 	int rcount;
1073 | 	uint16_t *buf;
1074 | 	int count;
1075 | 
1076 | 	if (lua_type(L, 3) == LUA_TTABLE) {
1077 | 		/* array style table only! */
1078 | 		count = lua_rawlen(L, 3);
1079 | 
1080 | 		if (count > MODBUS_MAX_WRITE_REGISTERS) {
1081 | 			return luaL_argerror(L, 3, "requested too many registers");
1082 | 		}
1083 | 
1084 | 		/* Convert table to uint16_t array */
1085 | 		buf = malloc(count * sizeof(uint16_t));
1086 | 		assert(buf);
1087 | 		for (int i = 1; i <= count; i++) {
1088 | 			lua_rawgeti(L, 3, i);
1089 | 			/* user beware! we're not range checking your values */
1090 | 			if (lua_type(L, -1) != LUA_TNUMBER) {
1091 | 				free(buf);
1092 | 				return luaL_argerror(L, 3, "table values must be numeric yo");
1093 | 			}
1094 | 			/* This preserves sign and fractions better than tointeger() */
1095 | 			lua_Number n = lua_tonumber(L, -1);
1096 | 			buf[i-1] = (int16_t)n;
1097 | 			lua_pop(L, 1);
1098 | 		}
1099 | 	} else {
1100 | 		/* Assume sequence of values then... */
1101 | 		int total_args = lua_gettop(L);
1102 | 		if (total_args < 3) {
1103 | 			return luaL_argerror(L, 3, "No values provided to write!");
1104 | 		}
1105 | 		count = total_args - 2;
1106 | 		buf = malloc(count * sizeof(uint16_t));
1107 | 		assert(buf);
1108 | 		for (int i = 0; i < count; i++) {
1109 | 			buf[i] = (int16_t)lua_tonumber(L, i + 3);
1110 | 		}
1111 | 	}
1112 | 	rc = modbus_write_registers(ctx->modbus, addr, count, buf);
1113 | 	if (rc == count) {
1114 | 		rcount = 1;
1115 | 		lua_pushboolean(L, true);
1116 | 	} else {
1117 | 		rcount = libmodbus_rc_to_nil_error(L, rc, count);
1118 | 	}
1119 | 
1120 | 	free(buf);
1121 | 	return rcount;
1122 | }
1123 | 
1124 | static int ctx_send_raw_request(lua_State *L)
1125 | {
1126 | 	ctx_t *ctx = ctx_check(L, 1);
1127 | 	int rc;
1128 | 	int rcount;
1129 | 	int count = lua_rawlen(L, 2);
1130 | 	luaL_checktype(L, 2, LUA_TTABLE);
1131 | 	/* array style table only! */
1132 | 
1133 | 	/* Convert table to uint8_t array */
1134 | 	uint8_t *buf = malloc(lua_rawlen(L, 2) * sizeof(uint8_t));
1135 | 	assert(buf);
1136 | 	for (int i = 1; i <= count; i++) {
1137 | 		lua_rawgeti(L, 2, i);
1138 | 		/* user beware! we're not range checking your values */
1139 | 		if (lua_type(L, -1) != LUA_TNUMBER) {
1140 | 			free(buf);
1141 | 			return luaL_argerror(L, 2, "table values must be numeric");
1142 | 		}
1143 | 		buf[i-1] = lua_tonumber(L, -1);
1144 | 		lua_pop(L, 1);
1145 | 	};
1146 | 	rc = modbus_send_raw_request(ctx->modbus, buf, count);
1147 | 
1148 | 	if (rc < 0) {
1149 | 		lua_pushnil(L);
1150 | 		lua_pushstring(L, modbus_strerror(errno));
1151 | 		rcount = 2;
1152 | 	} else {
1153 |                 // wait
1154 | 		lua_pushboolean(L, true);
1155 | 		rcount = 1;
1156 | 	}
1157 | 	long wait = lua_tonumber(L,3);
1158 | 	if (wait > 0) {
1159 | 		static struct timeval tim;
1160 | 		tim.tv_sec = 0;
1161 | 		tim.tv_usec = wait;
1162 | 		if (select(0, NULL, NULL, NULL, &tim) < 0) {
1163 | 			lua_pushnil(L);
1164 | 			return 2;
1165 | 		};
1166 | 	}
1167 | 
1168 | 	free(buf);
1169 | 	return rcount;
1170 | 
1171 | }
1172 | 
1173 | /**
1174 |  * @function ctx:tcp_pi_listen
1175 |  * @param conns max connections to listen to, defaults to 1
1176 |  * @return the new socket number or normal error
1177 |  */
1178 | static int ctx_tcp_pi_listen(lua_State *L)
1179 | {
1180 | 	ctx_t *ctx = ctx_check(L, 1);
1181 | 	if (ctx->is_rtu) {
1182 | 		return luaL_error(L, "Cannot call TCP methods on an RTU context");
1183 | 	}
1184 | 	int conns = luaL_optinteger(L, 2, 1);
1185 | 
1186 | 	int sock = modbus_tcp_pi_listen(ctx->modbus, conns);
1187 | 	if (sock == -1) {
1188 | 		return libmodbus_rc_to_nil_error(L, 0, 1);
1189 | 	}
1190 | 	lua_pushnumber(L, sock);
1191 | 
1192 | 	return 1;
1193 | }
1194 | 
1195 | /**
1196 |  * @function ctx:tcp_pi_accept
1197 |  * @param sock the socket we're accepting on
1198 |  * @return the new socket we've accepted or normal error
1199 |  */
1200 | static int ctx_tcp_pi_accept(lua_State *L)
1201 | {
1202 | 	ctx_t *ctx = ctx_check(L, 1);
1203 | 	if (ctx->is_rtu) {
1204 | 		return luaL_error(L, "Cannot call TCP methods on an RTU context");
1205 | 	}
1206 | 	int sock = luaL_checknumber(L, 2);
1207 | 
1208 | 	sock = modbus_tcp_pi_accept(ctx->modbus, &sock);
1209 | 	if (sock == -1) {
1210 | 		return libmodbus_rc_to_nil_error(L, 0, 1);
1211 | 	}
1212 | 	lua_pushnumber(L, sock);
1213 | 
1214 | 	return 1;
1215 | }
1216 | 
1217 | /**
1218 |  * Receives a request from a remote.
1219 |  * WARNING this might not be complete, documented that it exists
1220 |  * @function ctx:receive
1221 |  * @return the length of the data
1222 |  * @return the data received
1223 |  */
1224 | static int ctx_receive(lua_State *L)
1225 | {
1226 | 	ctx_t *ctx = ctx_check(L, 1);
1227 | 	int rcount;
1228 | 
1229 | 	uint8_t *req = malloc(ctx->max_len);
1230 | 	int rc = modbus_receive(ctx->modbus, req);
1231 | 	if (rc > 0) {
1232 | 		lua_pushnumber(L, rc);
1233 | 		lua_pushlstring(L, (char *)req, rc);
1234 | 		rcount = 2;
1235 | 	} else if (rc == 0) {
1236 | 		printf("Special case for rc = 0, can't remember\n");
1237 | 		rcount = 0;
1238 | 	} else {
1239 | 		rcount = libmodbus_rc_to_nil_error(L, rc, 0);
1240 | 	}
1241 | 	return rcount;
1242 | }
1243 | 
1244 | static int ctx_reply(lua_State *L)
1245 | {
1246 | 	ctx_t *ctx = ctx_check(L, 1);
1247 | 	size_t req_len;
1248 | 	const char *req = luaL_checklstring(L, 2, &req_len);
1249 | 
1250 | 	luaL_checktype(L, 3, LUA_TTABLE);
1251 | 
1252 | 	// FIXME - oh boy, probably need a whole lot of wrappers on the mappings?
1253 | 	//modbus_reply(ctx->modbus, (uint8_t*)req, req_len, mapping);
1254 | 	(void)ctx;
1255 | 	(void)req;
1256 | 	return luaL_error(L, "reply is simply unimplemented my friend!");
1257 | }
1258 | 
1259 | /**
1260 |  * Reply to a request with an Exception.
1261 |  * @function ctx:reply_exception
1262 |  * @param initial the initial request to build into an exception
1263 |  * @param exc the exception code to use from exception_codes
1264 |  * @return the new socket number or normal error
1265 |  */
1266 | static int ctx_reply_exception(lua_State *L)
1267 | {
1268 | 	ctx_t *ctx = ctx_check(L, 1);
1269 | 	const char *req = luaL_checklstring(L, 2, NULL);
1270 | 	int exception = luaL_checknumber(L, 3);
1271 | 
1272 | 	int rc = modbus_reply_exception(ctx->modbus, (uint8_t*)req, exception);
1273 | 	if (rc == -1) {
1274 | 		return libmodbus_rc_to_nil_error(L, 0, 1);
1275 | 	} else {
1276 | 		return libmodbus_rc_to_nil_error(L, rc, rc);
1277 | 	}
1278 | }
1279 | 
1280 | 
1281 | struct definei {
1282 |         const char* name;
1283 |         int value;
1284 | };
1285 | 
1286 | struct defines {
1287 |         const char* name;
1288 |         const char* value;
1289 | };
1290 | 
1291 | /** Constants provided for use.
1292 |  * Constants used in various functions, either as arguments or returns
1293 |  * @section constants
1294 |  */
1295 | 
1296 | /**
1297 |  * RTU Mode Constants
1298 |  * @see rtu_get_serial_mode
1299 |  * @see rtu_set_serial_mode
1300 |  * @table rtu_constants
1301 |  * @field RTU_RS232
1302 |  * @field RTU_RS485
1303 |  */
1304 | 
1305 | /**
1306 |  * RTU RTS Consants
1307 |  * @see rtu_set_rts
1308 |  * @see rtu_get_rts
1309 |  * @table rtu_rts_constants
1310 |  * @field RTU_RTS_NONE
1311 |  * @field RTU_RTS_UP
1312 |  * @field RTU_RTS_DOWN
1313 |  */
1314 | 
1315 | /** Error Recovery Constants
1316 |  * @see set_error_recovery
1317 |  * @table error_recovery_constants
1318 |  * @field ERROR_RECOVERY_NONE
1319 |  * @field ERROR_RECOVERY_LINK
1320 |  * @field ERROR_RECOVERY_PROTOCOL
1321 |  */
1322 | 
1323 | /** Exception codes
1324 |  * These are all MODBUS_xxxx upstream in libmodbus.
1325 |  * @table exception_codes
1326 |  * @field EXCEPTION_ILLEGAL_FUNCTION
1327 |  * @field EXCEPTION_ILLEGAL_DATA_ADDRESS
1328 |  * @field EXCEPTION_ILLEGAL_DATA_VALUE
1329 |  * @field EXCEPTION_SLAVE_OR_SERVER_FAILURE
1330 |  * @field EXCEPTION_ACKNOWLEDGE
1331 |  * @field EXCEPTION_SLAVE_OR_SERVER_BUSY
1332 |  * @field EXCEPTION_NEGATIVE_ACKNOWLEDGE
1333 |  * @field EXCEPTION_MEMORY_PARITY
1334 |  * @field EXCEPTION_NOT_DEFINED
1335 |  * @field EXCEPTION_GATEWAY_PATH
1336 |  */
1337 | static const struct definei D[] = {
1338 |         {"RTU_RS232", MODBUS_RTU_RS232},
1339 |         {"RTU_RS485", MODBUS_RTU_RS485},
1340 | 	{"TCP_SLAVE", MODBUS_TCP_SLAVE},
1341 | 	{"BROADCAST_ADDRESS", MODBUS_BROADCAST_ADDRESS},
1342 | 	{"ERROR_RECOVERY_NONE", MODBUS_ERROR_RECOVERY_NONE},
1343 | 	{"ERROR_RECOVERY_LINK", MODBUS_ERROR_RECOVERY_LINK},
1344 | 	{"ERROR_RECOVERY_PROTOCOL", MODBUS_ERROR_RECOVERY_PROTOCOL},
1345 | 	{"EXCEPTION_ILLEGAL_FUNCTION", MODBUS_EXCEPTION_ILLEGAL_FUNCTION},
1346 | 	{"EXCEPTION_ILLEGAL_DATA_ADDRESS", MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS},
1347 | 	{"EXCEPTION_ILLEGAL_DATA_VALUE", MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE},
1348 | 	{"EXCEPTION_SLAVE_OR_SERVER_FAILURE", MODBUS_EXCEPTION_SLAVE_OR_SERVER_FAILURE},
1349 | 	{"EXCEPTION_ACKNOWLEDGE", MODBUS_EXCEPTION_ACKNOWLEDGE},
1350 | 	{"EXCEPTION_SLAVE_OR_SERVER_BUSY", MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY},
1351 | 	{"EXCEPTION_NEGATIVE_ACKNOWLEDGE", MODBUS_EXCEPTION_NEGATIVE_ACKNOWLEDGE},
1352 | 	{"EXCEPTION_MEMORY_PARITY", MODBUS_EXCEPTION_MEMORY_PARITY},
1353 | 	{"EXCEPTION_NOT_DEFINED", MODBUS_EXCEPTION_NOT_DEFINED},
1354 | 	{"EXCEPTION_GATEWAY_PATH", MODBUS_EXCEPTION_GATEWAY_PATH},
1355 | 	{"EXCEPTION_GATEWAY_TARGET", MODBUS_EXCEPTION_GATEWAY_TARGET},
1356 | 	{"RTU_RTS_NONE", MODBUS_RTU_RTS_NONE},
1357 | 	{"RTU_RTS_UP", MODBUS_RTU_RTS_UP},
1358 | 	{"RTU_RTS_DOWN", MODBUS_RTU_RTS_DOWN},
1359 |         {NULL, 0}
1360 | };
1361 | 
1362 | /** Other constants
1363 |  * @table other_constants
1364 |  * @field VERSION_STRING the <em>compile time</em> version, see also version
1365 |  * @field BROADCAST_ADDRESS used in set_slave
1366 |  * @field TCP_SLAVE used in set_slave
1367 |  */
1368 | static const struct defines S[] = {
1369 | 	{"VERSION_STRING", LIBMODBUS_VERSION_STRING},
1370 |         {NULL, NULL}
1371 | };
1372 | 
1373 | static void modbus_register_defs(lua_State *L, const struct definei *D, const struct defines *S)
1374 | {
1375 |         while (D->name != NULL) {
1376 |                 lua_pushinteger(L, D->value);
1377 |                 lua_setfield(L, -2, D->name);
1378 |                 D++;
1379 |         }
1380 |         while (S->name != NULL) {
1381 |                 lua_pushstring(L, S->value);
1382 |                 lua_setfield(L, -2, S->name);
1383 |                 S++;
1384 |         }
1385 | }
1386 | 
1387 | 
1388 | static const struct luaL_Reg R[] = {
1389 | 	{"new_rtu",	libmodbus_new_rtu},
1390 | 	{"new_tcp_pi",	libmodbus_new_tcp_pi},
1391 | 	{"version",	libmodbus_version},
1392 | 
1393 | 	{"set_s32",	helper_set_s32},
1394 | 	{"set_f32",	helper_set_f32},
1395 | 
1396 | 	{"get_s16",	helper_get_s16},
1397 | 	{"get_s32",	helper_get_s32},
1398 | 	{"get_s32le",	helper_get_s32le},
1399 | 	{"get_u32",	helper_get_u32},
1400 | 	{"get_u32le",	helper_get_u32le},
1401 | 	{"get_f32",	helper_get_f32},
1402 | 	{"get_f32le",	helper_get_f32le},
1403 | 	{"get_s64",	helper_get_s64},
1404 | 	{"get_u64",	helper_get_u64},
1405 | 	/* {"get_u16",	helper_get_u16}, Not normally useful, just use the number as it was returned */
1406 | 
1407 | 	{NULL, NULL}
1408 | };
1409 | 
1410 | static const struct luaL_Reg ctx_M[] = {
1411 | 	{"connect",		ctx_connect},
1412 | 	{"close",		ctx_close},
1413 | 	{"destroy",		ctx_destroy},
1414 | 	{"get_socket",		ctx_get_socket},
1415 | 	{"get_byte_timeout",	ctx_get_byte_timeout},
1416 | 	{"get_header_length",	ctx_get_header_length},
1417 | 	{"get_response_timeout",ctx_get_response_timeout},
1418 | 	{"read_bits",		ctx_read_bits},
1419 | 	{"read_input_bits",	ctx_read_input_bits},
1420 | 	{"read_input_registers",ctx_read_input_registers},
1421 | 	{"read_registers",	ctx_read_registers},
1422 | 	{"report_slave_id",	ctx_report_slave_id},
1423 | 	{"set_debug",		ctx_set_debug},
1424 | 	{"set_byte_timeout",	ctx_set_byte_timeout},
1425 | 	{"set_error_recovery",	ctx_set_error_recovery},
1426 | 	{"set_response_timeout",ctx_set_response_timeout},
1427 | 	{"set_slave",		ctx_set_slave},
1428 | 	{"set_socket",		ctx_set_socket},
1429 | 	{"write_bit",		ctx_write_bit},
1430 | 	{"write_bits",		ctx_write_bits},
1431 | 	{"write_register",	ctx_write_register},
1432 | 	{"write_registers",	ctx_write_registers},
1433 | 	{"send_raw_request",	ctx_send_raw_request},
1434 | 	{"__gc",		ctx_destroy},
1435 | 	{"__tostring",		ctx_tostring},
1436 | 
1437 | 	{"tcp_pi_listen",	ctx_tcp_pi_listen},
1438 | 	{"tcp_pi_accept",	ctx_tcp_pi_accept},
1439 | 
1440 | 	{"rtu_get_serial_mode",	ctx_rtu_get_serial_mode},
1441 | 	{"rtu_set_serial_mode",	ctx_rtu_set_serial_mode},
1442 | 	{"rtu_get_rts",		ctx_rtu_get_rts},
1443 | 	{"rtu_set_rts",		ctx_rtu_set_rts},
1444 | 	{"rtu_get_rts_delay",	ctx_rtu_get_rts_delay},
1445 | 	{"rtu_set_rts_delay",	ctx_rtu_set_rts_delay},
1446 | 
1447 | 	{"receive",		ctx_receive},
1448 | 	{"reply",		ctx_reply}, /* Totally busted */
1449 | 	{"reply_exception",	ctx_reply_exception},
1450 | 
1451 | 	{NULL, NULL}
1452 | };
1453 | 
1454 | int luaopen_libmodbus(lua_State *L)
1455 | {
1456 | 
1457 | #ifdef LUA_ENVIRONINDEX
1458 | 	/* set private environment for this module */
1459 | 	lua_newtable(L);
1460 | 	lua_replace(L, LUA_ENVIRONINDEX);
1461 | #endif
1462 | 
1463 | 	/* metatable.__index = metatable */
1464 | 	luaL_newmetatable(L, MODBUS_META_CTX);
1465 | 	lua_pushvalue(L, -1);
1466 | 	lua_setfield(L, -2, "__index");
1467 | 	luaL_setfuncs(L, ctx_M, 0);
1468 | 
1469 | 	luaL_newlib(L, R);
1470 | 
1471 | 	modbus_register_defs(L, D, S);
1472 | 
1473 | 	return 1;
1474 | }
1475 | 1476 | 1477 |
1478 |
1479 |
1480 | generated by LDoc 1.4.6 1481 | Last updated 2022-12-02 11:44:08 1482 |
1483 |
1484 | 1485 | 1486 | -------------------------------------------------------------------------------- /lua-libmodbus.c: -------------------------------------------------------------------------------- 1 | /*** lua bindings to libmodbus. 2 |

Generally, this provides a very thin layer over libmodbus. Instead of 3 | passing the context around to all your modbus_xxx functions, you simply 4 | call them as member functions on the context returned by the new() functions. 5 | 6 | @module libmodbus 7 | @author Karl Palsson 2016-2020 8 | 9 | @license 10 | Permission is hereby granted, free of charge, to any person obtaining 11 | a copy of this software and associated documentation files (the 12 | "Software"), to deal in the Software without restriction, including 13 | without limitation the rights to use, copy, modify, merge, publish, 14 | distribute, sublicense, and/or sell copies of the Software, and to 15 | permit persons to whom the Software is furnished to do so, subject to 16 | the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be 19 | included in all copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 24 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 25 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 26 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 27 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | 29 | */ 30 | 31 | #define _POSIX_C_SOURCE 200809L 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #if defined(WIN32) 42 | #include 43 | #endif 44 | 45 | #include 46 | #include 47 | #include 48 | 49 | #include 50 | 51 | #include "compat.h" 52 | 53 | /* unique naming for userdata metatables */ 54 | #define MODBUS_META_CTX "modbus.ctx" 55 | 56 | typedef struct { 57 | lua_State *L; 58 | modbus_t *modbus; 59 | size_t max_len; 60 | 61 | /* only used for making tostring */ 62 | char *dev_host; 63 | char *service; 64 | int baud; 65 | char parity; 66 | int databits; 67 | int stopbits; 68 | 69 | /* used to prevent using tcp methods on a rtu context */ 70 | bool is_rtu; 71 | } ctx_t; 72 | 73 | /* 74 | * Pushes either "true" or "nil, errormessage" 75 | * @param L 76 | * @param rc rc from modbus_xxxx function call 77 | * @param expected what rc was meant to be 78 | * @return the count of stack elements pushed 79 | */ 80 | static int libmodbus_rc_to_nil_error(lua_State *L, int rc, int expected) 81 | { 82 | if (rc == expected) { 83 | lua_pushboolean(L, true); 84 | return 1; 85 | } else { 86 | lua_pushnil(L); 87 | // TODO - insert the integer errno code here too? 88 | lua_pushstring(L, modbus_strerror(errno)); 89 | return 2; 90 | } 91 | } 92 | 93 | /** 94 | * Returns the runtime linked version of libmodbus as a string. 95 | * The compile time version is available as a constant VERSION. 96 | * @function version 97 | * @return eg "3.0.6" 98 | * @see other_constants 99 | */ 100 | static int libmodbus_version(lua_State *L) 101 | { 102 | char version[16]; 103 | 104 | snprintf(version, sizeof(version) - 1, "%i.%i.%i", 105 | libmodbus_version_major, libmodbus_version_minor, libmodbus_version_micro); 106 | lua_pushstring(L, version); 107 | return 1; 108 | } 109 | 110 | /** 111 | * Create a Modbus/RTU context 112 | * @function new_rtu 113 | * @param device (required) 114 | * @param baud rate, defaults to 19200 115 | * @param parity defaults to EVEN 116 | * @param databits defaults to 8 117 | * @param stopbits defaults to 1 118 | * @return a modbus context ready for use 119 | */ 120 | static int libmodbus_new_rtu(lua_State *L) 121 | { 122 | const char *device = luaL_checkstring(L, 1); 123 | int baud = luaL_optnumber(L, 2, 19200); 124 | const char *parityin = luaL_optstring(L, 3, "EVEN"); 125 | int databits = luaL_optnumber(L, 4, 8); 126 | int stopbits = luaL_optnumber(L, 5, 1); 127 | 128 | /* just accept baud as is */ 129 | /* parity must be one of a few things... */ 130 | char parity; 131 | switch (parityin[0]) { 132 | case 'e': 133 | case 'E': 134 | parity = 'E'; 135 | break; 136 | case 'n': 137 | case 'N': 138 | parity = 'N'; 139 | break; 140 | case 'o': 141 | case 'O': 142 | parity = 'O'; 143 | break; 144 | default: 145 | return luaL_argerror(L, 3, "Unrecognised parity"); 146 | } 147 | 148 | ctx_t *ctx = (ctx_t *) lua_newuserdata(L, sizeof(ctx_t)); 149 | 150 | ctx->modbus = modbus_new_rtu(device, baud, parity, databits, stopbits); 151 | ctx->max_len = MODBUS_RTU_MAX_ADU_LENGTH; 152 | ctx->is_rtu = true; 153 | 154 | if (ctx->modbus == NULL) { 155 | return luaL_error(L, modbus_strerror(errno)); 156 | } 157 | 158 | ctx->L = L; 159 | 160 | /* save data for nice string representations */ 161 | ctx->baud = baud; 162 | ctx->databits = databits; 163 | ctx->dev_host = strdup(device); 164 | ctx->parity = parity; 165 | ctx->stopbits = stopbits; 166 | 167 | /* Make sure unused fields are zeroed */ 168 | ctx->service = NULL; 169 | 170 | luaL_getmetatable(L, MODBUS_META_CTX); 171 | // Can I put more functions in for rtu here? maybe? 172 | lua_setmetatable(L, -2); 173 | 174 | return 1; 175 | } 176 | 177 | 178 | /** 179 | * Create a Modbus/TCP context 180 | * @function new_tcp_pi 181 | * @param host eg "192.168.1.100" or "modbus.example.org" 182 | * @param service eg "502" or "mbap" 183 | * @return a modbus context ready for use 184 | */ 185 | static int libmodbus_new_tcp_pi(lua_State *L) 186 | { 187 | const char *host = luaL_checkstring(L, 1); 188 | const char *service = luaL_checkstring(L, 2); 189 | 190 | // Do we really need any of this? no callbacks in libmodbus? 191 | ctx_t *ctx = (ctx_t *) lua_newuserdata(L, sizeof(ctx_t)); 192 | 193 | ctx->modbus = modbus_new_tcp_pi(host, service); 194 | ctx->max_len = MODBUS_TCP_MAX_ADU_LENGTH; 195 | ctx->is_rtu = false; 196 | 197 | if (ctx->modbus == NULL) { 198 | return luaL_error(L, modbus_strerror(errno)); 199 | } 200 | 201 | ctx->L = L; 202 | 203 | /* save data for nice string representations */ 204 | ctx->dev_host = strdup(host); 205 | ctx->service = strdup(service); 206 | ctx->databits = 0; 207 | 208 | luaL_getmetatable(L, MODBUS_META_CTX); 209 | // Can I put more functions in for tcp here? maybe? 210 | lua_setmetatable(L, -2); 211 | 212 | return 1; 213 | } 214 | 215 | /** Write a 32bit (u)int to 2x16bit registers 216 | * @function set_s32 217 | * @param num 32bit number 218 | * @return reg1 upper 16bits 219 | * @return reg2 lower 16bits 220 | */ 221 | static int helper_set_s32(lua_State *L) 222 | { 223 | /* truncate as necessary */ 224 | //const uint32_t toval = (uint32_t)luaL_checknumber(L, 1); 225 | lua_Number n = luaL_checknumber(L, 1); 226 | int32_t toval = (int32_t)n; 227 | lua_pushinteger(L, toval >> 16); 228 | lua_pushinteger(L, toval & 0xffff); 229 | return 2; 230 | } 231 | 232 | /** Write a bit float to 2x16bit registers 233 | * @function set_f32 234 | * @param num floating point number 235 | * @return reg1 upper 16bits of a 32bit float 236 | * @return reg2 lower 16bits of a 32bit float 237 | */ 238 | static int helper_set_f32(lua_State *L) 239 | { 240 | /* truncate to float if necessary */ 241 | const float in = (float)luaL_checknumber(L, 1); 242 | uint32_t out; 243 | memcpy(&out, &in, sizeof(out)); 244 | lua_pushinteger(L, out >> 16); 245 | lua_pushinteger(L, out & 0xffff); 246 | return 2; 247 | } 248 | 249 | 250 | /** 251 | * 16bit register as number to signed 16bit 252 | * @function get_s16 253 | * @param 1 16bit register 254 | * @return signed 16bit number 255 | */ 256 | static int helper_get_s16(lua_State *L) 257 | { 258 | const int16_t in = luaL_checknumber(L, 1); 259 | lua_pushinteger(L, in); 260 | return 1; 261 | } 262 | 263 | /** 264 | * 2x16bit registers as number to signed 32 bit 265 | * @function get_s32 266 | * @param 1 16bit register 267 | * @param 2 16bit register 268 | * @return 32bit number 269 | */ 270 | static int helper_get_s32(lua_State *L) 271 | { 272 | const uint16_t in1 = luaL_checknumber(L, 1); 273 | const uint16_t in2 = luaL_checknumber(L, 2); 274 | int32_t out = in1 << 16 | in2; 275 | lua_pushinteger(L, out); 276 | return 1; 277 | } 278 | 279 | /** 280 | * 2x16bit registers as number to signed 32 bit (reverse order) 281 | * @function get_s32le 282 | * @param 1 16bit register 283 | * @param 2 16bit register 284 | * @return 32bit number 285 | */ 286 | static int helper_get_s32le(lua_State *L) 287 | { 288 | const uint16_t in2 = luaL_checknumber(L, 1); 289 | const uint16_t in1 = luaL_checknumber(L, 2); 290 | int32_t out = in1 << 16 | in2; 291 | lua_pushinteger(L, out); 292 | return 1; 293 | } 294 | 295 | 296 | /** 297 | * 2x16bit registers as number to unsigned 32 bit 298 | * @function get_u32 299 | * @param 1 16bit register 300 | * @param 2 16bit register 301 | * @return 32bit number 302 | */ 303 | static int helper_get_u32(lua_State *L) 304 | { 305 | const uint16_t in1 = luaL_checknumber(L, 1); 306 | const uint16_t in2 = luaL_checknumber(L, 2); 307 | uint32_t out = in1 << 16 | in2; 308 | lua_pushnumber(L, out); 309 | return 1; 310 | } 311 | 312 | /** 313 | * 2x16bit registers as number to unsigned 32 bit (reversed order) 314 | * @function get_u32le 315 | * @param 1 16bit register 316 | * @param 2 16bit register 317 | * @return 32bit number 318 | */ 319 | static int helper_get_u32le(lua_State *L) 320 | { 321 | const uint16_t in2 = luaL_checknumber(L, 1); 322 | const uint16_t in1 = luaL_checknumber(L, 2); 323 | uint32_t out = in1 << 16 | in2; 324 | lua_pushnumber(L, out); 325 | return 1; 326 | } 327 | 328 | /** 329 | * 2x16bit registers as number to 32 bit float 330 | * @function get_f32 331 | * @param 1 16bit register 332 | * @param 2 16bit register 333 | * @return 32bit float 334 | */ 335 | static int helper_get_f32(lua_State *L) 336 | { 337 | const uint16_t in1 = luaL_checknumber(L, 1); 338 | const uint16_t in2 = luaL_checknumber(L, 2); 339 | 340 | uint32_t inval = in1<<16 | in2; 341 | float f; 342 | memcpy(&f, &inval, sizeof(f)); 343 | 344 | lua_pushnumber(L, f); 345 | return 1; 346 | } 347 | 348 | /** 349 | * 2x16bit registers as number to 32 bit float (Reversed order) 350 | * @function get_f32le 351 | * @param 1 16bit register 352 | * @param 2 16bit register 353 | * @return 32bit float 354 | */ 355 | static int helper_get_f32le(lua_State *L) 356 | { 357 | const uint16_t in2 = luaL_checknumber(L, 1); 358 | const uint16_t in1 = luaL_checknumber(L, 2); 359 | 360 | uint32_t inval = in1<<16 | in2; 361 | float f; 362 | memcpy(&f, &inval, sizeof(f)); 363 | 364 | lua_pushnumber(L, f); 365 | return 1; 366 | } 367 | 368 | /** 369 | * 4x16bit registers as number to signed 64 bit 370 | * @function get_s64 371 | * @param 1 16bit register 372 | * @param 2 16bit register 373 | * @param 3 16bit register 374 | * @param 4 16bit register 375 | * @return 64bit number 376 | */ 377 | static int helper_get_s64(lua_State *L) 378 | { 379 | const uint16_t in1 = luaL_checknumber(L, 1); 380 | const uint16_t in2 = luaL_checknumber(L, 2); 381 | const uint16_t in3 = luaL_checknumber(L, 3); 382 | const uint16_t in4 = luaL_checknumber(L, 4); 383 | int64_t out = in1; 384 | out = out << 16 | in2; 385 | out = out << 16 | in3; 386 | out = out << 16 | in4; 387 | lua_pushnumber(L, out); 388 | return 1; 389 | } 390 | 391 | /** 392 | * 4x16bit registers as number to unsigned 64 bit 393 | * @function get_u64 394 | * @param 1 16bit register 395 | * @param 2 16bit register 396 | * @param 3 16bit register 397 | * @param 4 16bit register 398 | * @return 64bit number 399 | */ 400 | static int helper_get_u64(lua_State *L) 401 | { 402 | const uint16_t in1 = luaL_checknumber(L, 1); 403 | const uint16_t in2 = luaL_checknumber(L, 2); 404 | const uint16_t in3 = luaL_checknumber(L, 3); 405 | const uint16_t in4 = luaL_checknumber(L, 4); 406 | uint64_t out = (uint64_t)in1 << 48 | (uint64_t)in2 << 32 | (uint64_t)in3 << 16 | in4; 407 | lua_pushnumber(L, out); 408 | return 1; 409 | } 410 | 411 | 412 | static ctx_t * ctx_check(lua_State *L, int i) 413 | { 414 | return (ctx_t *) luaL_checkudata(L, i, MODBUS_META_CTX); 415 | } 416 | 417 | static int ctx_destroy(lua_State *L) 418 | { 419 | ctx_t *ctx = ctx_check(L, 1); 420 | modbus_close(ctx->modbus); 421 | modbus_free(ctx->modbus); 422 | if (ctx->dev_host) { 423 | free(ctx->dev_host); 424 | } 425 | if (ctx->service) { 426 | free(ctx->service); 427 | } 428 | 429 | /* remove all methods operating on ctx */ 430 | lua_newtable(L); 431 | lua_setmetatable(L, -2); 432 | 433 | /* Nothing to return on stack */ 434 | return 0; 435 | } 436 | 437 | static int ctx_tostring(lua_State *L) 438 | { 439 | ctx_t *ctx = ctx_check(L, 1); 440 | 441 | if (ctx->databits) { 442 | lua_pushfstring(L, "ModbusRTU<%s@%d/%c%d>", ctx->dev_host, ctx->databits, ctx->parity, ctx->stopbits); 443 | } else { 444 | lua_pushfstring(L, "ModbusTCP<%s@%s>", ctx->dev_host, ctx->service); 445 | } 446 | return 1; 447 | } 448 | 449 | /** Context Methods. 450 | * These functions are members of a modbus context, from either new_rtu() or new_tcp_pi() 451 | * @section context_methods 452 | */ 453 | 454 | /** 455 | * Connect, see modbus_connect() 456 | * @function ctx:connect 457 | */ 458 | static int ctx_connect(lua_State *L) 459 | { 460 | ctx_t *ctx = ctx_check(L, 1); 461 | 462 | int rc = modbus_connect(ctx->modbus); 463 | 464 | return libmodbus_rc_to_nil_error(L, rc, 0); 465 | } 466 | 467 | /** 468 | * Close, see modbus_close() 469 | * @function ctx:close 470 | */ 471 | static int ctx_close(lua_State *L) 472 | { 473 | ctx_t *ctx = ctx_check(L, 1); 474 | 475 | modbus_close(ctx->modbus); 476 | 477 | return 0; 478 | } 479 | 480 | /** 481 | * Set debug 482 | * @function ctx:set_debug 483 | * @param enable optional bool, defaults to true 484 | */ 485 | static int ctx_set_debug(lua_State *L) 486 | { 487 | ctx_t *ctx = ctx_check(L, 1); 488 | bool opt; 489 | if (lua_isnil(L, -1)) { 490 | opt = true; 491 | } else { 492 | opt = lua_toboolean(L, -1); 493 | } 494 | modbus_set_debug(ctx->modbus, opt); 495 | return 0; 496 | } 497 | 498 | /** 499 | * Set error recovery, see modbus_set_error_recovery. 500 | * The arguments will be or'd together. 501 | * @function ctx:set_error_recovery 502 | * @param a one of @{error_recovery_constants} 503 | * @param b one of @{error_recovery_constants} 504 | */ 505 | static int ctx_set_error_recovery(lua_State *L) 506 | { 507 | ctx_t *ctx = ctx_check(L, 1); 508 | int opt = luaL_checkinteger(L, 2); 509 | int opt2 = luaL_optinteger(L, 3, 0); 510 | 511 | int rc = modbus_set_error_recovery(ctx->modbus, opt | opt2); 512 | 513 | return libmodbus_rc_to_nil_error(L, rc, 0); 514 | } 515 | 516 | /** 517 | * See also modbus_set_byte_timeout 518 | * @function ctx:set_byte_timeout 519 | * @param seconds (required) 520 | * @param microseconds (optional, defaults to 0) 521 | */ 522 | static int ctx_set_byte_timeout(lua_State *L) 523 | { 524 | ctx_t *ctx = ctx_check(L, 1); 525 | int opt = luaL_checkinteger(L, 2); 526 | int opt2 = luaL_optinteger(L, 3, 0); 527 | 528 | #if LIBMODBUS_VERSION_CHECK(3,1,0) 529 | modbus_set_byte_timeout(ctx->modbus, opt, opt2); 530 | #else 531 | struct timeval t = { opt, opt2 }; 532 | modbus_set_byte_timeout(ctx->modbus, &t); 533 | #endif 534 | 535 | return 0; 536 | } 537 | 538 | /** 539 | * @function ctx:get_byte_timeout 540 | * @return[1] seconds 541 | * @return[1] microseconds 542 | */ 543 | static int ctx_get_byte_timeout(lua_State *L) 544 | { 545 | ctx_t *ctx = ctx_check(L, 1); 546 | uint32_t opt1, opt2; 547 | 548 | #if LIBMODBUS_VERSION_CHECK(3,1,0) 549 | modbus_get_byte_timeout(ctx->modbus, &opt1, &opt2); 550 | #else 551 | struct timeval t; 552 | modbus_get_byte_timeout(ctx->modbus, &t); 553 | opt1 = t.tv_sec; 554 | opt2 = t.tv_usec; 555 | #endif 556 | lua_pushnumber(L, opt1); 557 | lua_pushnumber(L, opt2); 558 | 559 | return 2; 560 | } 561 | 562 | /** 563 | * @function ctx:set_response_timeout 564 | * @param seconds (required) 565 | * @param microseconds (optional, defaults to 0) 566 | */ 567 | static int ctx_set_response_timeout(lua_State *L) 568 | { 569 | ctx_t *ctx = ctx_check(L, 1); 570 | int opt = luaL_checkinteger(L, 2); 571 | int opt2 = luaL_optinteger(L, 3, 0); 572 | 573 | #if LIBMODBUS_VERSION_CHECK(3,1,0) 574 | modbus_set_response_timeout(ctx->modbus, opt, opt2); 575 | #else 576 | struct timeval t = { opt, opt2 }; 577 | modbus_set_response_timeout(ctx->modbus, &t); 578 | #endif 579 | 580 | return 0; 581 | } 582 | 583 | /** 584 | * @function ctx:get_response_timeout 585 | * @return[1] seconds 586 | * @return[1] microseconds 587 | */ 588 | static int ctx_get_response_timeout(lua_State *L) 589 | { 590 | ctx_t *ctx = ctx_check(L, 1); 591 | uint32_t opt1, opt2; 592 | 593 | #if LIBMODBUS_VERSION_CHECK(3,1,0) 594 | modbus_get_response_timeout(ctx->modbus, &opt1, &opt2); 595 | #else 596 | struct timeval t; 597 | modbus_get_response_timeout(ctx->modbus, &t); 598 | opt1 = t.tv_sec; 599 | opt2 = t.tv_usec; 600 | #endif 601 | lua_pushnumber(L, opt1); 602 | lua_pushnumber(L, opt2); 603 | 604 | return 2; 605 | } 606 | 607 | /** 608 | * @function ctx:get_socket 609 | * @return the socket number 610 | */ 611 | static int ctx_get_socket(lua_State *L) 612 | { 613 | ctx_t *ctx = ctx_check(L, 1); 614 | 615 | lua_pushinteger(L, modbus_get_socket(ctx->modbus)); 616 | 617 | return 1; 618 | } 619 | 620 | /** 621 | * @function ctx:set_socket 622 | * @param sock integer socket number to set 623 | */ 624 | static int ctx_set_socket(lua_State *L) 625 | { 626 | ctx_t *ctx = ctx_check(L, 1); 627 | int newfd = luaL_checknumber(L, 2); 628 | 629 | modbus_set_socket(ctx->modbus, newfd); 630 | 631 | return 0; 632 | } 633 | 634 | /** 635 | * @function ctx:rtu_get_serial_mode 636 | * @return @{rtu_constants} the serial mode, either RTU_RS232 or RTU_RS485 637 | */ 638 | static int ctx_rtu_get_serial_mode(lua_State *L) 639 | { 640 | ctx_t *ctx = ctx_check(L, 1); 641 | if (!ctx->is_rtu) { 642 | return luaL_error(L, "Cannot call RTU methods on an TCP context"); 643 | } 644 | 645 | lua_pushinteger(L, modbus_rtu_get_serial_mode(ctx->modbus)); 646 | 647 | return 1; 648 | } 649 | 650 | /** 651 | * Sets the mode of a serial port. 652 | * Remember, this is only required if your kernel is handling rs485 natively. 653 | * If you are using a USB adapter, you do NOT need this. 654 | * @function ctx:rtu_set_serial_mode 655 | * @param mode The selected serial mode from @{rtu_constants}, either RTU_RS232 or RTU_RS485. 656 | */ 657 | static int ctx_rtu_set_serial_mode(lua_State *L) 658 | { 659 | ctx_t *ctx = ctx_check(L, 1); 660 | if (!ctx->is_rtu) { 661 | return luaL_error(L, "Cannot call RTU methods on an TCP context"); 662 | } 663 | int mode = luaL_checknumber(L, 2); 664 | 665 | int rc = modbus_rtu_set_serial_mode(ctx->modbus, mode); 666 | 667 | return libmodbus_rc_to_nil_error(L, rc, 0); 668 | } 669 | 670 | /** 671 | * @function ctx:rtu_get_rts 672 | * @return @{rtu_rts_constants} the RTS handling mode, up/down/none 673 | */ 674 | static int ctx_rtu_get_rts(lua_State *L) 675 | { 676 | ctx_t *ctx = ctx_check(L, 1); 677 | if (!ctx->is_rtu) { 678 | return luaL_error(L, "Cannot call RTU methods on an TCP context"); 679 | } 680 | 681 | lua_pushinteger(L, modbus_rtu_get_rts(ctx->modbus)); 682 | 683 | return 1; 684 | } 685 | 686 | /** 687 | * Sets the RTS handling of a serial port. 688 | * @function ctx:rtu_set_rts 689 | * @param mode The selected RTS handling mode from @{rtu_rts_constants}, up/down/none. 690 | */ 691 | static int ctx_rtu_set_rts(lua_State *L) 692 | { 693 | ctx_t *ctx = ctx_check(L, 1); 694 | if (!ctx->is_rtu) { 695 | return luaL_error(L, "Cannot call RTU methods on an TCP context"); 696 | } 697 | int mode = luaL_checknumber(L, 2); 698 | 699 | int rc = modbus_rtu_set_rts(ctx->modbus, mode); 700 | 701 | return libmodbus_rc_to_nil_error(L, rc, 0); 702 | } 703 | 704 | /** 705 | * @function ctx:rtu_get_rts_delay 706 | * @return usecs 707 | */ 708 | static int ctx_rtu_get_rts_delay(lua_State *L) 709 | { 710 | ctx_t *ctx = ctx_check(L, 1); 711 | if (!ctx->is_rtu) { 712 | return luaL_error(L, "Cannot call RTU methods on an TCP context"); 713 | } 714 | 715 | lua_pushinteger(L, modbus_rtu_get_rts_delay(ctx->modbus)); 716 | 717 | return 1; 718 | } 719 | 720 | /** 721 | * Sets the RTS delay of a serial port. 722 | * @function ctx:rtu_set_rts_delay 723 | * @param usecs 724 | */ 725 | static int ctx_rtu_set_rts_delay(lua_State *L) 726 | { 727 | ctx_t *ctx = ctx_check(L, 1); 728 | if (!ctx->is_rtu) { 729 | return luaL_error(L, "Cannot call RTU methods on an TCP context"); 730 | } 731 | int usecs = luaL_checknumber(L, 2); 732 | 733 | int rc = modbus_rtu_set_rts_delay(ctx->modbus, usecs); 734 | 735 | return libmodbus_rc_to_nil_error(L, rc, 0); 736 | } 737 | 738 | /** 739 | * Returns the header length of the transport 740 | * @function ctx:get_header_length 741 | * @return the length in bytes 742 | */ 743 | static int ctx_get_header_length(lua_State *L) 744 | { 745 | ctx_t *ctx = ctx_check(L, 1); 746 | 747 | lua_pushinteger(L, modbus_get_header_length(ctx->modbus)); 748 | 749 | return 1; 750 | } 751 | 752 | /** 753 | * @function ctx:set_slave 754 | * @param unitid the unit address / slave id to use 755 | */ 756 | static int ctx_set_slave(lua_State *L) 757 | { 758 | ctx_t *ctx = ctx_check(L, 1); 759 | int slave = luaL_checknumber(L, 2); 760 | 761 | int rc = modbus_set_slave(ctx->modbus, slave); 762 | 763 | return libmodbus_rc_to_nil_error(L, rc, 0); 764 | } 765 | 766 | static int _ctx_read_bits(lua_State *L, bool input) 767 | { 768 | ctx_t *ctx = ctx_check(L, 1); 769 | int addr = luaL_checknumber(L, 2); 770 | int count = luaL_checknumber(L, 3); 771 | int rcount = 0; 772 | int rc; 773 | 774 | if (count > MODBUS_MAX_READ_BITS) { 775 | return luaL_argerror(L, 3, "requested too many bits"); 776 | } 777 | 778 | uint8_t *buf = malloc(count * sizeof(uint8_t)); 779 | assert(buf); 780 | if (input) { 781 | rc = modbus_read_input_bits(ctx->modbus, addr, count, buf); 782 | } else { 783 | rc = modbus_read_bits(ctx->modbus, addr, count, buf); 784 | } 785 | 786 | if (rc == count) { 787 | lua_newtable(L); 788 | /* nota bene, lua style offsets! */ 789 | for (int i = 1; i <= rc; i++) { 790 | lua_pushnumber(L, i); 791 | /* TODO - push number or push bool? what's a better lua api? */ 792 | lua_pushnumber(L, buf[i-1]); 793 | lua_settable(L, -3); 794 | } 795 | rcount = 1; 796 | } else { 797 | rcount = libmodbus_rc_to_nil_error(L, rc, count); 798 | } 799 | 800 | free(buf); 801 | return rcount; 802 | } 803 | 804 | /** 805 | * @function ctx:read_input_bits 806 | * @param address 807 | * @param count 808 | * @return an array of results 809 | */ 810 | static int ctx_read_input_bits(lua_State *L) 811 | { 812 | return _ctx_read_bits(L, true); 813 | } 814 | 815 | /** 816 | * @function ctx:read_bits 817 | * @param address 818 | * @param count 819 | * @return an array of results 820 | */ 821 | static int ctx_read_bits(lua_State *L) 822 | { 823 | return _ctx_read_bits(L, false); 824 | } 825 | 826 | static int _ctx_read_regs(lua_State *L, bool input) 827 | { 828 | ctx_t *ctx = ctx_check(L, 1); 829 | int addr = luaL_checknumber(L, 2); 830 | int count = luaL_checknumber(L, 3); 831 | int rcount = 0; 832 | int rc; 833 | 834 | if (count > MODBUS_MAX_READ_REGISTERS) { 835 | return luaL_argerror(L, 3, "requested too many registers"); 836 | } 837 | 838 | // better malloc as much space as we need to return data here... 839 | uint16_t *buf = malloc(count * sizeof(uint16_t)); 840 | assert(buf); 841 | if (input) { 842 | rc = modbus_read_input_registers(ctx->modbus, addr, count, buf); 843 | } else { 844 | rc = modbus_read_registers(ctx->modbus, addr, count, buf); 845 | } 846 | if (rc == count) { 847 | lua_newtable(L); 848 | /* nota bene, lua style offsets! */ 849 | for (int i = 1; i <= rc; i++) { 850 | lua_pushnumber(L, i); 851 | lua_pushnumber(L, buf[i-1]); 852 | lua_settable(L, -3); 853 | } 854 | rcount = 1; 855 | } else { 856 | rcount = libmodbus_rc_to_nil_error(L, rc, count); 857 | } 858 | 859 | free(buf); 860 | return rcount; 861 | } 862 | 863 | /** 864 | * @function ctx:read_input_registers 865 | * @param address 866 | * @param count 867 | * @return an array of results 868 | */ 869 | static int ctx_read_input_registers(lua_State *L) 870 | { 871 | return _ctx_read_regs(L, true); 872 | } 873 | 874 | /** 875 | * @function ctx:read_registers 876 | * @param address 877 | * @param count 878 | * @return an array of results 879 | */ 880 | static int ctx_read_registers(lua_State *L) 881 | { 882 | return _ctx_read_regs(L, false); 883 | } 884 | 885 | /** 886 | * @function ctx:report_slave_id 887 | * @return a luastring with the raw result (lua strings can contain nulls) 888 | */ 889 | static int ctx_report_slave_id(lua_State *L) 890 | { 891 | ctx_t *ctx = ctx_check(L, 1); 892 | 893 | uint8_t *buf = malloc(ctx->max_len); 894 | assert(buf); 895 | #if LIBMODBUS_VERSION_CHECK(3,1,0) 896 | int rc = modbus_report_slave_id(ctx->modbus, ctx->max_len, buf); 897 | #else 898 | int rc = modbus_report_slave_id(ctx->modbus, buf); 899 | #endif 900 | if (rc < 0) { 901 | return libmodbus_rc_to_nil_error(L, rc, 0); 902 | } 903 | 904 | lua_pushlstring(L, (char *)buf, rc); 905 | return 1; 906 | } 907 | 908 | /** 909 | * @function ctx:write_bit 910 | * @param address 911 | * @param value either a number or a boolean 912 | */ 913 | static int ctx_write_bit(lua_State *L) 914 | { 915 | ctx_t *ctx = ctx_check(L, 1); 916 | int addr = luaL_checknumber(L, 2); 917 | int val; 918 | 919 | if (lua_type(L, 3) == LUA_TNUMBER) { 920 | val = lua_tonumber(L, 3); 921 | } else if (lua_type(L, 3) == LUA_TBOOLEAN) { 922 | val = lua_toboolean(L, 3); 923 | } else { 924 | return luaL_argerror(L, 3, "bit must be numeric or boolean"); 925 | } 926 | 927 | int rc = modbus_write_bit(ctx->modbus, addr, val); 928 | 929 | return libmodbus_rc_to_nil_error(L, rc, 1); 930 | } 931 | 932 | /** 933 | * @function ctx:write_register 934 | * @param address 935 | * @param value 936 | */ 937 | static int ctx_write_register(lua_State *L) 938 | { 939 | ctx_t *ctx = ctx_check(L, 1); 940 | int addr = luaL_checknumber(L, 2); 941 | int val = luaL_checknumber(L, 3); 942 | 943 | int rc = modbus_write_register(ctx->modbus, addr, val); 944 | 945 | return libmodbus_rc_to_nil_error(L, rc, 1); 946 | } 947 | 948 | 949 | /** 950 | * @function ctx:write_bits 951 | * @param address 952 | * @param value as a lua array table 953 | */ 954 | static int ctx_write_bits(lua_State *L) 955 | { 956 | ctx_t *ctx = ctx_check(L, 1); 957 | int addr = luaL_checknumber(L, 2); 958 | int rc; 959 | int rcount; 960 | 961 | /* 962 | * TODO - could allow just a series of arguments too? easier for 963 | * smaller sets?"?) 964 | */ 965 | luaL_checktype(L, 3, LUA_TTABLE); 966 | /* array style table only! */ 967 | int count = lua_rawlen(L, 3); 968 | 969 | if (count > MODBUS_MAX_WRITE_BITS) { 970 | return luaL_argerror(L, 3, "requested too many bits"); 971 | } 972 | 973 | /* Convert table to uint8_t array */ 974 | uint8_t *buf = malloc(count * sizeof(uint8_t)); 975 | assert(buf); 976 | for (int i = 1; i <= count; i++) { 977 | bool ok = false; 978 | lua_rawgeti(L, 3, i); 979 | if (lua_type(L, -1) == LUA_TNUMBER) { 980 | buf[i-1] = lua_tonumber(L, -1); 981 | ok = true; 982 | } 983 | if (lua_type(L, -1) == LUA_TBOOLEAN) { 984 | buf[i-1] = lua_toboolean(L, -1); 985 | ok = true; 986 | } 987 | 988 | if (ok) { 989 | lua_pop(L, 1); 990 | } else { 991 | free(buf); 992 | return luaL_argerror(L, 3, "table values must be numeric or bool"); 993 | } 994 | } 995 | rc = modbus_write_bits(ctx->modbus, addr, count, buf); 996 | if (rc == count) { 997 | rcount = 1; 998 | lua_pushboolean(L, true); 999 | } else { 1000 | rcount = libmodbus_rc_to_nil_error(L, rc, count); 1001 | } 1002 | 1003 | free(buf); 1004 | return rcount; 1005 | } 1006 | 1007 | 1008 | /** 1009 | * @function ctx:write_registers 1010 | * @param address base address to write to 1011 | * @param value as a lua array table, or a sequence of values. 1012 | * @usage either 1013 | * ctx:write_registers(0x2000, {1,2,3}) 1014 | * ctx:write_registers(0x2000, 1, 2, 3) 1015 | */ 1016 | static int ctx_write_registers(lua_State *L) 1017 | { 1018 | ctx_t *ctx = ctx_check(L, 1); 1019 | int addr = luaL_checknumber(L, 2); 1020 | int rc; 1021 | int rcount; 1022 | uint16_t *buf; 1023 | int count; 1024 | 1025 | if (lua_type(L, 3) == LUA_TTABLE) { 1026 | /* array style table only! */ 1027 | count = lua_rawlen(L, 3); 1028 | 1029 | if (count > MODBUS_MAX_WRITE_REGISTERS) { 1030 | return luaL_argerror(L, 3, "requested too many registers"); 1031 | } 1032 | 1033 | /* Convert table to uint16_t array */ 1034 | buf = malloc(count * sizeof(uint16_t)); 1035 | assert(buf); 1036 | for (int i = 1; i <= count; i++) { 1037 | lua_rawgeti(L, 3, i); 1038 | /* user beware! we're not range checking your values */ 1039 | if (lua_type(L, -1) != LUA_TNUMBER) { 1040 | free(buf); 1041 | return luaL_argerror(L, 3, "table values must be numeric yo"); 1042 | } 1043 | /* This preserves sign and fractions better than tointeger() */ 1044 | lua_Number n = lua_tonumber(L, -1); 1045 | buf[i-1] = (int16_t)n; 1046 | lua_pop(L, 1); 1047 | } 1048 | } else { 1049 | /* Assume sequence of values then... */ 1050 | int total_args = lua_gettop(L); 1051 | if (total_args < 3) { 1052 | return luaL_argerror(L, 3, "No values provided to write!"); 1053 | } 1054 | count = total_args - 2; 1055 | buf = malloc(count * sizeof(uint16_t)); 1056 | assert(buf); 1057 | for (int i = 0; i < count; i++) { 1058 | buf[i] = (int16_t)lua_tonumber(L, i + 3); 1059 | } 1060 | } 1061 | rc = modbus_write_registers(ctx->modbus, addr, count, buf); 1062 | if (rc == count) { 1063 | rcount = 1; 1064 | lua_pushboolean(L, true); 1065 | } else { 1066 | rcount = libmodbus_rc_to_nil_error(L, rc, count); 1067 | } 1068 | 1069 | free(buf); 1070 | return rcount; 1071 | } 1072 | 1073 | static int ctx_send_raw_request(lua_State *L) 1074 | { 1075 | ctx_t *ctx = ctx_check(L, 1); 1076 | int rc; 1077 | int rcount; 1078 | int count = lua_rawlen(L, 2); 1079 | luaL_checktype(L, 2, LUA_TTABLE); 1080 | /* array style table only! */ 1081 | 1082 | /* Convert table to uint8_t array */ 1083 | uint8_t *buf = malloc(lua_rawlen(L, 2) * sizeof(uint8_t)); 1084 | assert(buf); 1085 | for (int i = 1; i <= count; i++) { 1086 | lua_rawgeti(L, 2, i); 1087 | /* user beware! we're not range checking your values */ 1088 | if (lua_type(L, -1) != LUA_TNUMBER) { 1089 | free(buf); 1090 | return luaL_argerror(L, 2, "table values must be numeric"); 1091 | } 1092 | buf[i-1] = lua_tonumber(L, -1); 1093 | lua_pop(L, 1); 1094 | }; 1095 | rc = modbus_send_raw_request(ctx->modbus, buf, count); 1096 | 1097 | if (rc < 0) { 1098 | lua_pushnil(L); 1099 | lua_pushstring(L, modbus_strerror(errno)); 1100 | rcount = 2; 1101 | } else { 1102 | // wait 1103 | lua_pushboolean(L, true); 1104 | rcount = 1; 1105 | } 1106 | long wait = lua_tonumber(L,3); 1107 | if (wait > 0) { 1108 | static struct timeval tim; 1109 | tim.tv_sec = 0; 1110 | tim.tv_usec = wait; 1111 | if (select(0, NULL, NULL, NULL, &tim) < 0) { 1112 | lua_pushnil(L); 1113 | return 2; 1114 | }; 1115 | } 1116 | 1117 | free(buf); 1118 | return rcount; 1119 | 1120 | } 1121 | 1122 | /** 1123 | * @function ctx:tcp_pi_listen 1124 | * @param conns max connections to listen to, defaults to 1 1125 | * @return the new socket number or normal error 1126 | */ 1127 | static int ctx_tcp_pi_listen(lua_State *L) 1128 | { 1129 | ctx_t *ctx = ctx_check(L, 1); 1130 | if (ctx->is_rtu) { 1131 | return luaL_error(L, "Cannot call TCP methods on an RTU context"); 1132 | } 1133 | int conns = luaL_optinteger(L, 2, 1); 1134 | 1135 | int sock = modbus_tcp_pi_listen(ctx->modbus, conns); 1136 | if (sock == -1) { 1137 | return libmodbus_rc_to_nil_error(L, 0, 1); 1138 | } 1139 | lua_pushnumber(L, sock); 1140 | 1141 | return 1; 1142 | } 1143 | 1144 | /** 1145 | * @function ctx:tcp_pi_accept 1146 | * @param sock the socket we're accepting on 1147 | * @return the new socket we've accepted or normal error 1148 | */ 1149 | static int ctx_tcp_pi_accept(lua_State *L) 1150 | { 1151 | ctx_t *ctx = ctx_check(L, 1); 1152 | if (ctx->is_rtu) { 1153 | return luaL_error(L, "Cannot call TCP methods on an RTU context"); 1154 | } 1155 | int sock = luaL_checknumber(L, 2); 1156 | 1157 | sock = modbus_tcp_pi_accept(ctx->modbus, &sock); 1158 | if (sock == -1) { 1159 | return libmodbus_rc_to_nil_error(L, 0, 1); 1160 | } 1161 | lua_pushnumber(L, sock); 1162 | 1163 | return 1; 1164 | } 1165 | 1166 | /** 1167 | * Receives a request from a remote. 1168 | * WARNING this might not be complete, documented that it exists 1169 | * @function ctx:receive 1170 | * @return the length of the data 1171 | * @return the data received 1172 | */ 1173 | static int ctx_receive(lua_State *L) 1174 | { 1175 | ctx_t *ctx = ctx_check(L, 1); 1176 | int rcount; 1177 | 1178 | uint8_t *req = malloc(ctx->max_len); 1179 | int rc = modbus_receive(ctx->modbus, req); 1180 | if (rc > 0) { 1181 | lua_pushnumber(L, rc); 1182 | lua_pushlstring(L, (char *)req, rc); 1183 | rcount = 2; 1184 | } else if (rc == 0) { 1185 | printf("Special case for rc = 0, can't remember\n"); 1186 | rcount = 0; 1187 | } else { 1188 | rcount = libmodbus_rc_to_nil_error(L, rc, 0); 1189 | } 1190 | return rcount; 1191 | } 1192 | 1193 | static int ctx_reply(lua_State *L) 1194 | { 1195 | ctx_t *ctx = ctx_check(L, 1); 1196 | size_t req_len; 1197 | const char *req = luaL_checklstring(L, 2, &req_len); 1198 | 1199 | luaL_checktype(L, 3, LUA_TTABLE); 1200 | 1201 | // FIXME - oh boy, probably need a whole lot of wrappers on the mappings? 1202 | //modbus_reply(ctx->modbus, (uint8_t*)req, req_len, mapping); 1203 | (void)ctx; 1204 | (void)req; 1205 | return luaL_error(L, "reply is simply unimplemented my friend!"); 1206 | } 1207 | 1208 | /** 1209 | * Reply to a request with an Exception. 1210 | * @function ctx:reply_exception 1211 | * @param initial the initial request to build into an exception 1212 | * @param exc the exception code to use from @{exception_codes} 1213 | * @return the new socket number or normal error 1214 | */ 1215 | static int ctx_reply_exception(lua_State *L) 1216 | { 1217 | ctx_t *ctx = ctx_check(L, 1); 1218 | const char *req = luaL_checklstring(L, 2, NULL); 1219 | int exception = luaL_checknumber(L, 3); 1220 | 1221 | int rc = modbus_reply_exception(ctx->modbus, (uint8_t*)req, exception); 1222 | if (rc == -1) { 1223 | return libmodbus_rc_to_nil_error(L, 0, 1); 1224 | } else { 1225 | return libmodbus_rc_to_nil_error(L, rc, rc); 1226 | } 1227 | } 1228 | 1229 | 1230 | struct definei { 1231 | const char* name; 1232 | int value; 1233 | }; 1234 | 1235 | struct defines { 1236 | const char* name; 1237 | const char* value; 1238 | }; 1239 | 1240 | /** Constants provided for use. 1241 | * Constants used in various functions, either as arguments or returns 1242 | * @section constants 1243 | */ 1244 | 1245 | /** 1246 | * RTU Mode Constants 1247 | * @see rtu_get_serial_mode 1248 | * @see rtu_set_serial_mode 1249 | * @table rtu_constants 1250 | * @field RTU_RS232 1251 | * @field RTU_RS485 1252 | */ 1253 | 1254 | /** 1255 | * RTU RTS Consants 1256 | * @see rtu_set_rts 1257 | * @see rtu_get_rts 1258 | * @table rtu_rts_constants 1259 | * @field RTU_RTS_NONE 1260 | * @field RTU_RTS_UP 1261 | * @field RTU_RTS_DOWN 1262 | */ 1263 | 1264 | /** Error Recovery Constants 1265 | * @see set_error_recovery 1266 | * @table error_recovery_constants 1267 | * @field ERROR_RECOVERY_NONE 1268 | * @field ERROR_RECOVERY_LINK 1269 | * @field ERROR_RECOVERY_PROTOCOL 1270 | */ 1271 | 1272 | /** Exception codes 1273 | * These are all MODBUS_xxxx upstream in libmodbus. 1274 | * @table exception_codes 1275 | * @field EXCEPTION_ILLEGAL_FUNCTION 1276 | * @field EXCEPTION_ILLEGAL_DATA_ADDRESS 1277 | * @field EXCEPTION_ILLEGAL_DATA_VALUE 1278 | * @field EXCEPTION_SLAVE_OR_SERVER_FAILURE 1279 | * @field EXCEPTION_ACKNOWLEDGE 1280 | * @field EXCEPTION_SLAVE_OR_SERVER_BUSY 1281 | * @field EXCEPTION_NEGATIVE_ACKNOWLEDGE 1282 | * @field EXCEPTION_MEMORY_PARITY 1283 | * @field EXCEPTION_NOT_DEFINED 1284 | * @field EXCEPTION_GATEWAY_PATH 1285 | */ 1286 | static const struct definei D[] = { 1287 | {"RTU_RS232", MODBUS_RTU_RS232}, 1288 | {"RTU_RS485", MODBUS_RTU_RS485}, 1289 | {"TCP_SLAVE", MODBUS_TCP_SLAVE}, 1290 | {"BROADCAST_ADDRESS", MODBUS_BROADCAST_ADDRESS}, 1291 | {"ERROR_RECOVERY_NONE", MODBUS_ERROR_RECOVERY_NONE}, 1292 | {"ERROR_RECOVERY_LINK", MODBUS_ERROR_RECOVERY_LINK}, 1293 | {"ERROR_RECOVERY_PROTOCOL", MODBUS_ERROR_RECOVERY_PROTOCOL}, 1294 | {"EXCEPTION_ILLEGAL_FUNCTION", MODBUS_EXCEPTION_ILLEGAL_FUNCTION}, 1295 | {"EXCEPTION_ILLEGAL_DATA_ADDRESS", MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS}, 1296 | {"EXCEPTION_ILLEGAL_DATA_VALUE", MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE}, 1297 | {"EXCEPTION_SLAVE_OR_SERVER_FAILURE", MODBUS_EXCEPTION_SLAVE_OR_SERVER_FAILURE}, 1298 | {"EXCEPTION_ACKNOWLEDGE", MODBUS_EXCEPTION_ACKNOWLEDGE}, 1299 | {"EXCEPTION_SLAVE_OR_SERVER_BUSY", MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY}, 1300 | {"EXCEPTION_NEGATIVE_ACKNOWLEDGE", MODBUS_EXCEPTION_NEGATIVE_ACKNOWLEDGE}, 1301 | {"EXCEPTION_MEMORY_PARITY", MODBUS_EXCEPTION_MEMORY_PARITY}, 1302 | {"EXCEPTION_NOT_DEFINED", MODBUS_EXCEPTION_NOT_DEFINED}, 1303 | {"EXCEPTION_GATEWAY_PATH", MODBUS_EXCEPTION_GATEWAY_PATH}, 1304 | {"EXCEPTION_GATEWAY_TARGET", MODBUS_EXCEPTION_GATEWAY_TARGET}, 1305 | {"RTU_RTS_NONE", MODBUS_RTU_RTS_NONE}, 1306 | {"RTU_RTS_UP", MODBUS_RTU_RTS_UP}, 1307 | {"RTU_RTS_DOWN", MODBUS_RTU_RTS_DOWN}, 1308 | {NULL, 0} 1309 | }; 1310 | 1311 | /** Other constants 1312 | * @table other_constants 1313 | * @field VERSION_STRING the compile time version, see also @{version} 1314 | * @field BROADCAST_ADDRESS used in @{set_slave} 1315 | * @field TCP_SLAVE used in @{set_slave} 1316 | */ 1317 | static const struct defines S[] = { 1318 | {"VERSION_STRING", LIBMODBUS_VERSION_STRING}, 1319 | {NULL, NULL} 1320 | }; 1321 | 1322 | static void modbus_register_defs(lua_State *L, const struct definei *D, const struct defines *S) 1323 | { 1324 | while (D->name != NULL) { 1325 | lua_pushinteger(L, D->value); 1326 | lua_setfield(L, -2, D->name); 1327 | D++; 1328 | } 1329 | while (S->name != NULL) { 1330 | lua_pushstring(L, S->value); 1331 | lua_setfield(L, -2, S->name); 1332 | S++; 1333 | } 1334 | } 1335 | 1336 | 1337 | static const struct luaL_Reg R[] = { 1338 | {"new_rtu", libmodbus_new_rtu}, 1339 | {"new_tcp_pi", libmodbus_new_tcp_pi}, 1340 | {"version", libmodbus_version}, 1341 | 1342 | {"set_s32", helper_set_s32}, 1343 | {"set_f32", helper_set_f32}, 1344 | 1345 | {"get_s16", helper_get_s16}, 1346 | {"get_s32", helper_get_s32}, 1347 | {"get_s32le", helper_get_s32le}, 1348 | {"get_u32", helper_get_u32}, 1349 | {"get_u32le", helper_get_u32le}, 1350 | {"get_f32", helper_get_f32}, 1351 | {"get_f32le", helper_get_f32le}, 1352 | {"get_s64", helper_get_s64}, 1353 | {"get_u64", helper_get_u64}, 1354 | /* {"get_u16", helper_get_u16}, Not normally useful, just use the number as it was returned */ 1355 | 1356 | {NULL, NULL} 1357 | }; 1358 | 1359 | static const struct luaL_Reg ctx_M[] = { 1360 | {"connect", ctx_connect}, 1361 | {"close", ctx_close}, 1362 | {"destroy", ctx_destroy}, 1363 | {"get_socket", ctx_get_socket}, 1364 | {"get_byte_timeout", ctx_get_byte_timeout}, 1365 | {"get_header_length", ctx_get_header_length}, 1366 | {"get_response_timeout",ctx_get_response_timeout}, 1367 | {"read_bits", ctx_read_bits}, 1368 | {"read_input_bits", ctx_read_input_bits}, 1369 | {"read_input_registers",ctx_read_input_registers}, 1370 | {"read_registers", ctx_read_registers}, 1371 | {"report_slave_id", ctx_report_slave_id}, 1372 | {"set_debug", ctx_set_debug}, 1373 | {"set_byte_timeout", ctx_set_byte_timeout}, 1374 | {"set_error_recovery", ctx_set_error_recovery}, 1375 | {"set_response_timeout",ctx_set_response_timeout}, 1376 | {"set_slave", ctx_set_slave}, 1377 | {"set_socket", ctx_set_socket}, 1378 | {"write_bit", ctx_write_bit}, 1379 | {"write_bits", ctx_write_bits}, 1380 | {"write_register", ctx_write_register}, 1381 | {"write_registers", ctx_write_registers}, 1382 | {"send_raw_request", ctx_send_raw_request}, 1383 | {"__gc", ctx_destroy}, 1384 | {"__tostring", ctx_tostring}, 1385 | 1386 | {"tcp_pi_listen", ctx_tcp_pi_listen}, 1387 | {"tcp_pi_accept", ctx_tcp_pi_accept}, 1388 | 1389 | {"rtu_get_serial_mode", ctx_rtu_get_serial_mode}, 1390 | {"rtu_set_serial_mode", ctx_rtu_set_serial_mode}, 1391 | {"rtu_get_rts", ctx_rtu_get_rts}, 1392 | {"rtu_set_rts", ctx_rtu_set_rts}, 1393 | {"rtu_get_rts_delay", ctx_rtu_get_rts_delay}, 1394 | {"rtu_set_rts_delay", ctx_rtu_set_rts_delay}, 1395 | 1396 | {"receive", ctx_receive}, 1397 | {"reply", ctx_reply}, /* Totally busted */ 1398 | {"reply_exception", ctx_reply_exception}, 1399 | 1400 | {NULL, NULL} 1401 | }; 1402 | 1403 | int luaopen_libmodbus(lua_State *L) 1404 | { 1405 | 1406 | #ifdef LUA_ENVIRONINDEX 1407 | /* set private environment for this module */ 1408 | lua_newtable(L); 1409 | lua_replace(L, LUA_ENVIRONINDEX); 1410 | #endif 1411 | 1412 | /* metatable.__index = metatable */ 1413 | luaL_newmetatable(L, MODBUS_META_CTX); 1414 | lua_pushvalue(L, -1); 1415 | lua_setfield(L, -2, "__index"); 1416 | luaL_setfuncs(L, ctx_M, 0); 1417 | 1418 | luaL_newlib(L, R); 1419 | 1420 | modbus_register_defs(L, D, S); 1421 | 1422 | return 1; 1423 | } 1424 | -------------------------------------------------------------------------------- /luarocks/lua-libmodbus-0.1-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-libmodbus" 2 | version = "0.1-1" 3 | source = { 4 | url = "https://github.com/remakeelectric/lua-libmodbus.git", 5 | tag = "v0.1" 6 | } 7 | description = { 8 | summary = "Lua bindings to libmodbus", 9 | detailed = [[ 10 | Lua bindings to the libmodbus library. 11 | The parameters to all functions are as per libmodbus's api 12 | only with sensible defaults for optional values, and return 13 | values directly rather than via pointers. 14 | ]], 15 | homepage = "https://github.com/remakeelectric/lua-libmodbus", 16 | license = "MIT" 17 | } 18 | dependencies = { 19 | "lua >= 5.1" 20 | } 21 | external_dependencies = { 22 | LIBMODBUS = { 23 | header = "modbus/modbus.h" 24 | } 25 | } 26 | build = { 27 | type = "builtin", 28 | modules = { 29 | 30 | libmodbus = { 31 | sources = { "lua-libmodbus.c" }, 32 | defines = {}, 33 | libraries = { "modbus" }, 34 | incdirs = { "$LIBMODBUS_INCDIR" }, 35 | libdirs = { "$LIBMODBUS_LIBDIR" }, 36 | } 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /luarocks/lua-libmodbus-0.2-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-libmodbus" 2 | version = "0.2-1" 3 | source = { 4 | url = "git://github.com/remakeelectric/lua-libmodbus", 5 | tag = "v0.2" 6 | } 7 | description = { 8 | summary = "Lua bindings to libmodbus", 9 | detailed = [[ 10 | Lua bindings to the libmodbus library. 11 | The parameters to all functions are as per libmodbus's api 12 | only with sensible defaults for optional values, and return 13 | values directly rather than via pointers. 14 | ]], 15 | homepage = "https://github.com/remakeelectric/lua-libmodbus", 16 | license = "MIT" 17 | } 18 | dependencies = { 19 | "lua >= 5.1" 20 | } 21 | external_dependencies = { 22 | LIBMODBUS = { 23 | header = "modbus/modbus.h" 24 | } 25 | } 26 | build = { 27 | type = "builtin", 28 | modules = { 29 | 30 | libmodbus = { 31 | sources = { "lua-libmodbus.c" }, 32 | defines = {}, 33 | libraries = { "modbus" }, 34 | incdirs = { "$LIBMODBUS_INCDIR" }, 35 | libdirs = { "$LIBMODBUS_LIBDIR" }, 36 | } 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /luarocks/lua-libmodbus-0.3-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-libmodbus" 2 | version = "0.3-1" 3 | source = { 4 | url = "git://github.com/remakeelectric/lua-libmodbus", 5 | tag = "v0.3" 6 | } 7 | description = { 8 | summary = "Lua bindings to libmodbus", 9 | detailed = [[ 10 | Lua bindings to the libmodbus library. 11 | The parameters to all functions are as per libmodbus's api 12 | only with sensible defaults for optional values, and return 13 | values directly rather than via pointers. 14 | ]], 15 | homepage = "https://github.com/remakeelectric/lua-libmodbus", 16 | license = "MIT" 17 | } 18 | dependencies = { 19 | "lua >= 5.1" 20 | } 21 | external_dependencies = { 22 | LIBMODBUS = { 23 | header = "modbus/modbus.h" 24 | } 25 | } 26 | build = { 27 | type = "builtin", 28 | modules = { 29 | 30 | libmodbus = { 31 | sources = { "lua-libmodbus.c" }, 32 | defines = {}, 33 | libraries = { "modbus" }, 34 | incdirs = { "$LIBMODBUS_INCDIR" }, 35 | libdirs = { "$LIBMODBUS_LIBDIR" }, 36 | } 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /luarocks/lua-libmodbus-0.3.1-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-libmodbus" 2 | version = "0.3.1-1" 3 | source = { 4 | url = "git://github.com/remakeelectric/lua-libmodbus", 5 | tag = "v0.3.1" 6 | } 7 | description = { 8 | summary = "Lua bindings to libmodbus", 9 | detailed = [[ 10 | Lua bindings to the libmodbus library. 11 | The parameters to all functions are as per libmodbus's api 12 | only with sensible defaults for optional values, and return 13 | values directly rather than via pointers. 14 | ]], 15 | homepage = "https://github.com/remakeelectric/lua-libmodbus", 16 | license = "MIT" 17 | } 18 | dependencies = { 19 | "lua >= 5.1" 20 | } 21 | external_dependencies = { 22 | LIBMODBUS = { 23 | header = "modbus/modbus.h" 24 | } 25 | } 26 | build = { 27 | type = "builtin", 28 | modules = { 29 | 30 | libmodbus = { 31 | sources = { "lua-libmodbus.c" }, 32 | defines = {}, 33 | libraries = { "modbus" }, 34 | incdirs = { "$LIBMODBUS_INCDIR" }, 35 | libdirs = { "$LIBMODBUS_LIBDIR" }, 36 | } 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /luarocks/lua-libmodbus-0.4-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-libmodbus" 2 | version = "0.4-1" 3 | source = { 4 | url = "git://github.com/remakeelectric/lua-libmodbus", 5 | tag = "v0.4" 6 | } 7 | description = { 8 | summary = "Lua bindings to libmodbus", 9 | detailed = [[ 10 | Lua bindings to the libmodbus library. 11 | The parameters to all functions are as per libmodbus's api 12 | only with sensible defaults for optional values, and return 13 | values directly rather than via pointers. 14 | ]], 15 | homepage = "https://github.com/remakeelectric/lua-libmodbus", 16 | license = "MIT" 17 | } 18 | dependencies = { 19 | "lua >= 5.1" 20 | } 21 | external_dependencies = { 22 | LIBMODBUS = { 23 | header = "modbus/modbus.h" 24 | } 25 | } 26 | build = { 27 | type = "builtin", 28 | modules = { 29 | 30 | libmodbus = { 31 | sources = { "lua-libmodbus.c" }, 32 | defines = {}, 33 | libraries = { "modbus" }, 34 | incdirs = { "$LIBMODBUS_INCDIR" }, 35 | libdirs = { "$LIBMODBUS_LIBDIR" }, 36 | } 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /luarocks/lua-libmodbus-0.4.1-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-libmodbus" 2 | version = "0.4.1-1" 3 | source = { 4 | url = "git://github.com/remakeelectric/lua-libmodbus", 5 | tag = "v0.4.1" 6 | } 7 | description = { 8 | summary = "Lua bindings to libmodbus", 9 | detailed = [[ 10 | Lua bindings to the libmodbus library. 11 | The parameters to all functions are as per libmodbus's api 12 | only with sensible defaults for optional values, and return 13 | values directly rather than via pointers. 14 | ]], 15 | homepage = "https://github.com/remakeelectric/lua-libmodbus", 16 | license = "MIT" 17 | } 18 | dependencies = { 19 | "lua >= 5.1" 20 | } 21 | external_dependencies = { 22 | LIBMODBUS = { 23 | header = "modbus/modbus.h" 24 | } 25 | } 26 | build = { 27 | type = "builtin", 28 | modules = { 29 | 30 | libmodbus = { 31 | sources = { "lua-libmodbus.c" }, 32 | defines = {}, 33 | libraries = { "modbus" }, 34 | incdirs = { "$LIBMODBUS_INCDIR" }, 35 | libdirs = { "$LIBMODBUS_LIBDIR" }, 36 | } 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /luarocks/lua-libmodbus-0.4.2-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-libmodbus" 2 | version = "0.4.2-1" 3 | source = { 4 | url = "git://github.com/remakeelectric/lua-libmodbus", 5 | tag = "v0.4.2" 6 | } 7 | description = { 8 | summary = "Lua bindings to libmodbus", 9 | detailed = [[ 10 | Lua bindings to the libmodbus library. 11 | The parameters to all functions are as per libmodbus's api 12 | only with sensible defaults for optional values, and return 13 | values directly rather than via pointers. 14 | ]], 15 | homepage = "https://github.com/remakeelectric/lua-libmodbus", 16 | license = "MIT" 17 | } 18 | dependencies = { 19 | "lua >= 5.1" 20 | } 21 | external_dependencies = { 22 | LIBMODBUS = { 23 | header = "modbus/modbus.h" 24 | } 25 | } 26 | build = { 27 | type = "builtin", 28 | modules = { 29 | 30 | libmodbus = { 31 | sources = { "lua-libmodbus.c" }, 32 | defines = {}, 33 | libraries = { "modbus" }, 34 | incdirs = { "$LIBMODBUS_INCDIR" }, 35 | libdirs = { "$LIBMODBUS_LIBDIR" }, 36 | } 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /luarocks/lua-libmodbus-0.5-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-libmodbus" 2 | version = "0.5-1" 3 | source = { 4 | url = "git://github.com/remakeelectric/lua-libmodbus", 5 | tag = "v0.5" 6 | } 7 | description = { 8 | summary = "Lua bindings to libmodbus", 9 | detailed = [[ 10 | Lua bindings to the libmodbus library. 11 | The parameters to all functions are as per libmodbus's api 12 | only with sensible defaults for optional values, and return 13 | values directly rather than via pointers. 14 | ]], 15 | homepage = "https://github.com/remakeelectric/lua-libmodbus", 16 | license = "MIT" 17 | } 18 | dependencies = { 19 | "lua >= 5.1" 20 | } 21 | external_dependencies = { 22 | LIBMODBUS = { 23 | header = "modbus/modbus.h" 24 | } 25 | } 26 | build = { 27 | type = "builtin", 28 | modules = { 29 | libmodbus = { 30 | sources = { "lua-libmodbus.c" }, 31 | defines = {}, 32 | libraries = { "modbus" }, 33 | incdirs = { "$(LIBMODBUS_INCDIR)" }, 34 | libdirs = { "$(LIBMODBUS_LIBDIR)" }, 35 | } 36 | }, 37 | platforms = { 38 | win32 = { 39 | modules = { 40 | libmodbus = { 41 | libraries = { "modbus", "ws2_32" }, 42 | } 43 | } 44 | } 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /luarocks/lua-libmodbus-0.6-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-libmodbus" 2 | version = "0.6-1" 3 | source = { 4 | url = "git://github.com/etactica/lua-libmodbus", 5 | tag = "v0.6" 6 | } 7 | description = { 8 | summary = "Lua bindings to libmodbus", 9 | detailed = [[ 10 | Lua bindings to the libmodbus library. 11 | The parameters to all functions are as per libmodbus's api 12 | only with sensible defaults for optional values, and return 13 | values directly rather than via pointers. 14 | ]], 15 | homepage = "https://github.com/etactica/lua-libmodbus", 16 | license = "MIT" 17 | } 18 | dependencies = { 19 | "lua >= 5.1" 20 | } 21 | external_dependencies = { 22 | LIBMODBUS = { 23 | header = "modbus/modbus.h" 24 | } 25 | } 26 | build = { 27 | type = "builtin", 28 | modules = { 29 | libmodbus = { 30 | sources = { "lua-libmodbus.c" }, 31 | defines = {}, 32 | libraries = { "modbus" }, 33 | incdirs = { "$(LIBMODBUS_INCDIR)" }, 34 | libdirs = { "$(LIBMODBUS_LIBDIR)" }, 35 | } 36 | }, 37 | platforms = { 38 | win32 = { 39 | modules = { 40 | libmodbus = { 41 | libraries = { "modbus", "ws2_32" }, 42 | } 43 | } 44 | } 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /luarocks/lua-libmodbus-0.6.1-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-libmodbus" 2 | version = "0.6.1-1" 3 | source = { 4 | url = "git://github.com/etactica/lua-libmodbus", 5 | tag = "v0.6.1" 6 | } 7 | description = { 8 | summary = "Lua bindings to libmodbus", 9 | detailed = [[ 10 | Lua bindings to the libmodbus library. 11 | The parameters to all functions are as per libmodbus's api 12 | only with sensible defaults for optional values, and return 13 | values directly rather than via pointers. 14 | ]], 15 | homepage = "https://github.com/etactica/lua-libmodbus", 16 | license = "MIT" 17 | } 18 | dependencies = { 19 | "lua >= 5.1" 20 | } 21 | external_dependencies = { 22 | LIBMODBUS = { 23 | header = "modbus/modbus.h" 24 | } 25 | } 26 | build = { 27 | type = "builtin", 28 | modules = { 29 | libmodbus = { 30 | sources = { "lua-libmodbus.c" }, 31 | defines = {}, 32 | libraries = { "modbus" }, 33 | incdirs = { "$(LIBMODBUS_INCDIR)" }, 34 | libdirs = { "$(LIBMODBUS_LIBDIR)" }, 35 | } 36 | }, 37 | platforms = { 38 | win32 = { 39 | modules = { 40 | libmodbus = { 41 | libraries = { "modbus", "ws2_32" }, 42 | } 43 | } 44 | } 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /luarocks/lua-libmodbus-0.7-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-libmodbus" 2 | version = "0.7-1" 3 | source = { 4 | url = "git://github.com/etactica/lua-libmodbus", 5 | tag = "v0.7" 6 | } 7 | description = { 8 | summary = "Lua bindings to libmodbus", 9 | detailed = [[ 10 | Lua bindings to the libmodbus library. 11 | The parameters to all functions are as per libmodbus's api 12 | only with sensible defaults for optional values, and return 13 | values directly rather than via pointers. 14 | ]], 15 | homepage = "https://github.com/etactica/lua-libmodbus", 16 | license = "MIT" 17 | } 18 | dependencies = { 19 | "lua >= 5.1" 20 | } 21 | external_dependencies = { 22 | LIBMODBUS = { 23 | header = "modbus/modbus.h" 24 | } 25 | } 26 | build = { 27 | type = "builtin", 28 | modules = { 29 | libmodbus = { 30 | sources = { "lua-libmodbus.c" }, 31 | defines = {}, 32 | libraries = { "modbus" }, 33 | incdirs = { "$(LIBMODBUS_INCDIR)" }, 34 | libdirs = { "$(LIBMODBUS_LIBDIR)" }, 35 | } 36 | }, 37 | platforms = { 38 | win32 = { 39 | modules = { 40 | libmodbus = { 41 | libraries = { "modbus", "ws2_32" }, 42 | } 43 | } 44 | } 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /luarocks/lua-libmodbus-0.8-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-libmodbus" 2 | version = "0.8-1" 3 | source = { 4 | url = "git+https://github.com/etactica/lua-libmodbus", 5 | tag = "v0.8" 6 | } 7 | description = { 8 | summary = "Lua bindings to libmodbus", 9 | detailed = [[ 10 | Lua bindings to the libmodbus library. 11 | The parameters to all functions are as per libmodbus's api 12 | only with sensible defaults for optional values, and return 13 | values directly rather than via pointers. 14 | ]], 15 | homepage = "https://github.com/etactica/lua-libmodbus", 16 | license = "MIT" 17 | } 18 | dependencies = { 19 | "lua >= 5.1" 20 | } 21 | external_dependencies = { 22 | LIBMODBUS = { 23 | header = "modbus/modbus.h" 24 | } 25 | } 26 | build = { 27 | type = "builtin", 28 | modules = { 29 | libmodbus = { 30 | sources = { "lua-libmodbus.c" }, 31 | defines = {}, 32 | libraries = { "modbus" }, 33 | incdirs = { "$(LIBMODBUS_INCDIR)" }, 34 | libdirs = { "$(LIBMODBUS_LIBDIR)" }, 35 | } 36 | }, 37 | platforms = { 38 | win32 = { 39 | modules = { 40 | libmodbus = { 41 | libraries = { "modbus", "ws2_32" }, 42 | } 43 | } 44 | } 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /spec/modbus_spec.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | --You'll need a working modbus device of some description for these tests... 3 | --that's.... out of scope.... 4 | --]] 5 | local D = { 6 | slave = 0xb6, 7 | host = "192.168.255.74", 8 | service = 1502, 9 | base = 0x2000, 10 | count = 10, 11 | } 12 | 13 | require("busted") 14 | mb = require("libmodbus") 15 | 16 | describe("basic arg passing", function() 17 | 18 | it("should accept helpful new_ params", function() 19 | assert.truthy(mb.new_tcp_pi("192.168.255.74", "mbap")) 20 | assert.truthy(mb.new_tcp_pi("192.168.255.74", 1502)) 21 | assert.has_error(function() mb.new_tcp_pi(true, "xxx") end) 22 | assert.has_error(function() mb.new_tcp_pi("xxx", {}) end) 23 | end) 24 | 25 | it("should get/set byte timeouts properly", function() 26 | x = mb.new_tcp_pi("blah", 123) 27 | local tv = {x:get_byte_timeout()} 28 | assert.is_same({0, 500*1000}, tv) 29 | x:set_byte_timeout(1, 250*1000) 30 | tv = {x:get_byte_timeout()} 31 | assert.is_same({1, 250*1000}, tv) 32 | end) 33 | it("should get/set response timeouts properly", function() 34 | x = mb.new_tcp_pi("blah", 123) 35 | local tv = {x:get_response_timeout()} 36 | assert.is_same({0, 500*1000}, tv) 37 | x:set_response_timeout(1, 250*1000) 38 | tv = {x:get_response_timeout()} 39 | assert.is_same({1, 250*1000}, tv) 40 | end) 41 | 42 | end) 43 | 44 | describe("functional tcp pi tests #real", function() 45 | local x 46 | setup(function() 47 | x = mb.new_tcp_pi(D.host, D.service) 48 | end) 49 | 50 | it("should connect ok", function() 51 | assert.is_truthy(x:connect()) 52 | assert.is_truthy(x:set_slave(D.slave)) 53 | local res, err = x:read_registers(D.base, D.count) 54 | assert.is_truthy(res, err) 55 | end) 56 | 57 | it("should fail well", function() 58 | assert.is_truthy(x:connect()) 59 | assert.is_truthy(x:set_slave(D.slave)) 60 | local res, err = x:read_registers(9999, 8) 61 | assert.falsy(res, "should have failed with illegal address") 62 | end) 63 | 64 | 65 | end) -------------------------------------------------------------------------------- /spec/number_helpers_spec.lua: -------------------------------------------------------------------------------- 1 | require("busted") 2 | --local rmep = require("remake.plugin_support") 3 | local mb = require("libmodbus") 4 | 5 | function noit() 6 | end 7 | 8 | function nodescribe() 9 | end 10 | 11 | describe("get signed vs unsigned", function() 12 | it("should decode unsigned", function() 13 | regs = { 0x0020, 0x3456 } 14 | val = mb.get_s32(unpack(regs)) 15 | valu = mb.get_u32(unpack(regs)) 16 | assert.are.equal(val, valu) 17 | assert.are.equal(val, 2110550) 18 | end) 19 | it("shouldn't hurt things", function() 20 | assert.are.equal(5, mb.get_s16(5)) 21 | assert.are.equal(-5, mb.get_s16(-5)) 22 | assert.are.equal(-5, mb.get_s16(0xfffb)) 23 | assert.are.equal(-5, mb.get_s16(65531)) 24 | -- This isn't reasonable. In lua's view, 0xface is a moderately large number. 25 | -- it's only in modbus that we might consider this to be a negative 16bit. 26 | -- assert.are.equal(0xface, mb.get_s16(0xface)) 27 | assert.are.equal(0x1ace, mb.get_s16(0x1ace)) 28 | end) 29 | it("should decode signed negative and large positive", function() 30 | regs = { 0xffff, 0xfc44 } 31 | val = mb.get_s32(unpack(regs)) 32 | valu = mb.get_u32(unpack(regs)) 33 | assert.are.equal(-956, val) 34 | -- not all lua's interpret hex literals the same way, and this form is only for visual sanity 35 | --assert.are.equal(0xfffffc44, valu) 36 | assert.are.equal(4294966340, valu) 37 | end) 38 | it("should do near edges", function() 39 | regs = { 0xffff, 0xfffe } 40 | assert.are.equal(-2, mb.get_s32(unpack(regs))) 41 | valu = mb.get_u32(unpack(regs)) 42 | -- not all lua's interpret hex literals the same way, and this form is only for visual sanity 43 | --assert.are.equal(0xfffffffe, valu) 44 | assert.are.equal(4294967294, valu) 45 | end) 46 | it("should do edges_high", function() 47 | regs = { 0xffff, 0xffff } 48 | assert.are.equal(-1, mb.get_s32(unpack(regs))) 49 | assert.are.equal(4294967295, mb.get_u32(unpack(regs))) 50 | end) 51 | it("should do edges_low", function() 52 | regs = { 0x0, 0x0 } 53 | assert.are.equal(0, mb.get_s32(unpack(regs))) 54 | assert.are.equal(0, mb.get_u32(unpack(regs))) 55 | end) 56 | end) 57 | 58 | 59 | describe("set signed vs unsigned", function() 60 | it("should set signed nicely", function() 61 | local h,l = mb.set_s32(2110550) 62 | assert.are.equal(0x0020, h) 63 | assert.are.equal(0x3456, l) 64 | end) 65 | it("should set signed negative", function() 66 | local h,l = mb.set_s32(-2110550) 67 | -- different lua implementations treat literal hex differently. 68 | assert.are.equal(mb.get_s16(0xffdf), mb.get_s16(h)) 69 | assert.are.equal(mb.get_s16(0xcbaa), mb.get_s16(l)) 70 | end) 71 | end) 72 | 73 | describe("floating point should do sane things", function() 74 | it("simple positive truncation", function() 75 | local v = 5698.123 76 | local h,l = mb.set_s32(v) 77 | assert.are.equal(0, h) 78 | assert.are.equal(0x1642, l) 79 | assert.are.equal(5698, l) 80 | end) 81 | it("simple negative truncation", function() 82 | local v = -5698.123 83 | local h,l = mb.set_s32(v) 84 | assert.are.equal(mb.get_s16(0xffff), mb.get_s16(h)) 85 | assert.are.equal(mb.get_s16(0xe9be), mb.get_s16(l)) 86 | assert.are.equal(-5698, mb.get_s16(l)) 87 | end) 88 | it("bracketed scaling", function() 89 | local v = 5698.123 90 | local scale = 1000 91 | local h,l = mb.set_s32(v*scale) 92 | assert.are.equal(0x56, h) 93 | assert.are.equal(mb.get_s16(0xf24b), mb.get_s16(l)) 94 | end) 95 | it("bracketed negative scaling", function() 96 | local v = -5698.123 97 | local scale = 1000 98 | local h,l = mb.set_s32(v*scale) 99 | assert.are.equal(mb.get_s16(0xffa9), mb.get_s16(h)) 100 | assert.are.equal(0x0db5, l) 101 | end) 102 | end) 103 | 104 | describe("regswap", function() 105 | it("should work normally", function() 106 | regs = {0x1122, 0x3344} 107 | assert.are_equal(860098850, mb.get_s32le(unpack(regs))) 108 | assert.are_equal(287454020, mb.get_s32(unpack(regs))) 109 | assert.are_equal(860098850, mb.get_u32le(unpack(regs))) 110 | assert.are_equal(287454020, mb.get_u32(unpack(regs))) 111 | end) 112 | it("should handle negatives too", function() 113 | regs = { 0xfffa, 0xfffe } 114 | -- not all lua's interpret hex literals the same way, and this form is only for visual sanity 115 | --assert.are.equal(0xfffefffa, mb.get_u32le(unpack(regs))) 116 | assert.are.equal(4294901754, mb.get_u32le(unpack(regs))) 117 | assert.are.equal(-65542, mb.get_s32le(unpack(regs))) 118 | end) 119 | end) 120 | 121 | describe("64bit ops", function() 122 | it("should handle basic stuff", function() 123 | regs = {0xffff, 0xffff, 0xffff, 0xfffe} 124 | local valu = mb.get_u64(unpack(regs)) 125 | assert.are.equal(18446744073709551614, mb.get_u64(unpack(regs))) 126 | assert.are.equal(-2, mb.get_s64(unpack(regs))) 127 | end) 128 | it("should handle small stuff too", function() 129 | regs = {0x0001, 0x0001, 0xffff, 0xfffe} 130 | assert.are.equal(281483566645246, mb.get_s64(unpack(regs))) 131 | assert.are.equal(281483566645246, mb.get_u64(unpack(regs))) 132 | end) 133 | it("should handle zero", function() 134 | regs = {0x0000, 0x0000, 0x0000, 0x0000} 135 | assert.are.equal(0, mb.get_s64(unpack(regs))) 136 | assert.are.equal(0, mb.get_u64(unpack(regs))) 137 | end) 138 | it("should handle one", function() 139 | regs = {0x0000, 0x0000, 0x0000, 0x0001} 140 | assert.are.equal(1, mb.get_s64(unpack(regs))) 141 | assert.are.equal(1, mb.get_u64(unpack(regs))) 142 | end) 143 | it ("should handle minus one", function() 144 | regs = {0xffff, 0xffff, 0xffff, 0xffff} 145 | assert.are.equal(-1, mb.get_s64(unpack(regs))) 146 | end) 147 | it ("should handle giant number", function() 148 | regs = {0xffff, 0xffff, 0xffff, 0xffff} 149 | local result = mb.get_u64(unpack(regs)) 150 | assert.is_true(result >= 1.84467440737E19 and result <= 1.844674407372E19) 151 | end) 152 | it ("should handle giant negative number", function() 153 | regs = {0x8000, 0x0000, 0x0000, 0x0000} 154 | local result = mb.get_s64(unpack(regs)) 155 | assert.is_true(result >= -9.2233720368549E18 and result <= -9.2233720368547E18) 156 | end) 157 | end) 158 | --------------------------------------------------------------------------------