This forum uses cookies
This forum makes use of cookies to store your login information if you are registered, and your last visit if you are not. Cookies are small text documents stored on your computer; the cookies set by this forum can only be used on this website and pose no security risk. Cookies on this forum also track the specific topics you have read and when you last read them. Please confirm that you accept these cookies being set.

ELK M1 Alarm panel
#1
Hi,

Has anyone created a script for the ELK M1 security panel?

Admin is there any example for a non blocking SSL connection?

Is there an example of, 2 ASCII characters, 2-digit checksum. This is the hexadecimal two’s complement of the modulo-256 sum of the ASCII values of all characters in the message excluding the checksum itself and the CR-LF terminator at the end of the message. Permissible characters are ASCII 0-9 and upper case A-F. When all the characters are added to the Checksum, the value should equal 0.

Thanks,


Roger
Reply
#2
Here's a basic SSL connection example:
Code:
socket = require('socket')
ssl = require('ssl')

ip = '192.168.1.1'
port = 2601

sock = socket.tcp()
sock:settimeout(5)
sock:connect(ip, port)
sock = ssl.wrap(sock, 'tlsv1')
sock:dohandshake()

Checksum calculation:
Code:
function checksum(str)
  local sum = 0
  for i = 1, #str do
    sum = (sum + str:byte(i)) % 256
  end
  return string.format('%02X', 256 - sum)
end
Reply
#3
Hi Admin,

Thank you,  the checksum function works.  I am trying to do the reverse and verify the message received has a valid checksum



Code:
function checksum(str)
 local sum = 0
 for i = 1, #str do
   sum = (sum + string.byte(str,i)) % 256
 end
 x = 256-sum
 return string.format('%02X', 256 - sum)
end

function reversechk(str, cksum)
 local sum = tonumber(cksum,16)
 log(sum)
 for i=1, #str do
   sum= (sum + string.byte(str,i))
 end
 return string.format('%02X', sum)
end

local message= '16XK403923611081711000'
local chksum = checksum(message)
log(chksum)
-- chksum is 67
-- so the total message is '16XK40392361108171100067'
local isok = reversechk(message,chksum)
log(isok)


The reversechk function does not return a 0,  what am doing wrong?  I have the reverse calculation in C code,  maybe you can understand what they doing?

Code:
Calculate checksum on received and transmitted ASCII string
Example C code program
//INT8U is an 8 bit unsigned integer.
INT8U itAscRecBuf[82]; //ASCII receive character buffer
INT8U AscHexToBin(INT8U, INT8U *); //ASCII hex to binary conversion
INT8U AsciiToHex( INT8U); //Ascii to Hex conversion

