KNXnet/IP / KNX TP tunneling howto

After long web search and the study a lot of KNX documents, I want to make it easier for other users.
Therefore I created a HOWTO for the communication here.

To communicate over an IP interface (f.e. Siemens N148/22) you must learn the order of datagrams.

1st: send a CONNECT_REQUEST
2nd: receive a CONNECT_RESPONCE
3rd: send a CONNECTIONSTATE_REQUEST
4th: receive a CONNECTIONSTATE_RESPONCE
5th: send a TUNNELLING_REQUEST
6th: receive a TUNNELLING_ACK
7th: receive a TUNNELLING_REQUEST
8th: send a TUNNELLING_ACK
9th: send a DISCONNECT_REQUEST
10th: receive DISCONNECT_RESPONCE

You could initiate with 1st and 2nd step the communication, handle all your multiple requests (reading, writing and so on) and after your are finished, you stop the session with 9th and 10th.

Here are the details.

1st: sending CONNECTION_REQUEST
In this following datagram you find two different HAPI (IP + Port). The IP interface requires a local (sending) communication port for connection, connectionstate and disconnect requests and another one for tunnelling requests, only.

/* CONNECTION_REQUEST */
/* header */
creq[0] = 0x06; /* 06 - Header Length */
creq[1] = 0x10; /* 10 - KNXnet version (1.0) */
creq[2] = 0x02; /* 02 - hi-byte Service type descriptor (CONNECTION_REQUEST) */
creq[3] = 0x05; /* 05 - lo-byte Service type descriptor (CONNECTION_REQUEST) */
creq[4] = 0x00; /* 00 - hi-byte total length */
creq[5] = 0x1A; /* 1A - lo-byte total lengt 26 bytes */

/* Connection HPAI */
creq[6] = 0x08; /* 08 - Host Protocol Address Information (HPAI) Lenght */
creq[7] = 0x01; /* 01 - Host Protocol Address Information (HPAI) Lenght */
creq[8] = ntohl(sin_1st.sin_addr.s_addr) >> 24 & 0xff; /* c0 - IP address 00 = 192 */
creq[9] = ntohl(sin_1st.sin_addr.s_addr) >> 16 & 0xff; /* a8 - IP address 00 = 168 */
creq[10] = ntohl(sin_1st.sin_addr.s_addr) >> 8 & 0xff; /* 0a - IP address 00 = 10 */
creq[11] = ntohl(sin_1st.sin_addr.s_addr) & 0xff; /* b3 - IP address 00 = 179 */
creq[12] = (ntohs(sin_1st.sin_port) >> 8) & 0xff; /* xx - hi-byte local port number for CONNECTION, CONNECTIONSTAT and DISCONNECT requests */
creq[13] = ntohs(sin_1st.sin_port) & 0xff; /* xx - lo-byte local port number for CONNECTION, CONNECTIONSTAT and DISCONNECT requests */

/* Tunnelling HPAI */
creq[14] = 0x08; /* 08 - Host Protocol Address Information (HPAI) Lenght */
creq[15] = 0x01; /* 01 - Host Protocol Address Information (HPAI) Lenght */
creq[16] = ntohl(sin_2nd.sin_addr.s_addr) >> 24 & 0xff; /* c0 - IP address c0 = 192 */
creq[17] = ntohl(sin_2nd.sin_addr.s_addr) >> 16 & 0xff; /* a8 - IP address a8 = 168 */
creq[18] = ntohl(sin_2nd.sin_addr.s_addr) >> 8 & 0xff; /* 0a - IP address 0a = 10 */
creq[19] = ntohl(sin_2nd.sin_addr.s_addr) & 0xff; /* b3 - IP address 9F = 179 */
creq[20] = (ntohs(sin_2nd.sin_port) >> 8) & 0xff; /* yy - hi-byte local port number for TUNNELLING requests */
creq[21] = ntohs(sin_2nd.sin_port) & 0xff; /* yy - lo-byte local port number for TUNNELLING requests */

/* CRI */
creq[22] = 0x04; /* structure len (4 bytes) */
creq[23] = 0x04; /* Tunnel Connection */
creq[24] = 0x02; /* KNX Layer (Tunnel Link Layer) */
creq[25] = 0x00; /* Reserved */

f.e.: 06 10 02 05 00 1a 08 01 c0 a8 0a b3 d9 6d 08 01 c0 a8 0a b3 d8 36 04 04 02 00

