├── KnxTpUart ├── KnxTelegram.cpp ├── KnxTelegram.h ├── KnxTpUart.cpp ├── KnxTpUart.h └── examples │ ├── GroupRead │ └── GroupRead.ino │ ├── GroupWrite │ └── GroupWrite.ino │ ├── LearnKNXAddress │ └── LearnKNXAddress.ino │ ├── ReceiveKNXTelegrams │ └── ReceiveKNXTelegrams.ino │ ├── ReplyToKNXRead │ └── ReplyToKNXRead.ino │ ├── TemperatureReadoutOneWire │ └── TemperatureReadoutOneWire.ino │ └── UnitTests │ └── UnitTests.ino └── README.md /KnxTpUart/KnxTelegram.cpp: -------------------------------------------------------------------------------- 1 | // File: KnxTelegram.cpp 2 | // Author: Daniel Kleine-Albers (Since 2012) 3 | // Modified: Thorsten Gehrig (Since 2014) 4 | // Modified: Michael Werski (Since 2014) 5 | // Modified: Katja Blankenheim (Since 2014) 6 | // Modified: Mag Gyver (Since 2016) 7 | // Modified: Rouven Raudzus (Since 2017) 8 | 9 | // Last modified: 06.06.2017 10 | 11 | #include "KnxTelegram.h" 12 | 13 | KnxTelegram::KnxTelegram() { 14 | clear(); 15 | } 16 | 17 | void KnxTelegram::clear() { 18 | for (int i = 0; i < MAX_KNX_TELEGRAM_SIZE; i++) { 19 | buffer[i] = 0; 20 | } 21 | 22 | // Control Field, Normal Priority, No Repeat 23 | buffer[0] = B10111100; 24 | 25 | // Target Group Address, Routing Counter = 6, Length = 1 (= 2 Bytes) 26 | buffer[5] = B11100001; 27 | } 28 | 29 | int KnxTelegram::getBufferByte(int index) { 30 | return buffer[index]; 31 | } 32 | 33 | void KnxTelegram::setBufferByte(int index, int content) { 34 | buffer[index] = content; 35 | } 36 | 37 | bool KnxTelegram::isRepeated() { 38 | // Parse Repeat Flag 39 | if (buffer[0] & B00100000) { 40 | return false; 41 | } 42 | else { 43 | return true; 44 | } 45 | } 46 | 47 | void KnxTelegram::setRepeated(bool repeat) { 48 | if (repeat) { 49 | buffer[0] = buffer[0] & B11011111; 50 | } 51 | else { 52 | buffer[0] = buffer[0] | B00100000; 53 | } 54 | } 55 | 56 | void KnxTelegram::setPriority(KnxPriorityType prio) { 57 | buffer[0] = buffer[0] & B11110011; 58 | buffer[0] = buffer[0] | (prio << 2); 59 | } 60 | 61 | KnxPriorityType KnxTelegram::getPriority() { 62 | // Priority 63 | return (KnxPriorityType) ((buffer[0] & B00001100) >> 2); 64 | } 65 | 66 | void KnxTelegram::setSourceAddress(int area, int line, int member) { 67 | buffer[1] = (area << 4) | line; // Source Address 68 | buffer[2] = member; // Source Address 69 | } 70 | 71 | int KnxTelegram::getSourceArea() { 72 | return (buffer[1] >> 4); 73 | } 74 | 75 | int KnxTelegram::getSourceLine() { 76 | return (buffer[1] & B00001111); 77 | } 78 | 79 | int KnxTelegram::getSourceMember() { 80 | return buffer[2]; 81 | } 82 | 83 | void KnxTelegram::setTargetGroupAddress(int main, int middle, int sub) { 84 | buffer[3] = (main << 3) | middle; 85 | buffer[4] = sub; 86 | buffer[5] = buffer[5] | B10000000; 87 | } 88 | 89 | void KnxTelegram::setTargetIndividualAddress(int area, int line, int member) { 90 | buffer[3] = (area << 4) | line; 91 | buffer[4] = member; 92 | buffer[5] = buffer[5] & B01111111; 93 | } 94 | 95 | bool KnxTelegram::isTargetGroup() { 96 | return buffer[5] & B10000000; 97 | } 98 | 99 | int KnxTelegram::getTargetMainGroup() { 100 | return ((buffer[3] & B01111000) >> 3); 101 | } 102 | 103 | int KnxTelegram::getTargetMiddleGroup() { 104 | return (buffer[3] & B00000111); 105 | } 106 | 107 | int KnxTelegram::getTargetSubGroup() { 108 | return buffer[4]; 109 | } 110 | 111 | int KnxTelegram::getTargetArea() { 112 | return ((buffer[3] & B11110000) >> 4); 113 | } 114 | 115 | int KnxTelegram::getTargetLine() { 116 | return (buffer[3] & B00001111); 117 | } 118 | 119 | int KnxTelegram::getTargetMember() { 120 | return buffer[4]; 121 | } 122 | 123 | void KnxTelegram::setRoutingCounter(int counter) { 124 | buffer[5] = buffer[5] & B10000000; 125 | buffer[5] = buffer[5] | (counter << 4); 126 | } 127 | 128 | int KnxTelegram::getRoutingCounter() { 129 | return ((buffer[5] & B01110000) >> 4); 130 | } 131 | 132 | void KnxTelegram::setPayloadLength(int length) { 133 | buffer[5] = buffer[5] & B11110000; 134 | buffer[5] = buffer[5] | (length - 1); 135 | } 136 | 137 | int KnxTelegram::getPayloadLength() { 138 | int length = (buffer[5] & B00001111) + 1; 139 | return length; 140 | } 141 | 142 | void KnxTelegram::setCommand(KnxCommandType command) { 143 | buffer[6] = buffer[6] & B11111100; 144 | buffer[7] = buffer[7] & B00111111; 145 | 146 | buffer[6] = buffer[6] | (command >> 2); // Command first two bits 147 | buffer[7] = buffer[7] | (command << 6); // Command last two bits 148 | } 149 | 150 | KnxCommandType KnxTelegram::getCommand() { 151 | return (KnxCommandType) (((buffer[6] & B00000011) << 2) | ((buffer[7] & B11000000) >> 6)); 152 | } 153 | 154 | void KnxTelegram::setControlData(KnxControlDataType cd) { 155 | buffer[6] = buffer[6] & B11111100; 156 | buffer[6] = buffer[6] | cd; 157 | } 158 | 159 | KnxControlDataType KnxTelegram::getControlData() { 160 | return (KnxControlDataType) (buffer[6] & B00000011); 161 | } 162 | 163 | KnxCommunicationType KnxTelegram::getCommunicationType() { 164 | return (KnxCommunicationType) ((buffer[6] & B11000000) >> 6); 165 | } 166 | 167 | void KnxTelegram::setCommunicationType(KnxCommunicationType type) { 168 | buffer[6] = buffer[6] & B00111111; 169 | buffer[6] = buffer[6] | (type << 6); 170 | } 171 | 172 | int KnxTelegram::getSequenceNumber() { 173 | return (buffer[6] & B00111100) >> 2; 174 | } 175 | 176 | void KnxTelegram::setSequenceNumber(int number) { 177 | buffer[6] = buffer[6] & B11000011; 178 | buffer[6] = buffer[6] | (number << 2); 179 | } 180 | 181 | void KnxTelegram::createChecksum() { 182 | int checksumPos = getPayloadLength() + KNX_TELEGRAM_HEADER_SIZE; 183 | buffer[checksumPos] = calculateChecksum(); 184 | } 185 | 186 | int KnxTelegram::getChecksum() { 187 | int checksumPos = getPayloadLength() + KNX_TELEGRAM_HEADER_SIZE; 188 | return buffer[checksumPos]; 189 | } 190 | 191 | bool KnxTelegram::verifyChecksum() { 192 | int calculatedChecksum = calculateChecksum(); 193 | return (getChecksum() == calculatedChecksum); 194 | } 195 | 196 | void KnxTelegram::print(TPUART_SERIAL_CLASS* serial) { 197 | #if defined(TPUART_DEBUG) 198 | serial->print("Repeated: "); 199 | serial->println(isRepeated()); 200 | 201 | serial->print("Priority: "); 202 | serial->println(getPriority()); 203 | 204 | serial->print("Source: "); 205 | serial->print(getSourceArea()); 206 | serial->print("."); 207 | serial->print(getSourceLine()); 208 | serial->print("."); 209 | serial->println(getSourceMember()); 210 | 211 | if (isTargetGroup()) { 212 | serial->print("Target Group: "); 213 | serial->print(getTargetMainGroup()); 214 | serial->print("/"); 215 | serial->print(getTargetMiddleGroup()); 216 | serial->print("/"); 217 | serial->println(getTargetSubGroup()); 218 | } 219 | else { 220 | serial->print("Target Physical: "); 221 | serial->print(getTargetArea()); 222 | serial->print("."); 223 | serial->print(getTargetLine()); 224 | serial->print("."); 225 | serial->println(getTargetMember()); 226 | } 227 | 228 | serial->print("Routing Counter: "); 229 | serial->println(getRoutingCounter()); 230 | 231 | serial->print("Payload Length: "); 232 | serial->println(getPayloadLength()); 233 | 234 | serial->print("Command: "); 235 | serial->println(getCommand()); 236 | 237 | serial->print("First Data Byte: "); 238 | serial->println(getFirstDataByte()); 239 | 240 | for (int i = 2; i < getPayloadLength(); i++) { 241 | serial->print("Data Byte "); 242 | serial->print(i); 243 | serial->print(": "); 244 | serial->println(buffer[6 + i], BIN); 245 | } 246 | 247 | 248 | if (verifyChecksum()) { 249 | serial->println("Checksum matches"); 250 | } 251 | else { 252 | serial->println("Checksum mismatch"); 253 | serial->println(getChecksum(), BIN); 254 | serial->println(calculateChecksum(), BIN); 255 | } 256 | #endif 257 | } 258 | 259 | int KnxTelegram::calculateChecksum() { 260 | int bcc = 0xFF; 261 | int size = getPayloadLength() + KNX_TELEGRAM_HEADER_SIZE; 262 | 263 | for (int i = 0; i < size; i++) { 264 | bcc ^= buffer[i]; 265 | } 266 | 267 | return bcc; 268 | } 269 | 270 | int KnxTelegram::getTotalLength() { 271 | return KNX_TELEGRAM_HEADER_SIZE + getPayloadLength() + 1; 272 | } 273 | 274 | void KnxTelegram::setFirstDataByte(int data) { 275 | buffer[7] = buffer[7] & B11000000; 276 | buffer[7] = buffer[7] | data; 277 | } 278 | 279 | int KnxTelegram::getFirstDataByte() { 280 | return (buffer[7] & B00111111); 281 | } 282 | 283 | bool KnxTelegram::getBool() { 284 | if (getPayloadLength() != 2) { 285 | // Wrong payload length 286 | return 0; 287 | } 288 | 289 | return (getFirstDataByte() & B00000001); 290 | } 291 | 292 | int KnxTelegram::get4BitIntValue() { 293 | if (getPayloadLength() != 2) { 294 | // Wrong payload length 295 | return 0; 296 | } 297 | 298 | return (getFirstDataByte() & B00001111); 299 | } 300 | 301 | bool KnxTelegram::get4BitDirectionValue() { 302 | if (getPayloadLength() != 2) { 303 | // Wrong payload length 304 | return 0; 305 | } 306 | 307 | return ((getFirstDataByte() & B00001000)) >> 3; 308 | } 309 | 310 | byte KnxTelegram::get4BitStepsValue() { 311 | if (getPayloadLength() != 2) { 312 | // Wrong payload length 313 | return 0; 314 | } 315 | 316 | return (getFirstDataByte() & B00000111); 317 | } 318 | 319 | void KnxTelegram::set1ByteIntValue(int value) { 320 | setPayloadLength(3); 321 | buffer[8] = value; 322 | } 323 | 324 | int KnxTelegram::get1ByteIntValue() { 325 | if (getPayloadLength() != 3) { 326 | // Wrong payload length 327 | return 0; 328 | } 329 | 330 | return (buffer[8]); 331 | } 332 | 333 | void KnxTelegram::set2ByteIntValue(int value) { 334 | setPayloadLength(4); 335 | 336 | buffer[8] = byte(value >> 8); 337 | buffer[9] = byte(value & 0x00FF); 338 | } 339 | 340 | int KnxTelegram::get2ByteIntValue() { 341 | if (getPayloadLength() != 4) { 342 | // Wrong payload length 343 | return 0; 344 | } 345 | int value = int(buffer[8] << 8) + int(buffer[9]); 346 | 347 | return (value); 348 | } 349 | 350 | void KnxTelegram::set2ByteFloatValue(float value) { 351 | setPayloadLength(4); 352 | 353 | float v = value * 100.0f; 354 | int exponent = 0; 355 | for (; v < -2048.0f; v /= 2) exponent++; 356 | for (; v > 2047.0f; v /= 2) exponent++; 357 | long m = round(v) & 0x7FF; 358 | short msb = (short) (exponent << 3 | m >> 8); 359 | if (value < 0.0f) msb |= 0x80; 360 | buffer[8] = msb; 361 | buffer[9] = (byte)m; 362 | } 363 | 364 | float KnxTelegram::get2ByteFloatValue() { 365 | if (getPayloadLength() != 4) { 366 | // Wrong payload length 367 | return 0; 368 | } 369 | 370 | int exponent = (buffer[8] & B01111000) >> 3; 371 | int mantissa = ((buffer[8] & B00000111) << 8) | (buffer[9]); 372 | 373 | if (buffer[8] & B10000000) { 374 | return ((-2048 + mantissa) * 0.01) * pow(2.0, exponent); // Thanks to Rouven Raudzus for the note 375 | } 376 | 377 | return (mantissa * 0.01) * pow(2.0, exponent); 378 | } 379 | 380 | void KnxTelegram::set3ByteTime(int weekday, int hour, int minute, int second) { 381 | setPayloadLength(5); 382 | 383 | // Move the weekday by 5 bits to the left 384 | weekday = weekday << 5; 385 | 386 | // Buffer [8] bit 5-7 for weekday, bit 0-4 for hour 387 | buffer[8] = (weekday & B11100000) + (hour & B00011111); 388 | 389 | // Buffer [9] bit 6-7 empty, bit 0-5 for minutes 390 | buffer[9] = minute & B00111111; 391 | 392 | // Buffer [10] bit 6-7 empty, bit 0-5 for seconds 393 | buffer[10] = second & B00111111; 394 | } 395 | 396 | int KnxTelegram::get3ByteWeekdayValue() { 397 | if (getPayloadLength() != 5) { 398 | // Wrong payload length 399 | return 0; 400 | } 401 | return (buffer[8] & B11100000) >> 5; 402 | } 403 | 404 | int KnxTelegram::get3ByteHourValue() { 405 | if (getPayloadLength() != 5) { 406 | // Wrong payload length 407 | return 0; 408 | } 409 | return (buffer[8] & B00011111); 410 | } 411 | 412 | int KnxTelegram::get3ByteMinuteValue() { 413 | if (getPayloadLength() != 5) { 414 | // Wrong payload length 415 | return 0; 416 | } 417 | return (buffer[9] & B00111111); 418 | } 419 | 420 | int KnxTelegram::get3ByteSecondValue() { 421 | if (getPayloadLength() != 5) { 422 | // Wrong payload length 423 | return 0; 424 | } 425 | return (buffer[10] & B00111111); 426 | } 427 | 428 | void KnxTelegram::set3ByteDate(int day, int month, int year) { 429 | setPayloadLength(5); 430 | 431 | // Buffer [8] bit 5-7 empty, bit 0-4 for month days 432 | buffer[8] = day & B00011111; 433 | 434 | // Buffer [9] bit 4-7 empty, bit 0-3 for months 435 | buffer[9] = month & B00001111; 436 | 437 | // Buffer [10] fill with year 438 | buffer[10] = year; 439 | } 440 | 441 | int KnxTelegram::get3ByteDayValue() { 442 | if (getPayloadLength() != 5) { 443 | // Wrong payload length 444 | return 0; 445 | } 446 | return (buffer[8] & B00011111); 447 | } 448 | 449 | int KnxTelegram::get3ByteMonthValue() { 450 | if (getPayloadLength() != 5) { 451 | // Wrong payload length 452 | return 0; 453 | } 454 | return (buffer[9] & B00001111); 455 | } 456 | 457 | int KnxTelegram::get3ByteYearValue() { 458 | if (getPayloadLength() != 5) { 459 | // Wrong payload length 460 | return 0; 461 | } 462 | return (buffer[10]); 463 | } 464 | 465 | void KnxTelegram::set4ByteFloatValue(float value) { 466 | setPayloadLength(6); 467 | 468 | byte b[4]; 469 | float *f = (float*)(void*) & (b[0]); 470 | *f = value; 471 | 472 | buffer[8 + 3] = b[0]; 473 | buffer[8 + 2] = b[1]; 474 | buffer[8 + 1] = b[2]; 475 | buffer[8 + 0] = b[3]; 476 | } 477 | 478 | float KnxTelegram::get4ByteFloatValue() { 479 | if (getPayloadLength() != 6) { 480 | // Wrong payload length 481 | return 0; 482 | } 483 | byte b[4]; 484 | b[0] = buffer[8 + 3]; 485 | b[1] = buffer[8 + 2]; 486 | b[2] = buffer[8 + 1]; 487 | b[3] = buffer[8 + 0]; 488 | float *f = (float*)(void*) & (b[0]); 489 | float r = *f; 490 | return r; 491 | } 492 | 493 | void KnxTelegram::set14ByteValue(String value) { 494 | // Define 495 | char _load[15]; 496 | 497 | // Empty/Initialize with space 498 | for (int i = 0; i < 14; ++i) 499 | { 500 | _load[i] = 0; 501 | } 502 | setPayloadLength(16); 503 | // Make out of value the Chararray 504 | value.toCharArray(_load, 15); // Must be 15 - because it completes with 0 505 | buffer[8 + 0] = _load [0]; 506 | buffer[8 + 1] = _load [1]; 507 | buffer[8 + 2] = _load [2]; 508 | buffer[8 + 3] = _load [3]; 509 | buffer[8 + 4] = _load [4]; 510 | buffer[8 + 5] = _load [5]; 511 | buffer[8 + 6] = _load [6]; 512 | buffer[8 + 7] = _load [7]; 513 | buffer[8 + 8] = _load [8]; 514 | buffer[8 + 9] = _load [9]; 515 | buffer[8 + 10] = _load [10]; 516 | buffer[8 + 11] = _load [11]; 517 | buffer[8 + 12] = _load [12]; 518 | buffer[8 + 13] = _load [13]; 519 | } 520 | 521 | String KnxTelegram::get14ByteValue() { 522 | if (getPayloadLength() != 16) { 523 | // Wrong payload length 524 | return ""; 525 | } 526 | char _load[15]; 527 | _load[0] = buffer[8 + 0]; 528 | _load[1] = buffer[8 + 1]; 529 | _load[2] = buffer[8 + 2]; 530 | _load[3] = buffer[8 + 3]; 531 | _load[4] = buffer[8 + 4]; 532 | _load[5] = buffer[8 + 5]; 533 | _load[6] = buffer[8 + 6]; 534 | _load[7] = buffer[8 + 7]; 535 | _load[8] = buffer[8 + 8]; 536 | _load[9] = buffer[8 + 9]; 537 | _load[10] = buffer[8 + 10]; 538 | _load[11] = buffer[8 + 11]; 539 | _load[12] = buffer[8 + 12]; 540 | _load[13] = buffer[8 + 13]; 541 | return (_load); 542 | } -------------------------------------------------------------------------------- /KnxTpUart/KnxTelegram.h: -------------------------------------------------------------------------------- 1 | // File: KnxTelegram.h 2 | // Author: Daniel Kleine-Albers (Since 2012) 3 | // Modified: Thorsten Gehrig (Since 2014) 4 | // Modified: Michael Werski (Since 2014) 5 | // Modified: Katja Blankenheim (Since 2014) 6 | // Modified: Mag Gyver (Since 2016) 7 | 8 | // Last modified: 06.06.2017 9 | 10 | #ifndef KnxTelegram_h 11 | #define KnxTelegram_h 12 | 13 | #include "Arduino.h" 14 | 15 | #define MAX_KNX_TELEGRAM_SIZE 23 16 | #define KNX_TELEGRAM_HEADER_SIZE 6 17 | 18 | #define TPUART_SERIAL_CLASS Stream 19 | 20 | // KNX priorities 21 | enum KnxPriorityType { 22 | KNX_PRIORITY_SYSTEM = B00, 23 | KNX_PRIORITY_ALARM = B10, 24 | KNX_PRIORITY_HIGH = B01, 25 | KNX_PRIORITY_NORMAL = B11 26 | }; 27 | 28 | // KNX commands / APCI Coding 29 | enum KnxCommandType { 30 | KNX_COMMAND_READ = B0000, 31 | KNX_COMMAND_WRITE = B0010, 32 | KNX_COMMAND_ANSWER = B0001, 33 | KNX_COMMAND_INDIVIDUAL_ADDR_WRITE = B0011, 34 | KNX_COMMAND_INDIVIDUAL_ADDR_REQUEST = B0100, 35 | KNX_COMMAND_INDIVIDUAL_ADDR_RESPONSE = B0101, 36 | KNX_COMMAND_MASK_VERSION_READ = B1100, 37 | KNX_COMMAND_MASK_VERSION_RESPONSE = B1101, 38 | KNX_COMMAND_RESTART = B1110, 39 | KNX_COMMAND_ESCAPE = B1111 40 | }; 41 | 42 | // Extended (escaped) KNX commands 43 | enum KnxExtendedCommandType { 44 | KNX_EXT_COMMAND_AUTH_REQUEST = B010001, 45 | KNX_EXT_COMMAND_AUTH_RESPONSE = B010010 46 | }; 47 | 48 | // KNX Transport Layer Communication Type 49 | enum KnxCommunicationType { 50 | KNX_COMM_UDP = B00, // Unnumbered Data Packet 51 | KNX_COMM_NDP = B01, // Numbered Data Packet 52 | KNX_COMM_UCD = B10, // Unnumbered Control Data 53 | KNX_COMM_NCD = B11 // Numbered Control Data 54 | }; 55 | 56 | // KNX Control Data (for UCD / NCD packets) 57 | enum KnxControlDataType { 58 | KNX_CONTROLDATA_CONNECT = B00, // UCD 59 | KNX_CONTROLDATA_DISCONNECT = B01, // UCD 60 | KNX_CONTROLDATA_POS_CONFIRM = B10, // NCD 61 | KNX_CONTROLDATA_NEG_CONFIRM = B11 // NCD 62 | }; 63 | 64 | class KnxTelegram { 65 | public: 66 | KnxTelegram(); 67 | 68 | void clear(); 69 | void setBufferByte(int index, int content); 70 | int getBufferByte(int index); 71 | void setPayloadLength(int size); 72 | int getPayloadLength(); 73 | void setRepeated(bool repeat); 74 | bool isRepeated(); 75 | void setPriority(KnxPriorityType prio); 76 | KnxPriorityType getPriority(); 77 | void setSourceAddress(int area, int line, int member); 78 | int getSourceArea(); 79 | int getSourceLine(); 80 | int getSourceMember(); 81 | void setTargetGroupAddress(int main, int middle, int sub); 82 | void setTargetIndividualAddress(int area, int line, int member); 83 | bool isTargetGroup(); 84 | int getTargetMainGroup(); 85 | int getTargetMiddleGroup(); 86 | int getTargetSubGroup(); 87 | int getTargetArea(); 88 | int getTargetLine(); 89 | int getTargetMember(); 90 | void setRoutingCounter(int counter); 91 | int getRoutingCounter(); 92 | void setCommand(KnxCommandType command); 93 | KnxCommandType getCommand(); 94 | 95 | void setFirstDataByte(int data); 96 | int getFirstDataByte(); 97 | bool getBool(); 98 | 99 | int get4BitIntValue(); 100 | bool get4BitDirectionValue(); 101 | byte get4BitStepsValue(); 102 | 103 | void set1ByteIntValue(int value); 104 | int get1ByteIntValue(); 105 | 106 | void set2ByteIntValue(int value); 107 | int get2ByteIntValue(); 108 | void set2ByteFloatValue(float value); 109 | float get2ByteFloatValue(); 110 | 111 | void set3ByteTime(int weekday, int hour, int minute, int second); 112 | int get3ByteWeekdayValue(); 113 | int get3ByteHourValue(); 114 | int get3ByteMinuteValue(); 115 | int get3ByteSecondValue(); 116 | void set3ByteDate(int day, int month, int year); 117 | int get3ByteDayValue(); 118 | int get3ByteMonthValue(); 119 | int get3ByteYearValue(); 120 | 121 | void set4ByteFloatValue(float value); 122 | float get4ByteFloatValue(); 123 | 124 | void set14ByteValue(String value); 125 | String get14ByteValue(); 126 | 127 | void createChecksum(); 128 | bool verifyChecksum(); 129 | int getChecksum(); 130 | void print(TPUART_SERIAL_CLASS*); 131 | int getTotalLength(); 132 | KnxCommunicationType getCommunicationType(); 133 | void setCommunicationType(KnxCommunicationType); 134 | int getSequenceNumber(); 135 | void setSequenceNumber(int); 136 | KnxControlDataType getControlData(); 137 | void setControlData(KnxControlDataType); 138 | private: 139 | int buffer[MAX_KNX_TELEGRAM_SIZE]; 140 | int calculateChecksum(); 141 | 142 | }; 143 | 144 | #endif -------------------------------------------------------------------------------- /KnxTpUart/KnxTpUart.cpp: -------------------------------------------------------------------------------- 1 | // File: KnxTpUart.cpp 2 | // Author: Daniel Kleine-Albers (Since 2012) 3 | // Modified: Thorsten Gehrig (Since 2014) 4 | // Modified: Michael Werski (Since 2014) 5 | // Modified: Katja Blankenheim (Since 2014) 6 | // Modified: Mag Gyver (Since 2016) 7 | 8 | // Last modified: 06.06.2017 9 | 10 | #include "KnxTpUart.h" 11 | 12 | KnxTpUart::KnxTpUart(TPUART_SERIAL_CLASS* sport, String address) { 13 | _serialport = sport; 14 | _source_area = address.substring(0, address.indexOf('.')).toInt(); 15 | _source_line = address.substring(address.indexOf('.') + 1, address.length()).substring(0, address.substring(address.indexOf('.') + 1, address.length()).indexOf('.')).toInt(); 16 | _source_member = address.substring(address.lastIndexOf('.') + 1, address.length()).toInt(); 17 | _listen_group_address_count = 0; 18 | _tg = new KnxTelegram(); 19 | _tg_ptp = new KnxTelegram(); 20 | _listen_to_broadcasts = false; 21 | } 22 | 23 | void KnxTpUart::setListenToBroadcasts(bool listen) { 24 | _listen_to_broadcasts = listen; 25 | } 26 | 27 | void KnxTpUart::uartReset() { 28 | byte sendByte = 0x01; 29 | _serialport->write(sendByte); 30 | } 31 | 32 | void KnxTpUart::uartStateRequest() { 33 | byte sendByte = 0x02; 34 | _serialport->write(sendByte); 35 | } 36 | 37 | void KnxTpUart::setIndividualAddress(int area, int line, int member) { 38 | _source_area = area; 39 | _source_line = line; 40 | _source_member = member; 41 | } 42 | 43 | KnxTpUartSerialEventType KnxTpUart::serialEvent() { 44 | while (_serialport->available() > 0) { 45 | checkErrors(); 46 | 47 | int incomingByte = _serialport->peek(); 48 | printByte(incomingByte); 49 | 50 | if (isKNXControlByte(incomingByte)) { 51 | bool interested = readKNXTelegram(); 52 | if (interested) { 53 | #if defined(TPUART_DEBUG) 54 | TPUART_DEBUG_PORT.println("Event KNX_TELEGRAM"); 55 | #endif 56 | return KNX_TELEGRAM; 57 | } 58 | else { 59 | #if defined(TPUART_DEBUG) 60 | TPUART_DEBUG_PORT.println("Event IRRELEVANT_KNX_TELEGRAM"); 61 | #endif 62 | return IRRELEVANT_KNX_TELEGRAM; 63 | } 64 | } 65 | else if (incomingByte == TPUART_RESET_INDICATION_BYTE) { 66 | serialRead(); 67 | #if defined(TPUART_DEBUG) 68 | TPUART_DEBUG_PORT.println("Event TPUART_RESET_INDICATION"); 69 | #endif 70 | return TPUART_RESET_INDICATION; 71 | } 72 | else { 73 | serialRead(); 74 | #if defined(TPUART_DEBUG) 75 | TPUART_DEBUG_PORT.println("Event UNKNOWN"); 76 | #endif 77 | return UNKNOWN_EVENT; 78 | } 79 | } 80 | #if defined(TPUART_DEBUG) 81 | TPUART_DEBUG_PORT.println("Event UNKNOWN"); 82 | #endif 83 | return UNKNOWN_EVENT; 84 | } 85 | 86 | 87 | bool KnxTpUart::isKNXControlByte(int b) { 88 | return ( (b | B00101100) == B10111100 ); // Ignore repeat flag and priority flag 89 | } 90 | 91 | void KnxTpUart::checkErrors() { 92 | #if defined(TPUART_DEBUG) 93 | #if defined(_SAM3XA_) // For DUE 94 | if (USART1->US_CSR & US_CSR_OVRE) { 95 | TPUART_DEBUG_PORT.println("Overrun"); 96 | } 97 | 98 | if (USART1->US_CSR & US_CSR_FRAME) { 99 | TPUART_DEBUG_PORT.println("Frame Error"); 100 | } 101 | 102 | if (USART1->US_CSR & US_CSR_PARE) { 103 | TPUART_DEBUG_PORT.println("Parity Error"); 104 | } 105 | #elif defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) // For UNO 106 | if (UCSR0A & B00010000) { 107 | TPUART_DEBUG_PORT.println("Frame Error"); 108 | } 109 | 110 | if (UCSR0A & B00000100) { 111 | TPUART_DEBUG_PORT.println("Parity Error"); 112 | } 113 | #else 114 | if (UCSR1A & B00010000) { 115 | TPUART_DEBUG_PORT.println("Frame Error"); 116 | } 117 | 118 | if (UCSR1A & B00000100) { 119 | TPUART_DEBUG_PORT.println("Parity Error"); 120 | } 121 | #endif 122 | #endif 123 | } 124 | 125 | void KnxTpUart::printByte(int incomingByte) { 126 | #if defined(TPUART_DEBUG) 127 | TPUART_DEBUG_PORT.print("Incoming Byte: "); 128 | TPUART_DEBUG_PORT.print(incomingByte, DEC); 129 | TPUART_DEBUG_PORT.print(" - "); 130 | TPUART_DEBUG_PORT.print(incomingByte, HEX); 131 | TPUART_DEBUG_PORT.print(" - "); 132 | TPUART_DEBUG_PORT.print(incomingByte, BIN); 133 | TPUART_DEBUG_PORT.println(); 134 | #endif 135 | } 136 | 137 | bool KnxTpUart::readKNXTelegram() { 138 | // Receive header 139 | for (int i = 0; i < 6; i++) { 140 | _tg->setBufferByte(i, serialRead()); 141 | } 142 | 143 | #if defined(TPUART_DEBUG) 144 | TPUART_DEBUG_PORT.print("Payload Length: "); 145 | TPUART_DEBUG_PORT.println(_tg->getPayloadLength()); 146 | #endif 147 | int bufpos = 6; 148 | for (int i = 0; i < _tg->getPayloadLength(); i++) { 149 | _tg->setBufferByte(bufpos, serialRead()); 150 | bufpos++; 151 | } 152 | 153 | // Checksum 154 | _tg->setBufferByte(bufpos, serialRead()); 155 | 156 | #if defined(TPUART_DEBUG) 157 | // Print the received telegram 158 | _tg->print(&TPUART_DEBUG_PORT); 159 | #endif 160 | 161 | // Verify if we are interested in this message - GroupAddress 162 | bool interested = _tg->isTargetGroup() && isListeningToGroupAddress(_tg->getTargetMainGroup(), _tg->getTargetMiddleGroup(), _tg->getTargetSubGroup()); 163 | 164 | // Physical address 165 | interested = interested || ((!_tg->isTargetGroup()) && _tg->getTargetArea() == _source_area && _tg->getTargetLine() == _source_line && _tg->getTargetMember() == _source_member); 166 | 167 | // Broadcast (Programming Mode) 168 | interested = interested || (_listen_to_broadcasts && _tg->isTargetGroup() && _tg->getTargetMainGroup() == 0 && _tg->getTargetMiddleGroup() == 0 && _tg->getTargetSubGroup() == 0); 169 | 170 | if (interested) { 171 | sendAck(); 172 | } 173 | else { 174 | sendNotAddressed(); 175 | } 176 | 177 | if (_tg->getCommunicationType() == KNX_COMM_UCD) { 178 | #if defined(TPUART_DEBUG) 179 | TPUART_DEBUG_PORT.println("UCD Telegram received"); 180 | #endif 181 | } 182 | else if (_tg->getCommunicationType() == KNX_COMM_NCD) { 183 | #if defined(TPUART_DEBUG) 184 | TPUART_DEBUG_PORT.print("NCD Telegram "); 185 | TPUART_DEBUG_PORT.print(_tg->getSequenceNumber()); 186 | TPUART_DEBUG_PORT.println(" received"); 187 | #endif 188 | if (interested) { 189 | sendNCDPosConfirm(_tg->getSequenceNumber(), _tg->getSourceArea(), _tg->getSourceLine(), _tg->getSourceMember()); // Thanks to Katja Blankenheim for the help 190 | } 191 | } 192 | 193 | // Returns if we are interested in this diagram 194 | return interested; 195 | } 196 | 197 | KnxTelegram* KnxTpUart::getReceivedTelegram() { 198 | return _tg; 199 | } 200 | 201 | // Command Write 202 | 203 | bool KnxTpUart::groupWriteBool(String Address, bool value) { 204 | int valueAsInt = 0; 205 | if (value) { 206 | valueAsInt = B00000001; 207 | } 208 | 209 | createKNXMessageFrame(2, KNX_COMMAND_WRITE, Address, valueAsInt); 210 | return sendMessage(); 211 | } 212 | 213 | bool KnxTpUart::groupWrite4BitInt(String Address, int value) { 214 | int out_value = 0; 215 | if (value) { 216 | out_value = value & B00001111; 217 | } 218 | 219 | createKNXMessageFrame(2, KNX_COMMAND_WRITE, Address, out_value); 220 | return sendMessage(); 221 | } 222 | 223 | bool KnxTpUart::groupWrite4BitDim(String Address, bool direction, byte steps) { 224 | int value = 0; 225 | if (direction || steps) { 226 | value = (direction << 3) + (steps & B00000111); 227 | } 228 | 229 | createKNXMessageFrame(2, KNX_COMMAND_WRITE, Address, value); 230 | return sendMessage(); 231 | } 232 | 233 | bool KnxTpUart::groupWrite1ByteInt(String Address, int value) { 234 | createKNXMessageFrame(2, KNX_COMMAND_WRITE, Address, 0); 235 | _tg->set1ByteIntValue(value); 236 | _tg->createChecksum(); 237 | return sendMessage(); 238 | } 239 | 240 | bool KnxTpUart::groupWrite2ByteInt(String Address, int value) { 241 | createKNXMessageFrame(2, KNX_COMMAND_WRITE, Address, 0); 242 | _tg->set2ByteIntValue(value); 243 | _tg->createChecksum(); 244 | return sendMessage(); 245 | } 246 | 247 | bool KnxTpUart::groupWrite2ByteFloat(String Address, float value) { 248 | createKNXMessageFrame(2, KNX_COMMAND_WRITE, Address, 0); 249 | _tg->set2ByteFloatValue(value); 250 | _tg->createChecksum(); 251 | return sendMessage(); 252 | } 253 | 254 | bool KnxTpUart::groupWrite3ByteTime(String Address, int weekday, int hour, int minute, int second) { 255 | createKNXMessageFrame(2, KNX_COMMAND_WRITE, Address, 0); 256 | _tg->set3ByteTime(weekday, hour, minute, second); 257 | _tg->createChecksum(); 258 | return sendMessage(); 259 | } 260 | 261 | bool KnxTpUart::groupWrite3ByteDate(String Address, int day, int month, int year) { 262 | createKNXMessageFrame(2, KNX_COMMAND_WRITE, Address, 0); 263 | _tg->set3ByteDate(day, month, year); 264 | _tg->createChecksum(); 265 | return sendMessage(); 266 | } 267 | 268 | bool KnxTpUart::groupWrite4ByteFloat(String Address, float value) { 269 | createKNXMessageFrame(2, KNX_COMMAND_WRITE, Address, 0); 270 | _tg->set4ByteFloatValue(value); 271 | _tg->createChecksum(); 272 | return sendMessage(); 273 | } 274 | 275 | bool KnxTpUart::groupWrite14ByteText(String Address, String value) { 276 | createKNXMessageFrame(2, KNX_COMMAND_WRITE, Address, 0); 277 | _tg->set14ByteValue(value); 278 | _tg->createChecksum(); 279 | return sendMessage(); 280 | } 281 | 282 | // Command Answer 283 | 284 | bool KnxTpUart::groupAnswerBool(String Address, bool value) { 285 | int valueAsInt = 0; 286 | if (value) { 287 | valueAsInt = B00000001; 288 | } 289 | 290 | createKNXMessageFrame(2, KNX_COMMAND_ANSWER, Address, valueAsInt); 291 | return sendMessage(); 292 | } 293 | 294 | /* 295 | bool KnxTpUart::groupAnswerBitInt(String Address, int value) { 296 | int out_value = 0; 297 | if (value) { 298 | out_value = value & B00001111; 299 | } 300 | 301 | createKNXMessageFrame(2, KNX_COMMAND_ANSWER, Address, out_value); 302 | return sendMessage(); 303 | } 304 | 305 | bool KnxTpUart::groupAnswer4BitDim(String Address, bool direction, byte steps) { 306 | int value = 0; 307 | if (direction || steps) { 308 | value = (direction << 3) + (steps & B00000111); 309 | } 310 | 311 | createKNXMessageFrame(2, KNX_COMMAND_ANSWER, Address, value); 312 | return sendMessage(); 313 | } 314 | */ 315 | 316 | bool KnxTpUart::groupAnswer1ByteInt(String Address, int value) { 317 | createKNXMessageFrame(2, KNX_COMMAND_ANSWER, Address, 0); 318 | _tg->set1ByteIntValue(value); 319 | _tg->createChecksum(); 320 | return sendMessage(); 321 | } 322 | 323 | bool KnxTpUart::groupAnswer2ByteInt(String Address, int value) { 324 | createKNXMessageFrame(2, KNX_COMMAND_ANSWER, Address, 0); 325 | _tg->set2ByteIntValue(value); 326 | _tg->createChecksum(); 327 | return sendMessage(); 328 | } 329 | 330 | bool KnxTpUart::groupAnswer2ByteFloat(String Address, float value) { 331 | createKNXMessageFrame(2, KNX_COMMAND_ANSWER, Address, 0); 332 | _tg->set2ByteFloatValue(value); 333 | _tg->createChecksum(); 334 | return sendMessage(); 335 | } 336 | 337 | bool KnxTpUart::groupAnswer3ByteTime(String Address, int weekday, int hour, int minute, int second) { 338 | createKNXMessageFrame(2, KNX_COMMAND_ANSWER, Address, 0); 339 | _tg->set3ByteTime(weekday, hour, minute, second); 340 | _tg->createChecksum(); 341 | return sendMessage(); 342 | } 343 | 344 | bool KnxTpUart::groupAnswer3ByteDate(String Address, int day, int month, int year) { 345 | createKNXMessageFrame(2, KNX_COMMAND_ANSWER, Address, 0); 346 | _tg->set3ByteDate(day, month, year); 347 | _tg->createChecksum(); 348 | return sendMessage(); 349 | } 350 | bool KnxTpUart::groupAnswer4ByteFloat(String Address, float value) { 351 | createKNXMessageFrame(2, KNX_COMMAND_ANSWER, Address, 0); 352 | _tg->set4ByteFloatValue(value); 353 | _tg->createChecksum(); 354 | return sendMessage(); 355 | } 356 | 357 | bool KnxTpUart::groupAnswer14ByteText(String Address, String value) { 358 | createKNXMessageFrame(2, KNX_COMMAND_ANSWER, Address, 0); 359 | _tg->set14ByteValue(value); 360 | _tg->createChecksum(); 361 | return sendMessage(); 362 | } 363 | 364 | // Command Read 365 | 366 | bool KnxTpUart::groupRead(String Address) { 367 | createKNXMessageFrame(2, KNX_COMMAND_READ, Address, 0); 368 | _tg->createChecksum(); 369 | return sendMessage(); 370 | } 371 | 372 | bool KnxTpUart::individualAnswerAddress() { 373 | createKNXMessageFrame(2, KNX_COMMAND_INDIVIDUAL_ADDR_RESPONSE, "0/0/0", 0); 374 | _tg->createChecksum(); 375 | return sendMessage(); 376 | } 377 | 378 | bool KnxTpUart::individualAnswerMaskVersion(int area, int line, int member) { 379 | createKNXMessageFrameIndividual(4, KNX_COMMAND_MASK_VERSION_RESPONSE, String(area) + "/" + String(line) + "/" + String(member), 0); 380 | _tg->setCommunicationType(KNX_COMM_NDP); 381 | _tg->setBufferByte(8, 0x07); // Mask version part 1 for BIM M 112 382 | _tg->setBufferByte(9, 0x01); // Mask version part 2 for BIM M 112 383 | _tg->createChecksum(); 384 | return sendMessage(); 385 | } 386 | 387 | bool KnxTpUart::individualAnswerAuth(int accessLevel, int sequenceNo, int area, int line, int member) { 388 | createKNXMessageFrameIndividual(3, KNX_COMMAND_ESCAPE, String(area) + "/" + String(line) + "/" + String(member), KNX_EXT_COMMAND_AUTH_RESPONSE); 389 | _tg->setCommunicationType(KNX_COMM_NDP); 390 | _tg->setSequenceNumber(sequenceNo); 391 | _tg->setBufferByte(8, accessLevel); 392 | _tg->createChecksum(); 393 | return sendMessage(); 394 | } 395 | 396 | void KnxTpUart::createKNXMessageFrame(int payloadlength, KnxCommandType command, String address, int firstDataByte) { 397 | int mainGroup = address.substring(0, address.indexOf('/')).toInt(); 398 | int middleGroup = address.substring(address.indexOf('/') + 1, address.length()).substring(0, address.substring(address.indexOf('/') + 1, address.length()).indexOf('/')).toInt(); 399 | int subGroup = address.substring(address.lastIndexOf('/') + 1, address.length()).toInt(); 400 | _tg->clear(); 401 | _tg->setSourceAddress(_source_area, _source_line, _source_member); 402 | _tg->setTargetGroupAddress(mainGroup, middleGroup, subGroup); 403 | _tg->setFirstDataByte(firstDataByte); 404 | _tg->setCommand(command); 405 | _tg->setPayloadLength(payloadlength); 406 | _tg->createChecksum(); 407 | } 408 | 409 | void KnxTpUart::createKNXMessageFrameIndividual(int payloadlength, KnxCommandType command, String address, int firstDataByte) { 410 | int area = address.substring(0, address.indexOf('/')).toInt(); 411 | int line = address.substring(address.indexOf('/') + 1, address.length()).substring(0, address.substring(address.indexOf('/') + 1, address.length()).indexOf('/')).toInt(); 412 | int member = address.substring(address.lastIndexOf('/') + 1, address.length()).toInt(); 413 | _tg->clear(); 414 | _tg->setSourceAddress(_source_area, _source_line, _source_member); 415 | _tg->setTargetIndividualAddress(area, line, member); 416 | _tg->setFirstDataByte(firstDataByte); 417 | _tg->setCommand(command); 418 | _tg->setPayloadLength(payloadlength); 419 | _tg->createChecksum(); 420 | } 421 | 422 | bool KnxTpUart::sendNCDPosConfirm(int sequenceNo, int area, int line, int member) { 423 | _tg_ptp->clear(); 424 | _tg_ptp->setSourceAddress(_source_area, _source_line, _source_member); 425 | _tg_ptp->setTargetIndividualAddress(area, line, member); 426 | _tg_ptp->setSequenceNumber(sequenceNo); 427 | _tg_ptp->setCommunicationType(KNX_COMM_NCD); 428 | _tg_ptp->setControlData(KNX_CONTROLDATA_POS_CONFIRM); 429 | _tg_ptp->setPayloadLength(1); 430 | _tg_ptp->createChecksum(); 431 | 432 | 433 | int messageSize = _tg_ptp->getTotalLength(); 434 | 435 | uint8_t sendbuf[2]; 436 | for (int i = 0; i < messageSize; i++) { 437 | if (i == (messageSize - 1)) { 438 | sendbuf[0] = TPUART_DATA_END; 439 | } 440 | else { 441 | sendbuf[0] = TPUART_DATA_START_CONTINUE; 442 | } 443 | 444 | sendbuf[0] |= i; 445 | sendbuf[1] = _tg_ptp->getBufferByte(i); 446 | 447 | _serialport->write(sendbuf, 2); 448 | } 449 | 450 | 451 | int confirmation; 452 | while (true) { 453 | confirmation = serialRead(); 454 | if (confirmation == B10001011) { 455 | return true; // Sent successfully 456 | } 457 | else if (confirmation == B00001011) { 458 | return false; 459 | } 460 | else if (confirmation == -1) { 461 | // Read timeout 462 | return false; 463 | } 464 | } 465 | 466 | return false; 467 | } 468 | 469 | bool KnxTpUart::sendMessage() { 470 | int messageSize = _tg->getTotalLength(); 471 | 472 | uint8_t sendbuf[2]; 473 | for (int i = 0; i < messageSize; i++) { 474 | if (i == (messageSize - 1)) { 475 | sendbuf[0] = TPUART_DATA_END; 476 | } 477 | else { 478 | sendbuf[0] = TPUART_DATA_START_CONTINUE; 479 | } 480 | 481 | sendbuf[0] |= i; 482 | sendbuf[1] = _tg->getBufferByte(i); 483 | 484 | _serialport->write(sendbuf, 2); 485 | } 486 | 487 | 488 | int confirmation; 489 | while (true) { 490 | confirmation = serialRead(); 491 | if (confirmation == B10001011) { 492 | delay (SERIAL_WRITE_DELAY_MS); 493 | return true; // Sent successfully 494 | } 495 | else if (confirmation == B00001011) { 496 | delay (SERIAL_WRITE_DELAY_MS); 497 | return false; 498 | } 499 | else if (confirmation == -1) { 500 | // Read timeout 501 | delay (SERIAL_WRITE_DELAY_MS); 502 | return false; 503 | } 504 | } 505 | 506 | return false; 507 | } 508 | 509 | void KnxTpUart::sendAck() { 510 | byte sendByte = B00010001; 511 | _serialport->write(sendByte); 512 | delay(SERIAL_WRITE_DELAY_MS); 513 | } 514 | 515 | void KnxTpUart::sendNotAddressed() { 516 | byte sendByte = B00010000; 517 | _serialport->write(sendByte); 518 | delay(SERIAL_WRITE_DELAY_MS); 519 | } 520 | 521 | int KnxTpUart::serialRead() { 522 | unsigned long startTime = millis(); 523 | #if defined(TPUART_DEBUG) 524 | TPUART_DEBUG_PORT.print("Available: "); 525 | TPUART_DEBUG_PORT.println(_serialport->available()); 526 | #endif 527 | 528 | while (! (_serialport->available() > 0)) { 529 | if (abs(millis() - startTime) > SERIAL_READ_TIMEOUT_MS) { 530 | // Timeout 531 | #if defined(TPUART_DEBUG) 532 | TPUART_DEBUG_PORT.println("Timeout while receiving message"); 533 | #endif 534 | return -1; 535 | } 536 | delay(1); 537 | } 538 | 539 | int inByte = _serialport->read(); 540 | checkErrors(); 541 | printByte(inByte); 542 | 543 | return inByte; 544 | } 545 | 546 | void KnxTpUart::addListenGroupAddress(String address) { 547 | if (_listen_group_address_count >= MAX_LISTEN_GROUP_ADDRESSES) { 548 | #if defined(TPUART_DEBUG) 549 | TPUART_DEBUG_PORT.println("Already listening to MAX_LISTEN_GROUP_ADDRESSES, cannot listen to another"); 550 | #endif 551 | return; 552 | } 553 | 554 | _listen_group_addresses[_listen_group_address_count][0] = address.substring(0, address.indexOf('/')).toInt(); 555 | ; 556 | _listen_group_addresses[_listen_group_address_count][1] = address.substring(address.indexOf('/') + 1, address.length()).substring(0, address.substring(address.indexOf('/') + 1, address.length()).indexOf('/')).toInt(); 557 | _listen_group_addresses[_listen_group_address_count][2] = address.substring(address.lastIndexOf('/') + 1, address.length()).toInt(); 558 | 559 | _listen_group_address_count++; 560 | } 561 | 562 | bool KnxTpUart::isListeningToGroupAddress(int main, int middle, int sub) { 563 | for (int i = 0; i < _listen_group_address_count; i++) { 564 | if ( (_listen_group_addresses[i][0] == main) 565 | && (_listen_group_addresses[i][1] == middle) 566 | && (_listen_group_addresses[i][2] == sub)) { 567 | return true; 568 | } 569 | } 570 | 571 | return false; 572 | } 573 | -------------------------------------------------------------------------------- /KnxTpUart/KnxTpUart.h: -------------------------------------------------------------------------------- 1 | // File: KnxTpUart.h 2 | // Author: Daniel Kleine-Albers (Since 2012) 3 | // Modified: Thorsten Gehrig (Since 2014) 4 | // Modified: Michael Werski (Since 2014) 5 | // Modified: Katja Blankenheim (Since 2014) 6 | // Modified: Mag Gyver (Since 2016) 7 | 8 | // Last modified: 06.06.2017 9 | 10 | #ifndef KnxTpUart_h 11 | #define KnxTpUart_h 12 | 13 | #include "HardwareSerial.h" 14 | #include "Arduino.h" 15 | 16 | #include "KnxTelegram.h" 17 | 18 | // Services from TPUART 19 | #define TPUART_RESET_INDICATION_BYTE B11 20 | 21 | // Services to TPUART 22 | #define TPUART_DATA_START_CONTINUE B10000000 23 | #define TPUART_DATA_END B01000000 24 | 25 | // Uncomment the following line to enable debugging 26 | //#define TPUART_DEBUG 27 | 28 | #define TPUART_DEBUG_PORT Serial 29 | 30 | #define TPUART_SERIAL_CLASS Stream 31 | 32 | // Delay in ms between sending of packets to the bus 33 | // Change only if you know what you're doing 34 | #define SERIAL_WRITE_DELAY_MS 100 35 | 36 | // Timeout for reading a byte from TPUART 37 | // Change only if you know what you're doing 38 | #define SERIAL_READ_TIMEOUT_MS 10 39 | 40 | // Maximum number of group addresses that can be listened on 41 | #define MAX_LISTEN_GROUP_ADDRESSES 15 42 | 43 | enum KnxTpUartSerialEventType { 44 | TPUART_RESET_INDICATION, 45 | KNX_TELEGRAM, 46 | IRRELEVANT_KNX_TELEGRAM, 47 | UNKNOWN_EVENT 48 | }; 49 | 50 | class KnxTpUart { 51 | 52 | 53 | public: 54 | KnxTpUart(TPUART_SERIAL_CLASS*, String); 55 | void uartReset(); 56 | void uartStateRequest(); 57 | KnxTpUartSerialEventType serialEvent(); 58 | KnxTelegram* getReceivedTelegram(); 59 | 60 | void setIndividualAddress(int, int, int); 61 | 62 | void sendAck(); 63 | void sendNotAddressed(); 64 | 65 | bool groupWriteBool(String, bool); 66 | bool groupWrite4BitInt(String, int); 67 | bool groupWrite4BitDim(String, bool, byte); 68 | bool groupWrite1ByteInt(String, int); 69 | bool groupWrite2ByteInt(String, int); 70 | bool groupWrite2ByteFloat(String, float); 71 | bool groupWrite3ByteTime(String, int, int, int, int); 72 | bool groupWrite3ByteDate(String, int, int, int); 73 | bool groupWrite4ByteFloat(String, float); 74 | bool groupWrite14ByteText(String, String); 75 | 76 | bool groupAnswerBool(String, bool); 77 | /* 78 | bool groupAnswer4BitInt(String, int); 79 | bool groupAnswer4BitDim(String, bool, byte); 80 | */ 81 | bool groupAnswer1ByteInt(String, int); 82 | bool groupAnswer2ByteInt(String, int); 83 | bool groupAnswer2ByteFloat(String, float); 84 | bool groupAnswer3ByteTime(String, int, int, int, int); 85 | bool groupAnswer3ByteDate(String, int, int, int); 86 | bool groupAnswer4ByteFloat(String, float); 87 | bool groupAnswer14ByteText(String, String); 88 | 89 | bool groupRead(String); 90 | 91 | void addListenGroupAddress(String); 92 | bool isListeningToGroupAddress(int, int, int); 93 | 94 | bool individualAnswerAddress(); 95 | bool individualAnswerMaskVersion(int, int, int); 96 | bool individualAnswerAuth(int, int, int, int, int); 97 | 98 | void setListenToBroadcasts(bool); 99 | 100 | 101 | private: 102 | Stream* _serialport; 103 | KnxTelegram* _tg; // for normal communication 104 | KnxTelegram* _tg_ptp; // for PTP sequence confirmation 105 | int _source_area; 106 | int _source_line; 107 | int _source_member; 108 | int _listen_group_addresses[MAX_LISTEN_GROUP_ADDRESSES][3]; 109 | int _listen_group_address_count; 110 | bool _listen_to_broadcasts; 111 | 112 | bool isKNXControlByte(int); 113 | void checkErrors(); 114 | void printByte(int); 115 | bool readKNXTelegram(); 116 | void createKNXMessageFrame(int, KnxCommandType, String, int); 117 | void createKNXMessageFrameIndividual(int, KnxCommandType, String, int); 118 | bool sendMessage(); 119 | bool sendNCDPosConfirm(int, int, int, int); 120 | int serialRead(); 121 | }; 122 | 123 | #endif 124 | -------------------------------------------------------------------------------- /KnxTpUart/examples/GroupRead/GroupRead.ino: -------------------------------------------------------------------------------- 1 | // File: GroupRead.ino 2 | // Author: Mag Gyver (Since 2015) 3 | // Modified: Thorsten Gehrig (Since 2015) 4 | 5 | // Test constellation = ARDUINO UNO <-> 5WG1 117-2AB12 6 | 7 | /* 8 | -> During programming, the BCU may have no connection to the ARDUINO UNO. 9 | -> After programming for communication with the BCU. Connect the jumpers ICSP1 PIN 5 and ICSP1 PIN 6 together, tested only with ARDUINO UNO revision 3. 10 | -> For programming the jumper ICSP1 PIN 5 and ICSP1 PIN 6 must be not connected together and the voltage must be taken away for a short time. Then, you can transfer the new "sketch". 11 | */ 12 | 13 | #include 14 | 15 | KnxTpUart knx(&Serial, "1.1.199"); 16 | int LED = 13; 17 | int target_2_6_0; 18 | int target_5_6_0; 19 | 20 | void setup() { 21 | 22 | pinMode(LED, OUTPUT); // PIN 13 as output 23 | 24 | Serial.begin(19200, SERIAL_8E1); 25 | knx.uartReset(); 26 | 27 | knx.addListenGroupAddress("2/6/0"); 28 | knx.addListenGroupAddress("5/6/0"); 29 | 30 | // Read request to groups address-> all data types 31 | 32 | // The function delay(1000) only to delay the necessary initialization query. 33 | // If you want no initialization query, you should comment on the next lines to end the function. 34 | 35 | delay(1000); 36 | 37 | // Read request to group addresses -> possible call to the read request void loop() function 38 | 39 | knx.groupRead("2/6/0"); 40 | knx.groupRead("5/6/0"); 41 | } 42 | 43 | void loop() { 44 | 45 | } 46 | 47 | void serialEvent() { 48 | 49 | //Aufruf knx.serialEvent() 50 | 51 | KnxTpUartSerialEventType eType = knx.serialEvent(); 52 | 53 | //Evaluation of the received telegram -> only KNX telegrams are accepted 54 | 55 | if (eType == KNX_TELEGRAM) { 56 | KnxTelegram* telegram = knx.getReceivedTelegram(); 57 | 58 | // Telegram evaluation on KNX (at reception always necessary) 59 | 60 | String target = 61 | String(0 + telegram->getTargetMainGroup()) + "/" + 62 | String(0 + telegram->getTargetMiddleGroup()) + "/" + 63 | String(0 + telegram->getTargetSubGroup()); 64 | 65 | // Evaluation of group address of the received telegram and caching in variable "target" 66 | 67 | if (telegram->getCommand() == KNX_COMMAND_ANSWER) { 68 | 69 | // Evaluation of read request in serialEvent() with data types 70 | // Evaluation of the received KNX telegram with response to read request (flag) -> action 71 | 72 | if (target == "2/6/0") { 73 | target_2_6_0 = telegram->getBool(); 74 | 75 | // Storage of the contents in variable "target_2_6_0" of the response to the read request of the group address "2/6/0" 76 | 77 | if (target_2_6_0) { 78 | digitalWrite(LED, HIGH); 79 | } 80 | else { 81 | digitalWrite(LED,LOW); 82 | } 83 | 84 | // Evaluation of the content and output of the content of the group address "2/6/0" to PIN 13 of the ARDUINO UNO 85 | } 86 | else if (target == "5/6/0") { 87 | target_5_6_0 = telegram->get1ByteIntValue(); 88 | 89 | // Storage of the contents in a variable "target_5_6_0" of the response to the read request of the group address "5/6/0" for further processing 90 | 91 | } 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /KnxTpUart/examples/GroupWrite/GroupWrite.ino: -------------------------------------------------------------------------------- 1 | // File: GroupWrite.ino 2 | // Author: Daniel Kleine-Albers (Since 2012) 3 | // Modified: Thorsten Gehrig (Since 2014) 4 | // Modified: Mag Gyver (Since 2016) 5 | 6 | // Test constellation = ARDUINO MEGA <-> 5WG1 117-2AB12 7 | 8 | #include 9 | 10 | // Initialize the KNX TP-UART library on the Serial1 port of ARDUINO MEGA 11 | KnxTpUart knx(&Serial1, "15.15.20"); 12 | 13 | // Define input pin 14 | int inPin = 32; 15 | 16 | // Remember if we have sent on current keypress 17 | boolean haveSent = false; 18 | 19 | // Remember if we sent ON last or OFF 20 | boolean onSent = false; 21 | 22 | 23 | void setup() { 24 | pinMode(inPin, INPUT); 25 | digitalWrite(inPin, HIGH); // Turn on pullup 26 | 27 | // LED 28 | pinMode(13, OUTPUT); 29 | digitalWrite(13, LOW); 30 | 31 | 32 | Serial.begin(9600); 33 | Serial.println("TP-UART Test"); 34 | 35 | Serial1.begin(19200, SERIAL_8E1); 36 | 37 | Serial.print("UCSR1A: "); 38 | Serial.println(UCSR1A, BIN); 39 | 40 | Serial.print("UCSR1B: "); 41 | Serial.println(UCSR1B, BIN); 42 | 43 | Serial.print("UCSR1C: "); 44 | Serial.println(UCSR1C, BIN); 45 | 46 | knx.uartReset(); 47 | } 48 | 49 | 50 | void loop() { 51 | if (digitalRead(inPin) == LOW) { 52 | // Button is pressed 53 | digitalWrite(13, HIGH); 54 | 55 | if (!haveSent) { 56 | // Send the opposite of what we have sent last 57 | bool success = knx.groupWriteBool("0/0/3", !onSent); 58 | 59 | Serial.print("Successfully sent: "); 60 | Serial.println(!onSent); 61 | 62 | onSent = !onSent; 63 | haveSent = true; 64 | } 65 | } 66 | else { 67 | digitalWrite(13, LOW); 68 | haveSent = false; 69 | } 70 | } -------------------------------------------------------------------------------- /KnxTpUart/examples/LearnKNXAddress/LearnKNXAddress.ino: -------------------------------------------------------------------------------- 1 | // File: LearnKNXAddress.ino 2 | // Author: Daniel Kleine-Albers (Since 2012) 3 | // Modified: Thorsten Gehrig (Since 2014) 4 | // Modified: Mag Gyver (Since 2016) 5 | 6 | // Test constellation = ARDUINO MEGA <-> 5WG1 117-2AB12 7 | 8 | #include 9 | 10 | // Initialize the KNX TP-UART library on the Serial1 port of ARDUINO MEGA 11 | KnxTpUart knx(&Serial1, "15.15.20"); 12 | 13 | // Start in programming mode 14 | boolean programmingMode = true; 15 | 16 | void setup() { 17 | Serial.begin(115200); 18 | Serial.println("TP-UART Test"); 19 | 20 | Serial1.begin(19200, SERIAL_8E1); // Even parity 21 | 22 | knx.uartReset(); 23 | knx.setListenToBroadcasts(true); 24 | } 25 | 26 | 27 | void loop() { 28 | } 29 | 30 | void serialEvent1() { 31 | KnxTpUartSerialEventType eType = knx.serialEvent(); 32 | if (eType == KNX_TELEGRAM) { 33 | KnxTelegram* telegram = knx.getReceivedTelegram(); 34 | 35 | if (telegram->isTargetGroup() && telegram->getCommand() == KNX_COMMAND_INDIVIDUAL_ADDR_WRITE && programmingMode) { 36 | // Broadcast to all devices in programming mode to store new physical address 37 | Serial.print("Received IndvAddrWrite: "); 38 | 39 | int area = (telegram->getBufferByte(8) & B11110000) >> 4; 40 | int line = telegram->getBufferByte(8) & B00001111; 41 | int member = telegram->getBufferByte(9); 42 | 43 | Serial.print(area); 44 | Serial.print("."); 45 | Serial.print(line); 46 | Serial.print("."); 47 | Serial.println(member); 48 | 49 | knx.setIndividualAddress(area, line, member); 50 | 51 | // Here the new address could be stored to EEPROM and be reloaded after restart of Arduino 52 | } 53 | else if (telegram->getCommand() == KNX_COMMAND_MASK_VERSION_READ) { 54 | // Request for mask version (version of bus interface 55 | knx.individualAnswerMaskVersion(telegram->getSourceArea(), telegram->getSourceLine(), telegram->getSourceMember()); 56 | } 57 | else if (telegram->getCommand() == KNX_COMMAND_INDIVIDUAL_ADDR_REQUEST && programmingMode) { 58 | // Broadcast request for individual addresses of all devices in programming mode 59 | knx.individualAnswerAddress(); 60 | } 61 | else if (telegram->getFirstDataByte() == KNX_EXT_COMMAND_AUTH_REQUEST && programmingMode) { 62 | // Authentication request to allow memory access 63 | knx.individualAnswerAuth(15, telegram->getSequenceNumber(), telegram->getSourceArea(), telegram->getSourceLine(), telegram->getSourceMember()); 64 | } 65 | else if (telegram->getCommand() == KNX_COMMAND_RESTART && programmingMode) { 66 | // Restart the device -> end programming mode 67 | programmingMode = false; 68 | knx.setListenToBroadcasts(false); 69 | Serial.println("Received restart, ending programming mode"); 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /KnxTpUart/examples/ReceiveKNXTelegrams/ReceiveKNXTelegrams.ino: -------------------------------------------------------------------------------- 1 | // File: ReceiveKNXTelegrams.ino 2 | // Author: Daniel Kleine-Albers (Since 2012) 3 | // Modified: Thorsten Gehrig (Since 2014) 4 | // Modified: Mag Gyver (Since 2016) 5 | 6 | // Test constellation = ARDUINO MEGA <-> 5WG1 117-2AB12 7 | 8 | #include 9 | 10 | // Initialize the KNX TP-UART library on the Serial1 port of ARDUINO MEGA 11 | // and with KNX physical address 15.15.20 12 | KnxTpUart knx(&Serial1, "15.15.20"); 13 | 14 | int LED = 13; 15 | 16 | void setup() { 17 | pinMode(LED, OUTPUT); 18 | digitalWrite(LED, LOW); 19 | 20 | Serial.begin(9600); 21 | Serial.println("TP-UART Test"); 22 | 23 | Serial1.begin(19200, SERIAL_8E1); 24 | 25 | Serial.print("UCSR1A: "); 26 | Serial.println(UCSR1A, BIN); 27 | 28 | Serial.print("UCSR1B: "); 29 | Serial.println(UCSR1B, BIN); 30 | 31 | Serial.print("UCSR1C: "); 32 | Serial.println(UCSR1C, BIN); 33 | 34 | knx.uartReset(); 35 | 36 | knx.addListenGroupAddress("15/0/0"); 37 | knx.addListenGroupAddress("15/0/1"); 38 | knx.addListenGroupAddress("15/0/2"); 39 | knx.addListenGroupAddress("15/0/3"); 40 | knx.addListenGroupAddress("15/0/4"); 41 | knx.addListenGroupAddress("15/0/5"); 42 | knx.addListenGroupAddress("15/0/6"); 43 | knx.addListenGroupAddress("15/0/7"); 44 | knx.addListenGroupAddress("15/0/8"); 45 | knx.addListenGroupAddress("15/0/9"); 46 | } 47 | 48 | void loop() { 49 | // nothing in the loop. This example is only to receive telegrams 50 | } 51 | 52 | void serialEvent1() { 53 | KnxTpUartSerialEventType eType = knx.serialEvent(); 54 | if (eType == TPUART_RESET_INDICATION) { 55 | Serial.println("Event TPUART_RESET_INDICATION"); 56 | } 57 | else if (eType == UNKNOWN) { 58 | Serial.println("Event UNKNOWN"); 59 | } 60 | else if (eType == KNX_TELEGRAM) { 61 | Serial.println("Event KNX_TELEGRAM"); 62 | KnxTelegram* telegram = knx.getReceivedTelegram(); 63 | // Telegrammauswertung auf KNX (bei Empfang immer notwendig) 64 | String target = 65 | String(0 + telegram->getTargetMainGroup()) + "/" + 66 | String(0 + telegram->getTargetMiddleGroup()) + "/" + 67 | String(0 + telegram->getTargetSubGroup()); 68 | 69 | // Here you have the telegram and can do whatever you want 70 | if (telegram->getCommand() == KNX_COMMAND_WRITE) { 71 | // Auswertung des empfangenen KNX-Telegrammes mit Schreibbefehl (Flag) -> Aktion 72 | if (target == "15/0/0") { 73 | int received_15_0_0 = telegram->getBool(); 74 | Serial.print("Empfangener wert"); 75 | Serial.println(received_15_0_0); 76 | if (received_15_0_0) { 77 | digitalWrite(LED, HIGH); 78 | } 79 | else { 80 | digitalWrite(LED, LOW); 81 | } 82 | } 83 | if (target == "15/0/1") { 84 | int received_15_0_1 = telegram->get4BitIntValue(); 85 | Serial.print("Empfangener Wert:"); 86 | Serial.println(received_15_0_1); 87 | } 88 | if (target == "15/0/2") { 89 | int received_15_0_2_0 = telegram->get4BitDirectionValue(); 90 | int received_15_0_2_1 = telegram->get4BitStepsValue(); 91 | Serial.print("Empfangener Wert:"); 92 | Serial.println(""); 93 | switch (received_15_0_2_0) { 94 | case 0: 95 | Serial.print("Direction: down"); 96 | break; 97 | case 1: 98 | Serial.print("Direction: up"); 99 | break; 100 | } 101 | Serial.print(" "); 102 | switch (received_15_0_2_1) { 103 | case 0: 104 | Serial.print("Step: stop"); 105 | break; 106 | case 1: 107 | Serial.print("Step: 100%"); 108 | break; 109 | case 2: 110 | Serial.print("Step: 50%"); 111 | break; 112 | case 3: 113 | Serial.print("Step: 25%"); 114 | break; 115 | case 4: 116 | Serial.print("Step: 12%"); 117 | break; 118 | case 5: 119 | Serial.print("Step: 6%"); 120 | break; 121 | case 6: 122 | Serial.print("Step: 3%"); 123 | break; 124 | case 7: 125 | Serial.print("Step: 1%"); 126 | break; 127 | } 128 | Serial.println(""); 129 | } 130 | if (target == "15/0/3") { 131 | int received_15_0_3 = telegram->get1ByteIntValue(); 132 | Serial.print("Empfangener Wert:"); 133 | Serial.println(received_15_0_3); 134 | } 135 | if (target == "15/0/4") { 136 | int received_15_0_4 = telegram->get2ByteIntValue(); 137 | Serial.print("Empfangener Wert:"); 138 | Serial.println(received_15_0_4); 139 | } 140 | if (target == "15/0/5") { 141 | float received_15_0_5 = telegram->get2ByteFloatValue(); 142 | Serial.print("Empfangener Wert:"); 143 | Serial.println(received_15_0_5); 144 | } 145 | if (target == "15/0/6") { 146 | int received_15_0_6_0 = telegram->get3ByteWeekdayValue(); 147 | int received_15_0_6_1 = telegram->get3ByteHourValue(); 148 | int received_15_0_6_2 = telegram->get3ByteMinuteValue(); 149 | int received_15_0_6_3 = telegram->get3ByteSecondValue(); 150 | Serial.print("Empfangener Wert:"); 151 | Serial.println(""); 152 | Serial.print(received_15_0_6_0); 153 | Serial.print(" "); 154 | Serial.print(received_15_0_6_1); 155 | Serial.print(":"); 156 | Serial.print(received_15_0_6_2); 157 | Serial.print(":"); 158 | Serial.print(received_15_0_6_3); 159 | Serial.println(""); 160 | } 161 | if (target == "15/0/7") { 162 | int received_15_0_7_0 = telegram->get3ByteDayValue(); 163 | int received_15_0_7_1 = telegram->get3ByteMonthValue(); 164 | int received_15_0_7_2 = telegram->get3ByteYearValue(); 165 | Serial.print("Empfangener Wert:"); 166 | Serial.println(""); 167 | Serial.print(received_15_0_7_0); 168 | Serial.print("."); 169 | Serial.print(received_15_0_7_1); 170 | Serial.print("."); 171 | Serial.print(received_15_0_7_2); 172 | Serial.println(""); 173 | } 174 | if (target == "15/0/8") { 175 | float received_15_0_8 = telegram->get4ByteFloatValue(); 176 | Serial.print("Empfangener Wert:"); 177 | Serial.println(received_15_0_8); 178 | } 179 | if (target == "15/0/9") { 180 | String received_15_0_9 = telegram->get14ByteValue(); 181 | Serial.print("Empfangener Wert:"); 182 | Serial.println(received_15_0_9); 183 | } 184 | } 185 | } 186 | } -------------------------------------------------------------------------------- /KnxTpUart/examples/ReplyToKNXRead/ReplyToKNXRead.ino: -------------------------------------------------------------------------------- 1 | // File: ReplyToKNXRead.ino 2 | // Author: Daniel Kleine-Albers (Since 2012) 3 | // Modified: Thorsten Gehrig (Since 2014) 4 | // Modified: Mag Gyver (Since 2016) 5 | 6 | // Test constellation = ARDUINO MEGA <-> 5WG1 117-2AB12 7 | 8 | #include 9 | 10 | // Initialize the KNX TP-UART library on the Serial1 port of ARDUINO MEGA 11 | KnxTpUart knx(&Serial1, "1.1.15"); 12 | 13 | void setup() { 14 | Serial.begin(9600); 15 | Serial.println("TP-UART Test"); 16 | 17 | Serial1.begin(19200, SERIAL_8E1); 18 | 19 | Serial.print("UCSR1A: "); 20 | Serial.println(UCSR1A, BIN); 21 | 22 | Serial.print("UCSR1B: "); 23 | Serial.println(UCSR1B, BIN); 24 | 25 | Serial.print("UCSR1C: "); 26 | Serial.println(UCSR1C, BIN); 27 | 28 | knx.uartReset(); 29 | // Define group address to react on 30 | knx.addListenGroupAddress("3/0/1"); 31 | knx.addListenGroupAddress("3/0/2"); 32 | knx.addListenGroupAddress("3/0/3"); 33 | knx.addListenGroupAddress("3/0/4"); 34 | knx.addListenGroupAddress("3/0/5"); 35 | knx.addListenGroupAddress("3/0/6"); 36 | knx.addListenGroupAddress("3/0/7"); 37 | knx.addListenGroupAddress("3/0/8"); 38 | } 39 | 40 | void loop() { 41 | } 42 | 43 | void serialEvent1() { 44 | KnxTpUartSerialEventType eType = knx.serialEvent(); 45 | if (eType == KNX_TELEGRAM) { 46 | KnxTelegram* telegram = knx.getReceivedTelegram(); 47 | 48 | String target = 49 | String(0 + telegram->getTargetMainGroup()) + "/" + 50 | String(0 + telegram->getTargetMiddleGroup()) + "/" + 51 | String(0 + telegram->getTargetSubGroup()); 52 | 53 | // Is it a read request? 54 | if (telegram->getCommand() == KNX_COMMAND_READ) { 55 | 56 | // Is the destination address equal to group address 3/0/1? 57 | if (target == "3/0/1") { 58 | knx.groupAnswerBool("3/0/1", true); 59 | // Display serial port : 1 60 | } 61 | // Is the destination address equal to group address 3/0/2? 62 | if (target == "3/0/2") { 63 | knx.groupAnswer1ByteInt("3/0/2", 126); 64 | // Display serial port : 49% 65 | } 66 | // Is the destination address equal to group address 3/0/3? 67 | if (target == "3/0/3") { 68 | knx.groupAnswer2ByteInt("3/0/3", 1000); 69 | // Display serial port : 1000 70 | } 71 | // Is the destination address equal to group address 3/0/4? 72 | if (target == "3/0/4") { 73 | knx.groupAnswer2ByteFloat("3/0/4", 25.28); 74 | // Display serial port : 25,28 75 | } 76 | // Is the destination address equal to group address 3/0/5? 77 | if (target == "3/0/5") { 78 | knx.groupAnswer3ByteTime("3/0/5", 7, 0, 0, 1); 79 | // Display serial port : Sonntag 00:00:01 80 | } 81 | // Is the destination address equal to group address 3/0/6? 82 | if (target == "3/0/6") { 83 | knx.groupAnswer3ByteDate("3/0/6", 31, 1, 3); 84 | // Display serial port : 31.01.2003 85 | } 86 | // Is the destination address equal to group address 3/0/7? 87 | if (target == "3/0/7") { 88 | knx.groupAnswer4ByteFloat("3/0/7", -100); 89 | // Display serial port : -100 90 | } 91 | // Is the destination address equal to group address 3/0/8? 92 | if (target == "3/0/8") { 93 | knx.groupAnswer14ByteText("3/0/8", "Hallo"); 94 | // Display serial port : "Hallo" 95 | } 96 | } 97 | } 98 | } -------------------------------------------------------------------------------- /KnxTpUart/examples/TemperatureReadoutOneWire/TemperatureReadoutOneWire.ino: -------------------------------------------------------------------------------- 1 | // File: TemperatureReadoutOneWire.ino 2 | // Author: Daniel Kleine-Albers (Since 2012) 3 | // Modified: Thorsten Gehrig (Since 2014) 4 | // Modified: Mag Gyver (Since 2016) 5 | 6 | // Test constellation = ARDUINO MEGA <-> 5WG1 117-2AB12 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | // Initialize OneWire and DallasTemperature libs 13 | #define ONE_WIRE_BUS 53 14 | OneWire oneWire(ONE_WIRE_BUS); 15 | DallasTemperature sensors(&oneWire); 16 | 17 | // Define group address to react on (for read requests) 18 | #define READ_GROUP "0/0/100"" 19 | 20 | // Define group address to send temperature to 21 | #define WRITE_GROUP "0/0/101" 22 | 23 | // Define send interval 24 | #define SEND_INTERVAL_MS 5000 25 | 26 | // Initialize the KNX TP-UART library on the Serial1 port of ARDUINO MEGA 27 | KnxTpUart knx(&Serial1, "15.15.20"); 28 | 29 | unsigned long startTime; 30 | 31 | void setup() { 32 | Serial.begin(9600); 33 | Serial.println("TP-UART Test"); 34 | 35 | Serial1.begin(19200, SERIAL_8E1); 36 | 37 | Serial.print("UCSR1A: "); 38 | Serial.println(UCSR1A, BIN); 39 | 40 | Serial.print("UCSR1B: "); 41 | Serial.println(UCSR1B, BIN); 42 | 43 | Serial.print("UCSR1C: "); 44 | Serial.println(UCSR1C, BIN); 45 | 46 | knx.uartReset(); 47 | knx.addListenGroupAddress(READ_GROUP); 48 | 49 | startTime = millis(); 50 | } 51 | 52 | 53 | void loop() { 54 | if (abs(millis() - startTime) < SEND_INTERVAL_MS) { 55 | delay(1); 56 | return; 57 | } 58 | 59 | startTime = millis(); 60 | float temp = getTemp(); 61 | Serial.print("Sending temp: "); 62 | Serial.println(temp); 63 | bool result = knx.groupWrite2ByteFloat(WRITE_GROUP, temp); 64 | Serial.print("Sent successfully: "); 65 | Serial.println(result); 66 | } 67 | 68 | void serialEvent1() { 69 | KnxTpUartSerialEventType eType = knx.serialEvent(); 70 | if (eType == KNX_TELEGRAM) { 71 | KnxTelegram* telegram = knx.getReceivedTelegram(); 72 | 73 | // Is it a read request? 74 | if (telegram->getCommand() == KNX_COMMAND_READ) { 75 | knx.groupAnswer2ByteFloat(READ_GROUP, getTemp()); 76 | } 77 | } 78 | } 79 | 80 | float getTemp() { 81 | sensors.requestTemperatures(); 82 | return sensors.getTempCByIndex(0); 83 | } -------------------------------------------------------------------------------- /KnxTpUart/examples/UnitTests/UnitTests.ino: -------------------------------------------------------------------------------- 1 | // File: UnitTests.ino 2 | // Author: Daniel Kleine-Albers (Since 2012) 3 | // Modified: Thorsten Gehrig (Since 2014) 4 | // Modified: Mag Gyver (Since 2016) 5 | 6 | // Test constellation = Not tested 7 | 8 | #include 9 | #include 10 | 11 | TestSuite suite; 12 | KnxTpUart knx(&Serial1, "15.15.20"); 13 | KnxTelegram* knxTelegram = new KnxTelegram(); 14 | 15 | void setup() { 16 | } 17 | 18 | test(knxTelegramClearAfterCreation) { 19 | knxTelegram = new KnxTelegram(); 20 | 21 | for (int i = 0; i < MAX_KNX_TELEGRAM_SIZE; i++) { 22 | if (i != 0 && i != 5) { 23 | assertEquals(0, knxTelegram->getBufferByte(i)); 24 | } 25 | } 26 | 27 | assertEquals(B10111100, knxTelegram->getBufferByte(0)); 28 | assertEquals(B11100001, knxTelegram->getBufferByte(5)); 29 | assertEquals(9, knxTelegram->getTotalLength()); 30 | } 31 | 32 | test(knxTelegramClear) { 33 | knxTelegram->setBufferByte(1, 12345); 34 | knxTelegram->clear(); 35 | 36 | for (int i = 0; i < MAX_KNX_TELEGRAM_SIZE; i++) { 37 | if (i != 0 && i != 5) { 38 | assertEquals(0, knxTelegram->getBufferByte(i)); 39 | } 40 | } 41 | 42 | assertEquals(B10111100, knxTelegram->getBufferByte(0)); 43 | assertEquals(B11100001, knxTelegram->getBufferByte(5)); 44 | } 45 | 46 | test(repeatProperty) { 47 | knxTelegram->setRepeated(true); 48 | assertTrue(knxTelegram->isRepeated()); 49 | 50 | knxTelegram->setRepeated(false); 51 | assertTrue(! knxTelegram->isRepeated()); 52 | } 53 | 54 | test(priorityProperty) { 55 | knxTelegram->setPriority(KNX_PRIORITY_NORMAL); 56 | assertEquals(KNX_PRIORITY_NORMAL, knxTelegram->getPriority()); 57 | 58 | knxTelegram->setPriority(KNX_PRIORITY_HIGH); 59 | assertEquals(KNX_PRIORITY_HIGH, knxTelegram->getPriority()); 60 | 61 | knxTelegram->setPriority(KNX_PRIORITY_ALARM); 62 | assertEquals(KNX_PRIORITY_ALARM, knxTelegram->getPriority()); 63 | 64 | knxTelegram->setPriority(KNX_PRIORITY_SYSTEM); 65 | assertEquals(KNX_PRIORITY_SYSTEM, knxTelegram->getPriority()); 66 | } 67 | 68 | test(sourceAddressProperties) { 69 | knxTelegram->setSourceAddress(15, 12, 20); 70 | assertEquals(15, knxTelegram->getSourceArea()); 71 | assertEquals(12, knxTelegram->getSourceLine()); 72 | assertEquals(20, knxTelegram->getSourceMember()); 73 | } 74 | 75 | test(targetAddressProperties) { 76 | knxTelegram->setTargetGroupAddress(0, 3, 15); 77 | assertTrue(knxTelegram->isTargetGroup()); 78 | assertEquals(0, knxTelegram->getTargetMainGroup()); 79 | assertEquals(3, knxTelegram->getTargetMiddleGroup()); 80 | assertEquals(15, knxTelegram->getTargetSubGroup()); 81 | } 82 | 83 | test(routingCounterProperty) { 84 | knxTelegram->setRoutingCounter(5); 85 | assertEquals(5, knxTelegram->getRoutingCounter()); 86 | } 87 | 88 | test(payloadLengthProperty) { 89 | knxTelegram->setPayloadLength(5); 90 | assertEquals(5, knxTelegram->getPayloadLength()); 91 | } 92 | 93 | test(commandProperty) { 94 | knxTelegram->setCommand(KNX_COMMAND_READ); 95 | assertEquals(KNX_COMMAND_READ, knxTelegram->getCommand()); 96 | 97 | knxTelegram->setCommand(KNX_COMMAND_WRITE); 98 | assertEquals(KNX_COMMAND_WRITE, knxTelegram->getCommand()); 99 | 100 | knxTelegram->setCommand(KNX_COMMAND_ANSWER); 101 | assertEquals(KNX_COMMAND_ANSWER, knxTelegram->getCommand()); 102 | } 103 | 104 | test(firstDataByteProperty) { 105 | knxTelegram->setFirstDataByte(B1100); 106 | assertEquals(B1100, knxTelegram->getFirstDataByte()); 107 | } 108 | 109 | test(checksumProperty) { 110 | knxTelegram->createChecksum(); 111 | assertTrue(knxTelegram->verifyChecksum()); 112 | } 113 | 114 | test(receivingGroupAddresses) { 115 | knx.addListenGroupAddress(15, 15, 100); 116 | assertTrue(knx.isListeningToGroupAddress(15, 15, 100)); 117 | assertTrue(! knx.isListeningToGroupAddress(15, 3, 28)); 118 | } 119 | 120 | test(floatValues) { 121 | knxTelegram->set2ByteFloatValue(25.28); 122 | assertEquals(4, knxTelegram->getPayloadLength()); 123 | assertEquals(25.28 * 100.0, knxTelegram->get2ByteFloatValue() * 100); 124 | } 125 | 126 | 127 | void loop() { 128 | suite.run(); 129 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Arduino EIB/KNX Interface via TP-UART 2 | ===================================== 3 | 4 | 5 | This is a very first version of an interface between Arduino and EIB/KNX using the TP-UART interface and Arduino library. 6 | 7 | Last modified 06.06.2017 8 | 9 | Hardware 10 | -------- 11 | 12 | We leverage Siemens BCU 5WG1 117-2AB12 - for about ~30€ 13 | 14 | -> Please note: When using an additional power supply galvanic separation between ARDUINO and BCU is necessary. 15 | 16 | 17 | We leverage ARDUINO MEGA (SERIAL PORT 1) 18 | 19 | -> No restrictions on the programming 20 | 21 | 22 | We leverage ARDUINO UNO (SERIAL PORT) 23 | 24 | -> During programming, the BCU may have no connection to the ARDUINO UNO. 25 | 26 | -> After programming for communication with the BCU. Connect the jumpers ICSP1 PIN 5 and ICSP1 PIN 6 together, tested only with ARDUINO UNO revision 3. 27 | 28 | -> For programming the jumper ICSP1 PIN 5 and ICSP1 PIN 6 must be not connected together and the voltage must be taken away for a short time. Then, you can transfer the new "sketch". 29 | 30 | 31 | Software (library was tested with IDE 1.0.5-R2 and IDE 1.6.9 by arduino.cc) 32 | --------------------------------------------------------------------------- 33 | 34 | The Arduino library is found in the directory `KnxTpUart` and can be directly placed in Arduino's library folder. 35 | 36 | 37 | Issues, Comments and Suggestions 38 | -------------------------------- 39 | 40 | If you have any, don't hesitate to contact me or use the issue tracker. You are also invited to improve the code and send me a pull request to reintegrate the changes here. 41 | 42 | 43 | Supported commands / telegram types 44 | ----------------------------------- 45 | All commando (write) to bus : 46 | ----------------------------- 47 | 48 | Bool (DPT 1 - 0 or 1) 49 | 50 | knx.groupWriteBool("1/2/3", bool); 51 | 52 | 53 | 54 | 4 Bit Int (DPT 3) 55 | 56 | knx.groupWrite4BitInt("1/2/3", int); 57 | 58 | 59 | 60 | 4 Bit Dim (DPT 3) 61 | 62 | knx.groupWrite4BitDim("1/2/3", direction, steps); 63 | 64 | 65 | 66 | 1 Byte Int (DTP 5 - 0...255) 67 | 68 | knx.groupWrite1ByteInt("1/2/3", int); 69 | 70 | 71 | 72 | 2 Byte Int (DTP 7 - 0…65 535]) 73 | 74 | knx.groupWrite2ByteInt("1/2/3", int); 75 | 76 | 77 | 78 | 2 Byte Float (DPT9 - -671 088,64 to 670 760,96 ) 79 | 80 | knx.groupWrite2ByteFloat("1/2/3", float); 81 | 82 | 83 | 84 | 3 Byte Time (DTP 10) 85 | 86 | groupWrite3ByteTime("1/2/3", Weekday, Hour, Minute, Second); 87 | 88 | 89 | 90 | 3 Byte Date (DTP 11) 91 | 92 | groupWrite3ByteDate("1/2/3", Day, Month, Year); 93 | 94 | 95 | 96 | 4 byte Float (DTP 14 - -2147483648 to 2147483647) 97 | 98 | knx.groupWrite4ByteFloat("1/2/3", float); 99 | 100 | 101 | 102 | 14 Byte Text (DTP 16) 103 | 104 | knx.groupWrite14ByteText("1/2/3", String); 105 | 106 | 107 | 108 | All commando (answer) to bus : 109 | ------------------------------ 110 | 111 | Bool (DPT 1 - 0 or 1) 112 | 113 | knx.groupAnswerBool("1/2/3", bool); 114 | 115 | 116 | 117 | 4 Bit Int (DPT 3) 118 | 119 | commented out -> knx.groupAnswer4BitInt("1/2/3", int); 120 | 121 | 122 | 123 | 4 Bit Int (DPT 3) 124 | 125 | commented out -> knx.groupWrite4BitDim("1/2/3", bool, byte); 126 | 127 | 128 | 129 | 1 Byte Int (DTP 5 - 0...255) 130 | 131 | knx.groupAnswer1ByteInt("1/2/3", int); 132 | 133 | 134 | 135 | 2 Byte Int (DTP 7 - 0…65 535]) 136 | 137 | knx.groupAnswer2ByteInt("1/2/3", int); 138 | 139 | 140 | 141 | 2 Byte Float (DPT9 - -671 088,64 to 670 760,96 ) 142 | 143 | knx.groupAnswer2ByteFloat("1/2/3", float); 144 | 145 | 146 | 147 | 3 Byte Time (DTP 10) 148 | 149 | knx.groupAnswer3ByteTime("1/2/3", int, int, int, int); 150 | 151 | 152 | 153 | 3 Byte Date (DTP 11) 154 | 155 | knx.groupAnswer3ByteDate("1/2/3", int, int, int); 156 | 157 | 158 | 159 | 4 byte Float (DTP 14 - -2147483648 to 2147483647) 160 | 161 | knx.groupAnswer4ByteFloat("1/2/3", float); 162 | 163 | 164 | 165 | 14 Byte Text (DTP 16) 166 | 167 | knx.groupAnswer14ByteText("1/2/3", String); 168 | 169 | 170 | Once for all DTP -> commando (read) to bus : 171 | -------------------------------------------- 172 | 173 | knx.groupRead("1/2/3"); 174 | 175 | 176 | 177 | Evaluation telegrams from bus : 178 | ------------------------------- 179 | 180 | Bool (DPT 1 - 0 or 1) 181 | 182 | value = telegram->getBool(); 183 | 184 | 185 | 186 | 4 Bit Int (DPT 3) 187 | 188 | value = telegram->get4BitIntValue(); 189 | 190 | 191 | 192 | Bool (DPT 3) 193 | 194 | value = telegram->get4BitDirectionValue(); 195 | 196 | 197 | 198 | 1 byte (DPT 3) 199 | 200 | value = telegram->get4BitStepsValue(); 201 | 202 | 203 | 204 | 1 Byte Int (DTP 5 - 0...255) 205 | 206 | value = telegram->get1ByteIntValue(); 207 | 208 | 209 | 210 | 2 Byte Int (DTP 7 - 0…65 535]) 211 | 212 | value = telegram->get2ByteIntValue(); 213 | 214 | 215 | 216 | 2 Byte Float (DPT9 - -671 088,64 to 670 760,96 ) 217 | 218 | value = telegram->get2ByteFloatValue(); 219 | 220 | 221 | 222 | 3 Byte Time (DTP 10) 223 | 224 | value = telegram->get3ByteWeekdayValue(); 225 | 226 | 227 | 228 | 3 Byte Time (DTP 10) 229 | 230 | value = telegram->get3ByteHourValue(); 231 | 232 | 233 | 234 | 3 Byte Time (DTP 10) 235 | 236 | value = telegram->get3ByteMinuteValue(); 237 | 238 | 239 | 240 | 3 Byte Time (DTP 10) 241 | 242 | value = telegram->get3ByteSecondValue(); 243 | 244 | 245 | 246 | 3 Byte Time (DTP 11) 247 | 248 | value = telegram->get3ByteDayValue(); 249 | 250 | 251 | 3 Byte Time (DTP 11) 252 | 253 | value = telegram->get3ByteMonthValue(); 254 | 255 | 256 | 257 | 3 Byte Time (DTP 11) 258 | 259 | value = telegram->get3ByteYearValue(); 260 | 261 | 262 | 263 | 4 byte Float (DTP 14 - -2147483648 to 2147483647) 264 | 265 | value = telegram->get4ByteFloatValue(); 266 | 267 | 268 | 269 | 14 Byte Text (DTP 16) 270 | 271 | value = telegram->get14ByteValue(); --------------------------------------------------------------------------------