//Calculate checksum on a received ASCII string, return checksum value.
//It should equal 0 if good.
INT8U CalcCheckSum(void)
{
    INT8U i,length, cc;
    length = AscHexToBin(2, &itAscRecBuf[0]);
    //get length value, first two characters
    
    cc = AscHexToBin(2, &itAscRecBuf[length]); //get checksum value at end of string.
    for (i=0;i<length ;i++ )
        {
            cc += itAscRecBuf[i]; //get string value and add it to checksum
        }
        return(cc);
        //good checksum should equal 0 }
    //ascii hex to binary, width 1 or 2
    INT8U AscHexToBin(INT8U Width, INT8U * DataPtr) //
        {
            INT8U aVal; // accumulated value
            aVal = AsciiToHex(*DataPtr);
            DataPtr++;
            if (Width == 2)    //two digits wide, else 1 digit wide
                {
                    aVal = aVal << 4;
                    aVal += AsciiToHex(*DataPtr);
                }
            return(aVal);
         }
    //Ascii to Hex conversion
    INT8U AsciiToHex( INT8U Value )
        {
            switch ( Value )
                {
                    case 'A': return( 10 );
                    case 'B': return( 11 );
                    case 'C': return( 12 );
                    case 'D': return( 13 );
                    case 'E': return( 14 );
                    case 'F': return( 15 );
                    default: return( Value - 0x30 );
                }
        }


Thanks,

Roger
Reply
#4
You don't need a separate function for this, last 2 bytes of the message is the resulting checksum. You can do it like this:
Code:
message = '16XK40392361108171100067'
calccs = checksum(message:sub(1, -3)) -- calculated checksum from message data
recvcs = message:sub(-2, -1) -- received checksum
log(calccs, recvcs)
Reply
#5
Hi Admin,

Thank you, I am trying to get the connection working with ssl,  but the connection gets closed.  I want to use copas since this gives more flexibility of building a non-blocking solution.  I have the option also to run code in separate threads. 

Code:
if not ready then
 socket = require("socket")
 copas = require("copas")
 require('ssl')
 ready=true
 esys = {}  

 function checksum(str)
   local sum = 0
   for i = 1, #str do
     sum = (sum + string.byte(str,i)) % 256
   end
   x = 256-sum
   return string.format('%02X', 256 - sum)
 end
 
 function validchecksum(str)
   calccs = checksum(str:sub(1, -3))    -- calculated checksum from message data
   recvcs = str:sub(-2, -1)        -- received checksum
   return calccs == recvcs        -- when they the same return true
 end
 
 --helper functions to round and increment values  
 function increment(n)
   n = n + 1
   return n
 end
 
 function parse(data)
   alert('elk parsing: %s', data)
   if(validchecksum(data)==true)then
     local response = string.sub(data,3,-5)
     alert('elk message is: %s',response)
   end
 end
 
 function sendElk(command)
   skt:send(command)
   sleep(1)    
 end
   
 function init()
   alert('running init')

 end
 
 function fromKNX(command)
   local telegram = string.split(command,',')
   alert('from knx %s',command)

 end
 
 local server = socket.udp()
 server:setsockname("127.0.0.1",33456)
 function handler(skts)
   skts = copas.wrap(skts)
   alert("UDP connection handler")
   while true do
     local s, err
     alert("UDP receiving..")
     s, erro = skts:receive(2048)
     if not s then
       alert("UDP Receive error: %s", erro)
       break
     end
     alert("UDP Received data, bytes: %s",s)
     fromKNX(s)
   end
 end
   
 copas.addserver(server, handler, 1)


--[[ co = copas.addthread(function()
     while true do  
       copas.sleep(5) -- wait 5 seconds in case of other events

       copas.sleep(-1) -- put the thread to sleep until wakeup is called
     end
   end)]]--
end

if not skt then
 -- create tcp client
 sslparams = {
 mode = "client",
 protocol = "sslv3",
 key = "crt/ca-key.pem",
 certificate = "crt/client.pem",
 cafile = "crt/ca-cert.pem",
 verify = "peer",
 options = {"all", "sslv3"}
  }
 skt = copas.wrap(socket.tcp(), sslparams)

 skt, err = socket.connect('192.168.0.11', 2601)
 -- when theres no error connect ok, initialize
 if(not err) then
   skt:settimeout(0)
   -- add receive thread

   copas.addthread(function()
       while true do
         local resp,err = copas.receive(skt)
         -- if theres no connection start a new connection
         if not resp then
           alert("[tcp-client] Receive error: %s", err)
           copas.removeserver(skt)
           skt = nil
           break
         end
         if(string.find(resp,'^16XK')==false)then -- don't process timer messages
           local fd,prtd = pcall(parse,resp)
           if(fd==false)then
             alert("Error with parsemsg %s ",prtd)
           end
         end
       end
     end)
   if skt then
     alert('[tcp-client] connection ok')
     init()
     -- error while connecting,
   else
     if warningfailed then alert('[tcp-client] connection failed (conn): %s', err) end
     return
   end
 else
   alert('[tcp-client] error connecting %s',err)
   return
 end
end
copas.loop()

What am I doing wrong?

Thanks,


Roger
Reply
#6
Replace this part:
Code:
-- create tcp client
sslparams = {
mode = "client",
protocol = "sslv3",
key = "crt/ca-key.pem",
certificate = "crt/client.pem",
cafile = "crt/ca-cert.pem",
verify = "peer",
options = {"all", "sslv3"}
}
skt = copas.wrap(socket.tcp(), sslparams)

skt, err = socket.connect('192.168.0.11', 2601)

With this:
Code:
skt = socket.tcp()
skt:settimeout(5)
skt:connect('192.168.0.11', 2601)
skt, err = ssl.wrap(skt, 'sslv23')
if not err then
  skt:dohandshake()
  skt = copas.wrap(skt)
end
Reply
#7
ok thanks, I will try and let you know.

thx

Roger
Reply
#8
Hi Admin,

Ok so with a slight modification its now working, see the revised code below.  Question in terms of error handling for the connection will this be sufficient for this client script to reconnect if for whatever reason the connection is lost?

Thanks,

Roger



Code:
if not skt then
 skt = socket.tcp()
 skt:settimeout(5)
 skt:connect('192.168.0.11', 2601)
 skt, err = ssl.wrap(skt,'tlsv1')
 if not err then
   skt:dohandshake()
   skt = copas.wrap(skt)
   copas.addthread(function()
       while true do
         local resp,err = copas.receive(skt)
         -- if theres no connection start a new connection
         if not resp or err ~=nil then
           alert("[elk-client] Receive error: %s", err)
           copas.removeserver(skt)
           skt = nil
           break
         end
         if(string.find(resp,'^16XK')==nil)then
           local fd,prtd = pcall(parse,resp)
           if(fd==false)then
             alert("[elk-client] Error with parsemsg %s ",prtd)
           end
         else
           alert('[elk-client] time message: %s', resp)
         end
       end
     end)
 else              
   alert("[elk-client] connection error: %s", err)
   skt = nil
   return
 end
end
Reply
#9
(19.08.2017, 23:32)rocfusion Wrote: Hi Admin,

Ok so with a slight modification its now working, see the revised code below.  Question in terms of error handling for the connection will this be sufficient for this client script to reconnect if for whatever reason the connection is lost?

Thanks,

Roger



Code:
if not skt then
 skt = socket.tcp()
 skt:settimeout(5)
 skt:connect('192.168.0.11', 2601)
 skt, err = ssl.wrap(skt,'tlsv1')
 if not err then
   skt:dohandshake()
   skt = copas.wrap(skt)
   copas.addthread(function()
       while true do
         local resp,err = copas.receive(skt)
         -- if theres no connection start a new connection
         if not resp or err ~=nil then
           alert("[elk-client] Receive error: %s", err)
           copas.removeserver(skt)
           skt = nil
           break
         end
         if(string.find(resp,'^16XK')==nil)then
           local fd,prtd = pcall(parse,resp)
           if(fd==false)then
             alert("[elk-client] Error with parsemsg %s ",prtd)
           end
         else
           alert('[elk-client] time message: %s', resp)
         end
       end
     end)
 else              
   alert("[elk-client] connection error: %s", err)
   skt = nil
   return
 end
end

Hi Admin,  is this ok?
Thx
Roger
Reply
#10
Yes, looks fine
Reply


Forum Jump: