Automatically adjust the read value returned via serial connection ?

Hi

Not sure if this is possible or not, but Im working on some basic code to test/control another hdmi matrix - below…

[code]function HDMIA1()
local socket = require(“socket”)
host = “192.168.1.147”
c = assert(socket.connect(host, 4002))
c:settimeout(5)
local sres, serr = c:send(string.char(0x02,0x32,0x31,0x31,0x03))
print(“Send:”, sres, serr)
local data, rerr = c:receive(300)
luup.log (data)
print (“Receive:”, data, rerr)
c:close()

end

HDMIA1()[/code]

I have noticed that depending on the HEX command that is sent it can time out if the c:receive(xxx) value is too large. While a low value works for all of them. For some everything that the matrix returns it needs to be 300 long , for the more quick commands I can set it to e.g 100 for it to work.

To deal with this, does anyone know how to code that part of the above so that it can automatically adjust in size or does each command need to have its own c:receive(xxx) associated ?

(The timeout value does not seem to play a part, it seems to relate to the number of characters it returns)

Here are a couple of examples of HEX commands that can be sent and the maximum c:receive value I can assign, any higher for each one and it will not complete and time out.

local sres, serr = c:send(string.char(0x73,0x77,0x20,0x69,0x30,0x31,0x20,0x6f,0x30,0x33,0x0d,0x0a)) print("Send:", sres, serr) local data, rerr = c:receive(113)

local sres, serr = c:send(string.char(0x72,0x65,0x61,0x64,0x0d,0x0a)) print("Send:", sres, serr) local data, rerr = c:receive(314)

The serial protocol should define a termination character. A fixed length read is simply not appropriate for variable length messages! Vera supports a number of message protocols/terminators, including , , . What documentation do you have for your device?

Hi @akbooer

The serial commands are here.

Its an ATEN Matrix, I?m currently converting their ASCII commands to HEX.

https://eservice.aten.com/eServiceCx/Common/FAQ/view.do?id=3975

Well, it’s clear that both send and receiving use the CRLF terminator, and this is one supported by Vera. If you use the luup.io package, then the end of line handling is taken care for you, if you have set the correct protocol.

Actually, on further thought, you can achieve the same with the socket package that you’ve set out using.

Look at the read patterns defined here: LuaSocket: TCP/IP support

Thanks

Trying the options I get the following returned, it seems only the ?number? options can show all what the matrix can return.

local data, rerr = c:receive(225)
Print output Send: 6 Receive: ??? ??? ??read Command OK o01 i02 video on audio on CEC off o02 i01 video on audio on CEC off o03 i01 video on audio on CEC off o04 i01 video on audio on CEC off FW: V2.2.213 FPGA: V1.0.076 EDID: default
local data, rerr = c:receive('*l')
Print output Send: 6 Receive: ??? ??? ??read Command OK
local data, rerr = c:receive('*a')
[font=arial][size=medium]Send: 6 [/size][/font][font=arial] [size=medium]Receive: nil timeout [/size][/font]

Yes, that’s because the sender keeps the socket open, after a transmission. You might try reading line-by-line to get the data you need. It may also be worthwhile setting the tcp-nodelay option described here, if you plan on interleaving receive and send requests alternately to the same socket.

Hi @akbooer

Many thanks for your suggestions, I tried the option as you suggested, changing c:receive to c:setoption

local data, rerr = c:setoption('tcp-nodelay', true)

but that just returns the following (luatest extract)

[u][b]Results[/b][/u] No errors Runtime: 3.4 ms Code returned: nil

Print output
Send: 6
Receive: 1

I had a go at switching things over to Luup.io.xxxx, but I?m not having much luck with that route.

[b][u]LuaTest 1.7[/u][/b]

Lua file: /nas/luatest/luup-io.lua

Results
No errors
Runtime: 2.2 ms
Code returned: nil

Print output

Code
1
2
3 local incoming_data = “”
4
5 luup.io.intercept()
6 luup.io.write(string.char(0x72,0x65,0x61,0x64,0x0d,0x0a), “192.168.1.147”, 4002)
7 incoming_data = luup.io.read()
8 luup.log(incoming_data)
9
10 print(incoming_data)

I tried the option as you suggested, changing c:receive to c:setoption

That’s not what I suggested! I was asking just to add the line to set that option and then continue using receive, line by line.

On your later experiment, yes, luup.io is tricky too. It’s designed for use in plugins, so I,m not sure how it would fare in LuaTest.

Ok, I tired it with c:receive first , but as it just timed out I assumed it must be a replacement - let me try again.

Also please hold off on the ?!? - If I misinterpreted anything I?m sorry.

Sadly still the same results as before, I only seem to be able to get the most information back by defining the number of bytes. E.g. (255)

Here is my current code, with the c:receive options.

[code]function HDMIA1()
local socket = require(“socket”)
host = “192.168.1.147”
c = assert(socket.connect(host, 4002))
c:settimeout(5)
c:setoption(‘tcp-nodelay’, true)

– read matrix status
local sres, serr = c:send(string.char(0x72,0x65,0x61,0x64,0x0d,0x0a))

– reset matrix
– local sres, serr = c:send(string.char(0x72,0x65,0x73,0x65,0x74,0x0d,0x0a))

print(“Send:”, sres, serr)
local data, rerr = c:receive(‘*a’)
– local data, rerr = c:receive(‘*l’)
– local data, rerr = c:receive(225)

print (“Receive:”, data, rerr)
c:close()
end

HDMIA1()[/code]

If the following only reads one line, I tried duplicating that line of code in the above a few time and in doing so it seems to read/return the line number the number of lines added relates to e.g so if I duplicate that line 3 times, it seems to return just the 3rd line of information that the matrix sends back. (But each time it?s only one line of text that is returned)
[ul][li][font=verdana]‘[font=Andale Mono]*l[/font][/font]’: reads a line of text from the socket. The line is terminated by a LF character (ASCII 10), optionally preceded by a CR character (ASCII 13). The CR and LF characters are not included in the returned line. In fact, all CR characters are ignored by the pattern. This is the default pattern;[/li][/ul]

Hi @akbooer,

Do you have any other thoughts on how to adjust the code to deal with different sized responses fro the matrix ?

As you have found, you can read the response line by line. The trick is knowing when to stop.

Once again, the LuaSocket documentation is your friend…

You may use socket.select() to test if there’s pending input.

For reference, the information returned by the matrix when the read command is sent to it, is as follows. (Copy from luatest)

[u][b]Print output[/b][/u] Send: 6 Receive: read Command OK o01 i02 video on audio on CEC off o02 i01 video on audio on CEC off o03 i01 video on audio on CEC off o04 i01 video on audio on CEC off FW: V2.2.213 FPGA: V1.0.076 EDID: default

I?m currently trying to read the second line and extract the first part, which I?m doing via the following code.

local sres, serr = c:send(string.char(0x72,0x65,0x61,0x64,0x0d,0x0a)) print("Send:", sres, serr) local data, rerr = c:receive('*l') local data, rerr = c:receive('*l') luup.log (data) print ("Receive:", data, rerr) local result = string:match (data, "%o%d+%s%i%d+") or "error" print ("Data=" ..result)

But currently I?m getting the following error, any ideas ?

Runtime error: Line 31: calling 'match' on bad self (string expected, got table)

Line 31 being.

local result = string:match (data, "%o%d+%s%i%d+") or "error"

FYI - I tested a few string match commands first to be sure it worked.

– = string.match(“o01 i02”, ‘%o%d%d%s%i%d%d’) = o01 i02
– = string.match(“o01 i02”, ‘%o(%d+)%s%i(%d+)’) = 01 02
– = string.match(“o01 i02”, ‘%o%d+%s%i%d+’) = o01 i02

Yes. You need either:

local result = string.match (data, "%o%d+%s%i%d+") or "error"

or

local result = data:match ("%o%d+%s%i%d+") or "error"

Thanks @akbooer

Is there a better way I can read each line, and then capture the key information that each line provides?

function send_command_to_hdmi_matrix(...) local socket = require("socket") host = "192.168.1.147" c = assert(socket.connect(host, 4002)) c:settimeout(5) -- read matrix status local sres, serr = c:send(string.char(...)) print("Send:", sres, serr) local data1, rerr1 = c:receive('*l') local data2, rerr2 = c:receive('*l') local data3, rerr3 = c:receive('*l') local data4, rerr4 = c:receive('*l') local data5, rerr5 = c:receive('*l') local line1 = string.match (data1, "%o%d+%s%i%d+") or "error" local line2 = string.match (data2, "%o%d+%s%i%d+") or "error" local line3 = string.match (data3, "%o%d+%s%i%d+") or "error" local line4 = string.match (data4, "%o%d+%s%i%d+") or "error" local line5 = string.match (data5, "%o%d+%s%i%d+") or "error" print ("line 1:", data1, rerr1) print ("line 2:", data2, rerr2) print ("line 3:", data3, rerr3) print ("line 4:", data4, rerr4) print ("line 5:", data5, rerr5) print ("" ..line1) print ("" ..line2) print ("" ..line3) print ("" ..line4) print ("" ..line5) c:close() return line2, line3 -- return the result can I store more than one to use elsewhere? end

Print output

Send: 6 line 1: read Command OK line 2: o01 i02 video on audio on CEC off line 3: o02 i01 video on audio on CEC off line 4: o03 i01 video on audio on CEC off line 5: o04 i01 video on audio on CEC off error o01 i02 o02 i01 o03 i01 o04 i01

The objective is to to capture the key information from each line and translate it with this function.

[code]local function interpret_reply_from_hdmi_matrix(status)
local message = {
– error
[“error”] = “Something else happened, it likely failed”,
– Output 1
[“o01 i01”] = “Ouput 1 is showing input 1”,
[“o01 i02”] = “Ouput 1 is showing input 2”,
[“o01 i03”] = “Ouput 1 is showing input 3”,
[“o01 i04”] = “Ouput 1 is showing input 4”,
– Output 2
[“o02 i01”] = “Ouput 2 is showing input 1”,
[“o02 i02”] = “Ouput 2 is showing input 2”,
[“o02 i03”] = “Ouput 2 is showing input 3”,
[“o02 i04”] = “Ouput 2 is showing input 4”,
}
return message[status] or “Unexpected status code received”
end

[/code]

Ok, here is my finished code, a bit rough - but it works and makes a call to the ATEN hdmi matrix for its status, extracting the first 5 lines. - which it then translates and presents the resulting information into the UI5 messaging/task window. (Once again thanks to @akbooer for the basis of this workflow)

[code]function send_command_to_hdmi_matrix(…)
local socket = require(“socket”)
host = “192.168.1.147”
c = assert(socket.connect(host, 4002))
c:settimeout(5)
– read matrix status
local sres, serr = c:send(string.char(…))
print(“Send:”, sres, serr)
local data1, rerr1 = c:receive(‘*l’)
local data2, rerr2 = c:receive(‘*l’)
local data3, rerr3 = c:receive(‘*l’)
local data4, rerr4 = c:receive(‘*l’)
local data5, rerr5 = c:receive(‘*l’)
local line1 = string.match (data1, “%u%a+%s%a+”) or “error”
local line2 = string.match (data2, “%o%d+%s%i%d+”) or “error”
local line3 = string.match (data3, “%o%d+%s%i%d+”) or “error”
local line4 = string.match (data4, “%o%d+%s%i%d+”) or “error”
local line5 = string.match (data5, “%o%d+%s%i%d+”) or “error”
print (“line 1:”, data1, rerr1)
print (“line 2:”, data2, rerr2)
print (“line 3:”, data3, rerr3)
print (“line 4:”, data4, rerr4)
print (“line 5:”, data5, rerr5)
print (“” …line1)
print (“” …line2)
print (“” …line3)
print (“” …line4)
print (“” …line5)
c:close()
return line2, line3, line4, line5 – return the result
end

local function interpret_reply_from_hdmi_matrix(status)
local message = {
– error
[“error”] = “Something else happened, it likely failed”,
– Output 1
[“o01 i01”] = “Ouput 1 is showing input 1”,
[“o01 i02”] = “Ouput 1 is showing input 2”,
[“o01 i03”] = “Ouput 1 is showing input 3”,
[“o01 i04”] = “Ouput 1 is showing input 4”,
– Output 2
[“o02 i01”] = “Ouput 2 is showing input 1”,
[“o02 i02”] = “Ouput 2 is showing input 2”,
[“o02 i03”] = “Ouput 2 is showing input 3”,
[“o02 i04”] = “Ouput 2 is showing input 4”,
– Output 3
[“o03 i01”] = “Ouput 3 is showing input 1”,
[“o03 i02”] = “Ouput 3 is showing input 2”,
[“o03 i03”] = “Ouput 3 is showing input 3”,
[“o03 i04”] = “Ouput 3 is showing input 4”,
– Output 4
[“o04 i01”] = “Ouput 4 is showing input 1”,
[“o04 i02”] = “Ouput 4 is showing input 2”,
[“o04 i03”] = “Ouput 4 is showing input 3”,
[“o04 i04”] = “Ouput 4 is showing input 4”,
}
return message[status] or “Unexpected status code received”
end

– here’s the message in the UI5 panel
local function present_results_of_hdmi_matrix_interaction(result)
print (result)
local handle = luup.task(“” … result, 1, “ATEN HDMI Matrix”, -1)
luup.call_delay(“clearTaskMsg”,5,handle)
end

function clearTaskMsg(strHandle) – this HAS to be non-local for call_delay to work
luup.task(“”,4,“”,tonumber(strHandle))
end

– here’s the main ‘workflow’
local l1, l2, l3, l4 = send_command_to_hdmi_matrix(0x72,0x65,0x61,0x64,0x0d,0x0a)
local message1 = interpret_reply_from_hdmi_matrix(l1)
local message2 = interpret_reply_from_hdmi_matrix(l2)
local message3 = interpret_reply_from_hdmi_matrix(l3)
local message4 = interpret_reply_from_hdmi_matrix(l4)
present_results_of_hdmi_matrix_interaction(message1)
present_results_of_hdmi_matrix_interaction(message2)
present_results_of_hdmi_matrix_interaction(message3)
present_results_of_hdmi_matrix_interaction(message4)

print (l1, l2, l3, l4)
print (message1, message2, message3, message4)[/code]

Here is the Lua Test output, happy to hear any recommendations on code improvement - Im also looking to see if I can create a basic visual representation of each output/input e.g [1] [2] [1] [1]

[u][b]LuaTest 1.7[/b][/u]

Lua file: /nas/luatest/aten_matrix-read-v2.lua

Results
No errors
Runtime: 124.0 ms
Code returned: nil

Print output
Send: 6
line 1: read Command OK
line 2: o01 i02 video on audio on CEC off
line 3: o02 i01 video on audio on CEC off
line 4: o03 i01 video on audio on CEC off
line 5: o04 i01 video on audio on CEC off
Command OK
o01 i02
o02 i01
o03 i01
o04 i01
Ouput 1 is showing input 2
Ouput 2 is showing input 1
Ouput 3 is showing input 1
Ouput 4 is showing input 1
o01 i02 o02 i01 o03 i01 o04 i01
Ouput 1 is showing input 2 Ouput 2 is showing input 1 Ouput 3 is showing input 1 Ouput 4 is showing input 1