byte no. 3 and 4: service type:
– SEARCH_REQUEST 0x0201
– SEARCH_RESPONSE 0x0202
– DESCRIPTION_REQUEST 0x0203
– DESCRIPTION_RESPONSE 0x0204
– CONNECTION_REQUEST 0x0205
– CONNECTION_RESPONSE 0x0206
– CONNECTIONSTATE_REQUEST 0x0207
– CONNECTIONSTATE_RESPONSE 0x0208
– DISCONNECT_REQUEST 0x0209
– DISCONNECT_RESPONSE 0x020A
– TUNNEL_REQUEST 0x0420
– TUNNEL_RESPONSE 0x0421
– DEVICE_CONFIGURATION_REQUEST 0x0310
– DEVICE_CONFIGURATION_ACK 0x0311
– ROUTING_INDICATION 0x0530

2nd: receive a CONNECT_RESPONCE
You get an answer like:

f.e.: 06 10 02 06 00 14 49 00 08 01 c0 a8 0a 0e 0e 57 04 04 10 01

byte no. 7 is the important channel-ID, remember that!
byte no. 8 is the status/error number, zero is perfect!

3rd: send a CONNECTIONSTATE_REQUEST
Now you send a connectionstate request to ensure that no errors occured in this session.

/* CONNECTIONSTATE_REQUEST */
/* Header (6 Bytes) */
csreq[0] = 0x06; /* 06 - Header Length */
csreq[1] = 0x10; /* 10 - KNXnet version (1.0) */
csreq[2] = 0x02; /* 02 - hi-byte Service type descriptor (CONNECTIONSTATE_REQUEST) */
csreq[3] = 0x07; /* 07 - lo-byte Service type descriptor (CONNECTIONSTATE_REQUEST) */
csreq[4] = 0x00; /* 00 - hi-byte total length */
csreq[5] = 0x10; /* 10 - lo-byte total lengt 16 bytes */

/* Connection HAPI (10 Bytes) */
csreq[6] = iChannelID & 0xff; /* zz - given channel id */
csreq[7] = 0x00; /* 00 - */
csreq[8] = 0x08; /* 08 - Host Protocol Address Information (HPAI) Lenght */
csreq[9] = 0x01; /* 01 - Host Protocol Code 0x01 -> IPV4_UDP, 0x02 -> IPV6_TCP */
csreq[10] = ntohl(sin_1st.sin_addr.s_addr) >> 24 & 0xff; /* c0 - IP address 00 = 192 */
csreq[11] = ntohl(sin_1st.sin_addr.s_addr) >> 16 & 0xff; /* a8 - IP address 00 = 168 */
csreq[12] = ntohl(sin_1st.sin_addr.s_addr) >> 8 & 0xff; /* 0a - IP address 00 = 10 */
csreq[13] = ntohl(sin_1st.sin_addr.s_addr) & 0xff; /* b3 - IP address 00 = 179 */
csreq[14] = (ntohs(sin_1st.sin_port) >> 8) & 0xff; /* xx - hi-byte local port number for CONNECTION, CONNECTIONSTAT and DISCONNECT requests */
csreq[15] = ntohs(sin_1st.sin_port) & 0xff; /* xx - lo-byte local port number for CONNECTION, CONNECTIONSTAT and DISCONNECT requests */

f.e.: 06 10 02 07 00 10 49 00 08 01 c0 a8 0a b3 d9 6d

byte no. 7 is the given channel-id from step no. 2

4th: receive a CONNECTIONSTATE_RESPONCE
You get an answer like:

f.e.: 06 10 02 08 00 08 49 00

byte no. 8 is the status/error number, zero is perfect!

5th: send a TUNNELLING_REQUEST
Now we send the important cEMI frame. Here it is „switching lightbumb on“ at group address 1/0/2.

/* TUNNELLING_REQUEST */
/* Header (6 Bytes) */
treq[0] = 0x06; /* 06 - Header Length */
treq[1] = 0x10; /* 10 - KNXnet version (1.0) */
treq[2] = 0x04; /* 04 - hi-byte Service type descriptor (TUNNELLING_REQUEST) */
treq[3] = 0x20; /* 20 - lo-byte Service type descriptor (TUNNELLING_REQUEST) */
treq[4] = 0x00; /* 00 - hi-byte total length */
treq[5] = 0x15; /* 15 - lo-byte total lengt 21 bytes */

/* Connection Header (4 Bytes) */
treq[6] = 0x04; /* 04 - Structure length */
treq[7] = iChannelID & 0xff; /* given channel id */
treq[8] = 0x00; /* sequence counter, zero if you send one tunnelling request only at this session, otherwise count ++ */
treq[9] = 0x00; /* 00 - Reserved */

/* cEMI-Frame (11 Bytes) */
treq[10] = 0x11; /* message code, 11: Data Service transmitting */
treq[11] = 0x00; /* add. info length (0 bytes) */
treq[12] = 0xbc; /* control byte */
treq[13] = 0xe0; /* DRL byte */
treq[14] = 0x00; /* hi-byte source individual address */
treq[15] = 0x00; /* lo-byte source (replace throw IP-Gateway) */
treq[16] = (destaddr >> 8) & 0xff; /* hi-byte destination address (20: group address) 4/0/0: (4*2048) + (0*256) + (0*1) = 8192 = 20 00 */
treq[17] = destaddr & 0xff; /* lo-Byte destination */
treq[18] = 0x01; /* 01 data byte following */
treq[19] = 0x00; /* tpdu */
treq[20] = 0x81; /* 81: switch on, 80: off */

f.e.: 06 10 04 20 00 15 04 49 00 00 11 00 bc e0 00 00 08 02 01 00 81

byte no. 11 is the data service. here are the other opportunities
FROM NETWORK LAYER TO DATA LINK LAYER
– L_Raw.req 0x10
– L_Data.req 0x11 Data Service. Primitive used for transmitting a data frame
– L_Poll_Data.req 0x13 Poll Data Service

FROM DATA LINK LAYER TO NETWORK LAYER
– L_Poll_Data.con 0x25 Poll Data Service
– L_Data.ind 0x29 Data Service. Primitive used for receiving a data frame
– L_Busmon.ind 0x2B Bus Monitor Service
– L_Raw.ind 0x2D
– L_Data.con 0x2E Data Service. Primitive used for local confirmation that a frame was sent (does not indicate a successful receive though)
– L_Raw.con 0x2F

byte no. 13 is the control byte, you must calculate them
Control byte=0xBC = 1 0 1 1 11 0 0
– 1: 1 bit (0: extended frame, 1: standard frame)
– 0: 1 bit (0: reserved)
– 1: 1 bit (0: repeat frame on medium in case of a mirror, 1: do not repeat)
– 1: 1 bit (0: system broadcast, 1: broadcast)
– 3: 2 bit (priority – 0: system, 1: normal, 2: urgent, 3: low)
– 0: 1 bit (ackbowledge request – 0: no ack requested, 1: ack requested)
– 0: 1 bit (confirm – 0: no error, 1: error)

byte no. 14 is the DLR byte, you must calculate them
DRL-Byte=0xe0 = 1 110 0000
– 1: 1bit (destination address type – 0: individual address, 1: group address)
– 6: 3bit (hop count – 0-7 110: standard)
– 0: 4bit (extended frame format – 0000: standard frame)

6th: receive a TUNNELLING_ACK
You get an answer like:

f.e.: 06 10 04 21 00 0a 04 49 00 00

byte no. 10 is the status/error number, zero is perfect!

7th: receive a TUNNELLING_REQUEST
Immediately after the TUNNELLING_ACK you get an answer like:

f.e.: 06 10 04 20 00 15 04 49 00 00 2e 00 bd e0 10 01 08 02 01 00 81

That is our tunnelling request with a replaced byte no. 11. It’s not 0x11 (L_Data.req), it is 0x2e (L_Data.con)
byte no. 9: remember the sequence Counter in f.e. variable „iSequenceCounter“.

8th: send a TUNNELLING_ACK
We compared the data and then send a TUNNELLING_ACK.

/* TUNNELLING_ACK */
/* Header (6 Bytes) */
tack[0] = 0x06; /* 06 - Header Length */
tack[1] = 0x10; /* 10 - KNXnet version (1.0) */
tack[2] = 0x04; /* 04 - hi-byte Service type descriptor (TUNNELLING_ACK) */
tack[3] = 0x21; /* 21 - lo-byte Service type descriptor (TUNNELLING_ACK) */
tack[4] = 0x00; /* 00 - hi-byte total length */
tack[5] = 0x0A; /* 0A - lo-byte total lengt 10 bytes */

/* ConnectionHeader (4 Bytes) */
tack[6] = 0x04; /* 04 - Structure length */
tack[7] = iChannelID & 0xff; /* given channel id */
tack[8] = iSequenceCounter & 0x01; /* 01 the sequence counter from 7th: receive a TUNNELLING_REQUEST */
tack[9] = 0x00; /* 00 our error code */

f.e.: 06 10 04 21 00 0a 04 49 01 00

9th: send a DISCONNECT_REQUEST

/* DISCONNECT_REQUEST */
/* header */
dcreq[0] = 0x06; /* 06 - Header Length */
dcreq[1] = 0x10; /* 10 - KNXnet version (1.0) */
dcreq[2] = 0x02; /* 02 - hi-byte Service type descriptor (DISCONNECT_REQUEST) */
dcreq[3] = 0x09; /* 09 - lo-byte Service type descriptor (DISCONNECT_REQUEST) */
dcreq[4] = 0x00; /* 00 - hi-byte total length */
dcreq[5] = 0x10; /* 10 - lo-byte total lengt 16 Bytes */

/* data (10 Bytes) */
dcreq[6] = iChannelID & 0xff; /* given channel id */
dcreq[7] = 0x00; /* 00 - */
dcreq[8] = 0x08; /* 08 - Host Protocol Address Information (HPAI) Lenght */
dcreq[9] = 0x01; /* 01 - Host Protocol Code 0x01 -> IPV4_UDP, 0x02 -> IPV6_TCP */
dcreq[10] = ntohl(sin_1st.sin_addr.s_addr) >> 24 & 0xff; /* c0 - IP address c0 = 192 */
dcreq[11] = ntohl(sin_1st.sin_addr.s_addr) >> 16 & 0xff; /* a8 - IP address a8 = 168 */
dcreq[12] = ntohl(sin_1st.sin_addr.s_addr) >> 8 & 0xff; /* 0a - IP address 0a = 10 */
dcreq[13] = ntohl(sin_1st.sin_addr.s_addr) & 0xff; /* b3 - IP address 9F = 179 */
dcreq[14] = (ntohs(sin_1st.sin_port) >> 8) & 0xff; /* 0e - hi-byte local port number for CONNECTION, CONNECTIONSTAT and DISCONNECT requests */
dcreq[15] = ntohs(sin_1st.sin_port) & 0xff; /* 57 - lo-byte local port number for CONNECTION, CONNECTIONSTAT and DISCONNECT requests */

f.e.: 06 10 02 09 00 10 49 00 08 01 c0 a8 0a b3 d9 6d

10th: receive DISCONNECT_RESPONCE
You get an answer like:

f.e.: 06 10 02 0a 00 08 49 00

byte no. 8 is the status/error number, zero is perfect!

That’s all. Any questions?

Wireshark Example:

wireshark

Now a knx tool:

To control the light bulbs in a knx house, you can buy a expensive visualisation or use an iPhone with siri.

You don’t need an jailbreak! You need an Rasperry-PI with siriproxy and a little command line tool, which broadcast a knx datagram to the KNX gateway.

Here is a good tutorial for the installation of siriproxy.

SiriProxy auf Raspberry Pi

Is it done, you can download the command-line-tool. Two versions: for ARM and LINIX x86 platform.

knxcmd_1_0_3

Extract the zip file and put in in /bin directory. Then make it executable:

> chmod +x knxcmd

Now execute:

> knxcmd -?

you get this:

Usage: ./knxcmd [-d?] [-i ipaddress] [-p port] [-g groupaddress] [-c command]
-i 192.168.10.10 Interface IP address
-p 3671 Specified port
-g 1/1/90 Group address of receiver
-c 81 Command byte in hex
-d Shows debug infos
-? This help info

Now a sample to switch on the lightbulb on address 1/1/90.

> knxcmd -d -i 192.168.10.14 -p 3671 -g 1/1/90 -c 81

and to switch off

> knxcmd -d -i 192.168.10.14 -p 3671 -g 1/1/90 -c 80

Feel free!

The Sourcecode can be purchased: 50 Euro via paypal.

If you need an other sourcecode to control, to switch, to dim, read status or set binary values via KNXnetIP, please contract me.

(reverse engineering by german law: § 69d UrhG Nr.3)

Schreibe einen Kommentar

Translate »