knx
ETS configurable knx-stack
cemi_frame.cpp
Go to the documentation of this file.
1 #include "cemi_frame.h"
2 #include "bits.h"
3 #include "string.h"
4 #include <stdio.h>
5 
6 /*
7 cEMI Frame Format
8 
9  +--------+--------+--------+--------+---------+---------+--------+---------+
10  | _data |
11  +--------+--------+--------+--------+---------+---------+--------+---------+
12  | LPDU |
13  +--------+--------+--------+--------+---------+---------+--------+---------+
14  | NPDU |
15  +---------+--------+--------+--------+--------+---------+---------+--------+---------+
16  | Header | Msg |Add.Info| Ctrl 1 | Ctrl 2 | Source | Dest. | Data | TPDU |
17  | | Code | Length | | | Address | Address | Length | APDU |
18  +---------+--------+--------+--------+--------+---------+---------+--------+---------+
19  6 bytes 1 byte 1 byte 1 byte 1 byte 2 bytes 2 bytes 1 byte n bytes
20 
21  Header = See below the structure of a cEMI header
22  Message Code = See below. On Appendix A is the list of all existing EMI and cEMI codes
23  Add.Info Length = 0x00 - no additional info
24  Control Field 1 =
25  Control Field 2 =
26  Source Address = 0x0000 - filled in by router/gateway with its source address which is
27  part of the KNX subnet
28  Dest. Address = KNX group or individual address (2 byte)
29  Data Length = Number of bytes of data in the APDU excluding the TPCI/APCI bits
30  APDU = Application Protocol Data Unit - the actual payload including transport
31  protocol control information (TPCI), application protocol control
32  information (APCI) and data passed as an argument from higher layers of
33  the KNX communication stack
34 
35 Control Field 1
36 
37  Bit |
38  ------+---------------------------------------------------------------
39  7 | Frame Type - 0x0 for extended frame
40  | 0x1 for standard frame
41  ------+---------------------------------------------------------------
42  6 | Reserved
43  |
44  ------+---------------------------------------------------------------
45  5 | Repeat Flag - 0x0 repeat frame on medium in case of an error
46  | 0x1 do not repeat
47  ------+---------------------------------------------------------------
48  4 | System Broadcast - 0x0 system broadcast
49  | 0x1 broadcast
50  ------+---------------------------------------------------------------
51  3 | Priority - 0x0 system
52  | 0x1 normal
53  ------+ 0x2 urgent
54  2 | 0x3 low
55  |
56  ------+---------------------------------------------------------------
57  1 | Acknowledge Request - 0x0 no ACK requested
58  | (L_Data.req) 0x1 ACK requested
59  ------+---------------------------------------------------------------
60  0 | Confirm - 0x0 no error
61  | (L_Data.con) - 0x1 error
62  ------+---------------------------------------------------------------
63 
64  Control Field 2
65 
66  Bit |
67  ------+---------------------------------------------------------------
68  7 | Destination Address Type - 0x0 individual address
69  | - 0x1 group address
70  ------+---------------------------------------------------------------
71  6-4 | Hop Count (0-7)
72  ------+---------------------------------------------------------------
73  3-0 | Extended Frame Format - 0x0 standard frame
74  ------+---------------------------------------------------------------
75 */
76 
77 CemiFrame::CemiFrame(uint8_t* data, uint16_t length)
78  : _npdu(data + data[1] + NPDU_LPDU_DIFF, *this),
79  _tpdu(data + data[1] + TPDU_LPDU_DIFF, *this),
80  _apdu(data + data[1] + APDU_LPDU_DIFF, *this)
81 {
82  _data = data;
83  _ctrl1 = data + data[1] + CEMI_HEADER_SIZE;
84  _length = length;
85 }
86 
87 CemiFrame::CemiFrame(uint8_t apduLength)
88  : _data(buffer),
89  _npdu(_data + NPDU_LPDU_DIFF, *this),
90  _tpdu(_data + TPDU_LPDU_DIFF, *this),
91  _apdu(_data + APDU_LPDU_DIFF, *this)
92 {
93  _ctrl1 = _data + CEMI_HEADER_SIZE;
94 
95  memset(_data, 0, apduLength + APDU_LPDU_DIFF);
96  _ctrl1[0] |= Broadcast;
97  _npdu.octetCount(apduLength);
98  _length = _npdu.length() + NPDU_LPDU_DIFF;
99 }
100 
102  : _data(buffer),
103  _npdu(_data + NPDU_LPDU_DIFF, *this),
104  _tpdu(_data + TPDU_LPDU_DIFF, *this),
105  _apdu(_data + APDU_LPDU_DIFF, *this)
106 {
107  _ctrl1 = _data + CEMI_HEADER_SIZE;
108  _length = other._length;
109 
110  memcpy(_data, other._data, other.totalLenght());
111 }
112 
114 {
115  _length = other._length;
116  _data = buffer;
117  _ctrl1 = _data + CEMI_HEADER_SIZE;
118  memcpy(_data, other._data, other.totalLenght());
119  _npdu._data = _data + NPDU_LPDU_DIFF;
120  _tpdu._data = _data + TPDU_LPDU_DIFF;
121  _apdu._data = _data + APDU_LPDU_DIFF;
122  return *this;
123 }
124 
125 
127 {
128  return (MessageCode)_data[0];
129 }
130 
132 {
133  _data[0] = msgCode;
134 }
135 
136 uint16_t CemiFrame::totalLenght() const
137 {
138  return _length;
139 }
140 
142 {
143  if (frameType() == StandardFrame)
144  return totalLenght() - 2; /*-AddInfo -MsgCode - only one CTRL + CRC, */
145  else
146  return totalLenght() - 1; /*-AddInfo -MsgCode + CRC, */
147 }
148 
149 void CemiFrame::fillTelegramTP(uint8_t* data)
150 {
151  uint16_t len = telegramLengthtTP();
152 
153  if (frameType() == StandardFrame)
154  {
155  uint8_t octet5 = (_ctrl1[1] & 0xF0) | (_ctrl1[6] & 0x0F);
156  data[0] = _ctrl1[0]; //CTRL
157  memcpy(data + 1, _ctrl1 + 2, 4); // SA, DA
158  data[5] = octet5; // LEN; Hopcount, ..
159  memcpy(data + 6, _ctrl1 + 7, len - 7); // APDU
160  }
161  else
162  {
163  memcpy(data, _ctrl1, len - 1);
164  }
165 
166  data[len - 1] = calcCrcTP(data, len - 1);
167 }
168 
169 #ifdef USE_RF
170 
172 {
173  return totalLenght() - 3;
174 }
175 
176 void CemiFrame::fillTelegramRF(uint8_t* data)
177 {
178  uint16_t len = telegramLengthtRF();
179 
180  // We prepare the actual KNX telegram for RF here only.
181  // The packaging into blocks with CRC16 (Format based on FT3 Data Link Layer (IEC 870-5))
182  // is done in the RF Data Link Layer code.
183  // RF always uses the Extended Frame Format. However, the length field is missing (right before the APDU)
184  // as there is already a length field at the beginning of the raw RF frame which is also used by the
185  // physical layer to control the HW packet engine of the transceiver.
186 
187  data[0] = _ctrl1[1] & 0x0F; // KNX CTRL field for RF (bits 3..0 EFF only), bits 7..4 are set to 0 for asynchronous RF frames
188  memcpy(data + 1, _ctrl1 + 2, 4); // SA, DA
189  data[5] = (_ctrl1[1] & 0xF0) | ((_rfLfn & 0x7) << 1) | ((_ctrl1[0] & 0x10) >> 4); // L/NPCI field: AT, Hopcount, LFN, AET
190  memcpy(data + 6, _ctrl1 + 7, len - 6); // APDU
191 
192  //printHex("cEMI_fill: ", &data[0], len);
193 }
194 #endif
195 uint8_t* CemiFrame::data()
196 {
197  return _data;
198 }
199 
201 {
202  return _length;
203 }
204 
205 uint8_t CemiFrame::calcCrcTP(uint8_t* buffer, uint16_t len)
206 {
207  uint8_t crc = 0xFF;
208 
209  for (uint16_t i = 0; i < len; i++)
210  crc ^= buffer[i];
211 
212  return crc;
213 }
214 
216 {
217  return (FrameFormat)(_ctrl1[0] & StandardFrame);
218 }
219 
221 {
222  _ctrl1[0] &= ~StandardFrame;
223  _ctrl1[0] |= type;
224 }
225 
227 {
228  return (Repetition)(_ctrl1[0] & RepetitionAllowed);
229 }
230 
232 {
233  _ctrl1[0] &= ~RepetitionAllowed;
234  _ctrl1[0] |= rep;
235 }
236 
238 {
239  return (SystemBroadcast)(_ctrl1[0] & Broadcast);
240 }
241 
243 {
244  _ctrl1[0] &= ~Broadcast;
245  _ctrl1[0] |= value;
246 }
247 
249 {
250  return (Priority)(_ctrl1[0] & LowPriority);
251 }
252 
254 {
255  _ctrl1[0] &= ~LowPriority;
256  _ctrl1[0] |= value;
257 }
258 
260 {
261  return (AckType)(_ctrl1[0] & AckRequested);
262 }
263 
265 {
266  _ctrl1[0] &= ~AckRequested;
267  _ctrl1[0] |= value;
268 }
269 
271 {
272  return (Confirm)(_ctrl1[0] & ConfirmError);
273 }
274 
276 {
277  _ctrl1[0] &= ~ConfirmError;
278  _ctrl1[0] |= value;
279 }
280 
282 {
283  return (AddressType)(_ctrl1[1] & GroupAddress);
284 }
285 
287 {
288  _ctrl1[1] &= ~GroupAddress;
289  _ctrl1[1] |= value;
290 }
291 
292 uint8_t CemiFrame::hopCount() const
293 {
294  return ((_ctrl1[1] >> 4) & 0x7);
295 }
296 
297 void CemiFrame::hopCount(uint8_t value)
298 {
299  _ctrl1[1] &= ~(0x7 << 4);
300  _ctrl1[1] |= ((value & 0x7) << 4);
301 }
302 
303 uint16_t CemiFrame::sourceAddress() const
304 {
305  uint16_t addr;
306  popWord(addr, _ctrl1 + 2);
307  return addr;
308 }
309 
310 void CemiFrame::sourceAddress(uint16_t value)
311 {
312  pushWord(value, _ctrl1 + 2);
313 }
314 
316 {
317  uint16_t addr;
318  popWord(addr, _ctrl1 + 4);
319  return addr;
320 }
321 
322 void CemiFrame::destinationAddress(uint16_t value)
323 {
324  pushWord(value, _ctrl1 + 4);
325 }
326 #ifdef USE_RF
327 uint8_t* CemiFrame::rfSerialOrDoA() const
328 {
329  return _rfSerialOrDoA;
330 }
331 
332 void CemiFrame::rfSerialOrDoA(const uint8_t* rfSerialOrDoA)
333 {
334  _rfSerialOrDoA = (uint8_t*)rfSerialOrDoA;
335 }
336 
337 uint8_t CemiFrame::rfInfo() const
338 {
339  return _rfInfo;
340 }
341 
342 void CemiFrame::rfInfo(uint8_t rfInfo)
343 {
344  _rfInfo = rfInfo;
345 }
346 
347 uint8_t CemiFrame::rfLfn() const
348 {
349  return _rfLfn;
350 }
351 
352 void CemiFrame::rfLfn(uint8_t rfLfn)
353 {
354  _rfLfn = rfLfn;
355 }
356 #endif
358 {
359  return _npdu;
360 }
361 
363 {
364  return _tpdu;
365 }
366 
368 {
369  return _apdu;
370 }
371 
372 bool CemiFrame::valid() const
373 {
374  uint8_t addInfoLen = _data[1];
375  uint8_t apduLen = _data[_data[1] + NPDU_LPDU_DIFF];
376 
377  if (_length != 0 && _length != (addInfoLen + apduLen + NPDU_LPDU_DIFF + 2))
378  {
379  print("length issue, length: ");
380  print(_length);
381  print(" addInfoLen: ");
382  print(addInfoLen);
383  print(" apduLen: ");
384  print(apduLen);
385  print(" expected length: ");
386  println(addInfoLen + apduLen + NPDU_LPDU_DIFF + 2);
387  printHex("Frame: ", _data, _length, true);
388 
389  return false;
390  }
391 
392  if ((_ctrl1[0] & 0x40) > 0 // Bit 6 has do be 0
393  || (_ctrl1[1] & 0xF) > 0 // only standard or extended frames
394  || _npdu.octetCount() == 0xFF // not allowed
395  || (_npdu.octetCount() > 15 && frameType() == StandardFrame)
396  )
397  {
398  print("Other issue");
399  return false;
400  }
401 
402  return true;
403 }
void print(const char *s)
void println(const char *s)
void printHex(const char *suffix, const uint8_t *data, size_t length, bool newline)
Definition: bits.cpp:12
uint8_t * pushWord(uint16_t w, uint8_t *data)
Definition: bits.cpp:64
const uint8_t * popWord(uint16_t &w, const uint8_t *data)
Definition: bits.cpp:34
This class represents an Application Protocol Data Unit.
Definition: apdu.h:12
void fillTelegramRF(uint8_t *data)
Definition: cemi_frame.cpp:176
CemiFrame & operator=(CemiFrame other)
Definition: cemi_frame.cpp:113
SystemBroadcast systemBroadcast() const
Definition: cemi_frame.cpp:237
NPDU & npdu()
Definition: cemi_frame.cpp:357
uint16_t sourceAddress() const
Definition: cemi_frame.cpp:303
uint8_t * rfSerialOrDoA() const
Definition: cemi_frame.cpp:327
TPDU & tpdu()
Definition: cemi_frame.cpp:362
uint8_t hopCount() const
Definition: cemi_frame.cpp:292
void fillTelegramTP(uint8_t *data)
Definition: cemi_frame.cpp:149
bool valid() const
Definition: cemi_frame.cpp:372
CemiFrame(uint8_t *data, uint16_t length)
Definition: cemi_frame.cpp:77
uint8_t * data()
Definition: cemi_frame.cpp:195
AckType ack() const
Definition: cemi_frame.cpp:259
Repetition repetition() const
Definition: cemi_frame.cpp:226
uint8_t rfLfn() const
Definition: cemi_frame.cpp:347
APDU & apdu()
Definition: cemi_frame.cpp:367
uint8_t rfInfo() const
Definition: cemi_frame.cpp:337
uint16_t destinationAddress() const
Definition: cemi_frame.cpp:315
Confirm confirm() const
Definition: cemi_frame.cpp:270
MessageCode messageCode() const
Definition: cemi_frame.cpp:126
AddressType addressType() const
Definition: cemi_frame.cpp:281
uint16_t telegramLengthtTP() const
Definition: cemi_frame.cpp:141
uint16_t dataLength()
Definition: cemi_frame.cpp:200
FrameFormat frameType() const
Definition: cemi_frame.cpp:215
uint8_t calcCrcTP(uint8_t *buffer, uint16_t len)
Definition: cemi_frame.cpp:205
Priority priority() const
Definition: cemi_frame.cpp:248
uint16_t telegramLengthtRF() const
Definition: cemi_frame.cpp:171
uint16_t totalLenght() const
Definition: cemi_frame.cpp:136
Definition: npdu.h:9
uint8_t octetCount() const
Definition: npdu.cpp:11
uint8_t length() const
Definition: npdu.cpp:21
Definition: tpdu.h:9
Confirm
Definition: knx_types.h:118
@ ConfirmError
Definition: knx_types.h:120
Repetition
Definition: knx_types.h:104
@ RepetitionAllowed
Definition: knx_types.h:107
Priority
Definition: knx_types.h:10
@ LowPriority
Normal priority of group communication.
Definition: knx_types.h:11
FrameFormat
Definition: knx_types.h:4
@ StandardFrame
Definition: knx_types.h:6
MessageCode
Definition: knx_types.h:39
AckType
Definition: knx_types.h:18
@ AckRequested
We want a DataLinkLayer acknowledgement.
Definition: knx_types.h:20
AddressType
Definition: knx_types.h:33
@ GroupAddress
Definition: knx_types.h:35
SystemBroadcast
Definition: knx_types.h:112
@ Broadcast
Definition: knx_types.h:114