Code
1 function send_command_to_hdmi_matrix(…)
2 local socket = require(“socket”)
3 host = “192.168.1.147”
4 c = assert(socket.connect(host, 4002))
5 c:settimeout(5)
6 – read matrix status
7 local sres, serr = c:send(string.char(…))
8 print(“Send:”, sres, serr)
9 local data1, rerr1 = c:receive(‘*l’)
10 local data2, rerr2 = c:receive(‘*l’)
11 local data3, rerr3 = c:receive(‘*l’)
12 local data4, rerr4 = c:receive(‘*l’)
13 local data5, rerr5 = c:receive(‘*l’)
14 local line1 = string.match (data1, “%u%a+%s%a+”) or “error”
15 local line2 = string.match (data2, “%o%d+%s%i%d+”) or “error”
16 local line3 = string.match (data3, “%o%d+%s%i%d+”) or “error”
17 local line4 = string.match (data4, “%o%d+%s%i%d+”) or “error”
18 local line5 = string.match (data5, “%o%d+%s%i%d+”) or “error”
19 print (“line 1:”, data1, rerr1)
20 print (“line 2:”, data2, rerr2)
21 print (“line 3:”, data3, rerr3)
22 print (“line 4:”, data4, rerr4)
23 print (“line 5:”, data5, rerr5)
24 print (“” …line1)
25 print (“” …line2)
26 print (“” …line3)
27 print (“” …line4)
28 print (“” …line5)
29 c:close()
30 return line2, line3, line4, line5 – return the result
31 end
32
33
34 local function interpret_reply_from_hdmi_matrix(status)
35 local message = {
36 – error
37 [“error”] = “Something else happened, it likely failed”,
38 – Output 1
39 [“o01 i01”] = “Ouput 1 is showing input 1”,
40 [“o01 i02”] = “Ouput 1 is showing input 2”,
41 [“o01 i03”] = “Ouput 1 is showing input 3”,
42 [“o01 i04”] = “Ouput 1 is showing input 4”,
43 – Output 2
44 [“o02 i01”] = “Ouput 2 is showing input 1”,
45 [“o02 i02”] = “Ouput 2 is showing input 2”,
46 [“o02 i03”] = “Ouput 2 is showing input 3”,
47 [“o02 i04”] = “Ouput 2 is showing input 4”,
48 – Output 3
49 [“o03 i01”] = “Ouput 3 is showing input 1”,
50 [“o03 i02”] = “Ouput 3 is showing input 2”,
51 [“o03 i03”] = “Ouput 3 is showing input 3”,
52 [“o03 i04”] = “Ouput 3 is showing input 4”,
53 – Output 4
54 [“o04 i01”] = “Ouput 4 is showing input 1”,
55 [“o04 i02”] = “Ouput 4 is showing input 2”,
56 [“o04 i03”] = “Ouput 4 is showing input 3”,
57 [“o04 i04”] = “Ouput 4 is showing input 4”,
58 }
59 return message[status] or “Unexpected status code received”
60 end
61
62 – here’s the message in the UI5 panel
63 local function present_results_of_hdmi_matrix_interaction(result)
64 print (result)
65 local handle = luup.task(“” … result, 1, “ATEN HDMI Matrix”, -1)
66 luup.call_delay(“clearTaskMsg”,5,handle)
67 end
68
69 function clearTaskMsg(strHandle) – this HAS to be non-local for call_delay to work
70 luup.task(“”,4,“”,tonumber(strHandle))
71 end
72
73 – here’s the main ‘workflow’
74 local l1, l2, l3, l4 = send_command_to_hdmi_matrix(0x72,0x65,0x61,0x64,0x0d,0x0a)
75 local message1 = interpret_reply_from_hdmi_matrix(l1)
76 local message2 = interpret_reply_from_hdmi_matrix(l2)
77 local message3 = interpret_reply_from_hdmi_matrix(l3)
78 local message4 = interpret_reply_from_hdmi_matrix(l4)
79 present_results_of_hdmi_matrix_interaction(message1)
80 present_results_of_hdmi_matrix_interaction(message2)
81 present_results_of_hdmi_matrix_interaction(message3)
82 present_results_of_hdmi_matrix_interaction(message4)
83
84
85 print (l1, l2, l3, l4)
86 print (message1, message2, message3, message4)

Glad it works.

It’s normal to use functions and loops to cut down on the repetition, but it’s OK.

Thanks @akbooer

Please could you give me an example of what would improve/ change within my code above via a function/loop?