├── Arduino-Code └── ArduinoVBusDecoder.ino ├── Documentation ├── README.md ├── VBus-Protokollspezification_en_270111.pdf └── resol-circuit-diagram-v3.jpg └── README.md /Arduino-Code/ArduinoVBusDecoder.ino: -------------------------------------------------------------------------------- 1 | /* VBus decoder to Domoticz sketch for Arduino 2 | * * 3 | * Version 1.1 - August 2017 - Added Joule / Resol Deltasol C (credits: Fatbeard) 4 | * Version 1.0 - January 2016 5 | * 6 | * Copyright (c) 2016 - 'bbqkees' @ www.domoticz.com/forum 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 8 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 9 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, 10 | * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 14 | * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 15 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 16 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 17 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 18 | * 19 | * Legal Notices 20 | * RESOL, VBus, VBus.net and others are trademarks or registered trademarks of RESOL - Elektronische Regelungen GmbH. 21 | * All other trademarks are the property of their respective owners. 22 | * 23 | * 24 | * * What does it do? 25 | * This sketch reads the VBus data and depending on the format of the controller decodes 26 | * the data and puts in in variables. 27 | * You can then send the values via HTTP GET requests to Domoticz. 28 | * 29 | * In this sketch the VBus data is read out continously and send periodically to Domoticz 30 | * If the VBus is read out periodically, it sometimes reads nothing and you get all zero's. 31 | * 32 | * * Currently supports the following controllers: 33 | * -Resol DeltaTherm FK (a.k.a. Oranier Aquacontrol III) 34 | * -Conergy DT5 35 | * -Joule / Resol Deltasol C 36 | * If it does not find any of the supported controllers, 37 | * it will try to decode the first 2 frames which usually contain Temp 1 to 4. 38 | * 39 | * * My controller is not in your list, how can I add it? 40 | * Go to http://danielwippermann.github.io/resol-vbus/vbus-packets.html 41 | * and find your controller. In the list you can see which information the controller sends. 42 | * You need the controller ID and offset, bitsize and names of all the variables. 43 | * Now use the examples for the DT5 and FK in VBusRead() 44 | * to create a new entry for your own controller. 45 | * 46 | * Sketch is based on VBus library from 'Willie' from the Mbed community. 47 | * 48 | * * Hardware info: 49 | * VBus is NOT RS485. So you need a specific converter circuit to make the VBus data 50 | * readable for the Arduino UART. 51 | * See the Domoticz Wiki or my GitHub for a working example circuit. 52 | * 53 | * This sketch uses the Arduino Mega and the Wiznet 5100 ethernet shield. 54 | * You can also use another Arduino like the Uno but it only has one hardware serial port. 55 | * 56 | * Serial1 is used for the VBus module. 57 | * Serial is used to debug the output to PC. 58 | * Vbus serial works with 9600 Baudrate and 8N1. 59 | * 60 | * Arduino Mega: 61 | * Serial on pins 0 (RX) and 1 (TX), 62 | * Serial1 on pins 19 (RX) and 18 (TX), 63 | * Serial2 on pins 17 (RX) and 16 (TX), 64 | * Serial3 on pins 15 (RX) and 14 (TX). 65 | * 66 | * Arduino non-Mega: 67 | * You can use f.i. AltSoftSerial() instead of Serial1. 68 | * This sketch should work with AltSoftSerial. 69 | * If you do use it, you need to put the VBus input on the correct softserial pin! 70 | * If you do not need the debugging option, use 'normal' Serial. 71 | * 72 | * * Is this sketch the pinnacle of proper programming? 73 | * Not by any means, but it works. 74 | * 75 | * * Should I have made a library instead of a sketch? 76 | * Maybe, but this allows for better modification by myself or others. 77 | * Also depending on the Arduino you need to set another Serial port. 78 | * This is easier to do in the sketch. 79 | * 80 | */ 81 | 82 | /* Other settings: 83 | * 84 | * Domoticz JSON URL's: 85 | * Temp 86 | * /json.htm?type=command¶m=udevice&idx=IDX&nvalue=0&svalue=TEMP 87 | * Temp/Hum 88 | * /json.htm?type=command¶m=udevice&idx=IDX&nvalue=0&svalue=TEMP;HUM;HUM_STAT 89 | * Switch on 90 | * /json.htm?type=command¶m=switchlight&idx=99&switchcmd=On 91 | * Switch off 92 | * /json.htm?type=command¶m=switchlight&idx=99&switchcmd=Off 93 | * 94 | */ 95 | 96 | // SPI and Ethernet are used for Ethernet shield 97 | #include 98 | #include 99 | 100 | // Ethernet uses 10,11,12,13 101 | // and 4 for the SD card. 102 | 103 | // MAC and IP of the Arduino 104 | byte mac[] = { 105 | 0xDE, 0xAD, 0xBE, 0x30, 0x39, 0x32}; 106 | IPAddress ip(0,0,0,0); //The IP addres of this Arduino 107 | 108 | // IP address and port of Domoticz 109 | IPAddress domoticz(192,168,0,0); //The IP addres of the Domoticz server 110 | int port = 8080; 111 | EthernetClient client; 112 | 113 | long lastTime = 0; // will store last time 114 | long lastTimedht = 0; 115 | long lastTimetemp = 0; 116 | long lastTimevbus = 0; 117 | 118 | long intervalvbus = 30000; // interval at which to send data (milliseconds) 119 | 120 | 121 | // Change the IDX values below to your own! 122 | 123 | int IDXtempBuffer1 = 111; //(Resol) 124 | int IDXtempBuffer4 = 111; //(Resol) 125 | int IDXtempHaardUit = 111; //(Resol) 126 | int IDXtempCvRetour = 111; //(Resol) 127 | int IDXrelaisHaardPomp = 111; //(Resol) 128 | int IDXrelaisHaard3Weg = 111; //(Resol) 129 | int IDXsytemNotification = 111; //(Resol) 130 | 131 | String relayPump = "off"; 132 | String relay3WayValve = "off"; 133 | String SystemAlert = "off"; 134 | 135 | // Settings for the VBus decoding 136 | #define Sync 0xAA // Synchronisation bytes 137 | #define FLength 6 // Framelength 138 | #define FOffset 10 // Offset start of Frames 139 | #define FSeptet 4 // Septet byte in Frame 140 | #define ResolAddress 0x3271 // ConergyDT5 (0x3271) Original default Address of the controller 141 | #define SENSORNOTCONNECTED 8888 // Sometimes this might be 888 instead. 142 | 143 | uint16_t networkaddress; 144 | float Sensor1_temp; 145 | float Sensor2_temp; 146 | float Sensor3_temp; 147 | float Sensor4_temp; 148 | 149 | float Sensor1_temp_max; 150 | float Sensor2_temp_max; 151 | float Sensor3_temp_max; 152 | float Sensor4_temp_max; 153 | 154 | // Conergy DT5 specific 155 | char PumpSpeed1; // in % 156 | char PumpSpeed2; // in % 157 | char RelaisMask; 158 | char ErrorMask; 159 | char Scheme; 160 | char OptionPostPulse; 161 | char OptionThermostat; 162 | char OptionHQM; 163 | uint16_t OperatingHoursRelais1; 164 | uint16_t OperatingHoursRelais2; 165 | uint32_t HeatQuantity; 166 | uint16_t Version; 167 | uint16_t OperatingHoursRelais1Today; 168 | uint16_t SystemTime; 169 | // 170 | 171 | 172 | //Resol DeltaTherm FK specific 173 | char Relay1; // in % 174 | char Relay2; // in % 175 | char MixerOpen; // in % 176 | char MixerClosed; // in % 177 | char SystemNotification; 178 | // 179 | 180 | // These are set neither to 'on' or 'off' at initialization so at startup the value 181 | // from the first valid datagram will be sent to Domoticz. 182 | String lastRelay1 = "1"; 183 | String lastRelay2 = "1" ; 184 | String lastSystemAlert = "1" ; 185 | 186 | unsigned char Buffer[80]; 187 | volatile unsigned char Bufferlength; 188 | 189 | unsigned int Destination_address; 190 | unsigned int Source_address; 191 | unsigned char ProtocolVersion; 192 | unsigned int Command; 193 | unsigned char Framecnt; 194 | unsigned char Septet; 195 | unsigned char Checksum; 196 | 197 | long lastTimeTimer; 198 | long timerInterval; 199 | bool all; 200 | 201 | // Set to "1" when debugging 202 | // If enabled, the Arduino sends the decoded values over the Serial port. 203 | // If enabled it also prints the source address in case you do not 204 | // know what controller(s) you have. 205 | #define DEBUG 0 206 | 207 | // Clear all maximum values 208 | void VBusClearMax() { 209 | Sensor1_temp_max=0.0; 210 | Sensor2_temp_max=0.0; 211 | Sensor3_temp_max=0.0; 212 | Sensor4_temp_max=0.0; 213 | } 214 | 215 | // Initialize some parameters 216 | // Currently setting the controller does nothing special. 217 | void VBusInit(int addr=0) { 218 | timerInterval=2000; 219 | if (addr!=0) 220 | networkaddress=addr; 221 | else 222 | networkaddress=ResolAddress; 223 | 224 | } 225 | 226 | void setup() { 227 | Serial1.begin(9600); 228 | #if DEBUG 229 | Serial.begin(9600); 230 | Serial.println("Arduino debugging started"); 231 | #endif 232 | 233 | Ethernet.begin(mac, ip); 234 | 235 | all=false; 236 | VBusClearMax(); 237 | VBusInit(0x5611); //0 for Conergy DT5, 0x5611 for DeltaTherm FK 238 | } // end void setup() 239 | 240 | void loop() { 241 | 242 | if (VBusRead()){ 243 | #if DEBUG 244 | Serial.println("------Decoded VBus data------"); 245 | Serial.print("Destination: "); 246 | Serial.println(Destination_address, HEX); 247 | Serial.print("Source: "); 248 | Serial.println(Source_address, HEX); 249 | Serial.print("Protocol Version: "); 250 | Serial.println(ProtocolVersion); 251 | Serial.print("Command: "); 252 | Serial.println(Command, HEX); 253 | Serial.print("Framecount: "); 254 | Serial.println(Framecnt); 255 | Serial.print("Checksum: "); 256 | Serial.println(Checksum); 257 | Serial.println("------Values------"); 258 | Serial.print("Sensor 1: "); 259 | Serial.println(Sensor1_temp); 260 | Serial.print("Sensor 2: "); 261 | Serial.println(Sensor2_temp); 262 | Serial.print("Sensor 3: "); 263 | Serial.println(Sensor3_temp); 264 | Serial.print("Sensor 4: "); 265 | Serial.println(Sensor4_temp); 266 | Serial.print("Relay 1: "); 267 | Serial.println(Relay1, DEC); 268 | Serial.print("Relay 2: "); 269 | Serial.println(Relay2, DEC); 270 | Serial.println(SystemNotification, DEC); 271 | Serial.println("------END------"); 272 | #endif 273 | } //end VBusRead 274 | 275 | /* 276 | * S1 = Sensor 1 (sensor SFB/stove) 277 | S2 = Sensor 2 (sensor store base) 278 | S3 = Sensor 3 (sensor store top) 279 | S4 = Sensor 4 (system-dependent) 280 | * R1 = Pump 281 | * R2 = 3-way valve 282 | */ 283 | 284 | // loop for VBus readout and http GET requests 285 | // This loop is executed every intervalvbus milliseconds. 286 | if(millis() - lastTimevbus > intervalvbus) { 287 | lastTimevbus = millis(); 288 | 289 | // Convert relay value to On or Off. 290 | if (Relay1 == 0x64){relayPump = "On";} 291 | else if (Relay1 == 0x00){relayPump = "Off";} 292 | else {relayPump = "Off";} 293 | 294 | if (Relay2 == 0x64){relay3WayValve = "On";} 295 | else if (Relay2 == 0x00){relay3WayValve = "Off";} 296 | else {relay3WayValve = "Off";} 297 | 298 | if (SystemNotification != 0x00){SystemAlert = "On";} 299 | else if (SystemNotification == 0x00){SystemAlert = "Off";} 300 | else {SystemAlert = "Off";} 301 | 302 | // In the first (few) VBus readout all values are zero. 303 | // In order to prevent we are sending incorrect data to Domoticz we check it is not zero. 304 | 305 | // Only send relay status to Domoticz if it's state has changed 306 | if (Sensor1_temp != 0 && relayPump != lastRelay1){ 307 | httpRequestSwitch(IDXrelaisHaardPomp, relayPump); 308 | lastRelay1 = relayPump; 309 | } 310 | 311 | if (Sensor1_temp != 0 && relay3WayValve != lastRelay2){ 312 | httpRequestSwitch(IDXrelaisHaard3Weg, relay3WayValve); 313 | lastRelay2 = relay3WayValve; 314 | } 315 | 316 | if (Sensor3_temp != 0){httpRequestTemp(IDXtempBuffer1, Sensor3_temp);} 317 | if (Sensor2_temp != 0){httpRequestTemp(IDXtempBuffer4, Sensor2_temp);} 318 | if (Sensor1_temp != 0){httpRequestTemp(IDXtempHaardUit, Sensor1_temp);} 319 | if (Sensor4_temp != 0){httpRequestTemp(IDXtempCvRetour, Sensor4_temp);} 320 | 321 | // Only send a alert if the value has changed. 322 | if (Sensor1_temp != 0 && SystemAlert != lastSystemAlert){ 323 | httpRequestSwitch(IDXrelaisHaardPomp, SystemAlert); 324 | lastSystemAlert = SystemAlert; 325 | } 326 | } //end loop VBus readout 327 | 328 | 329 | }// end void loop() 330 | 331 | 332 | #if DEBUG 333 | #endif 334 | 335 | // The following is needed for decoding the data 336 | void InjectSeptet(unsigned char *Buffer, int Offset, int Length) { 337 | for (unsigned int i = 0; i < Length; i++) { 338 | if (Septet & (1 << i)) { 339 | Buffer [Offset + i] |= 0x80; 340 | } 341 | } 342 | } 343 | 344 | 345 | // The following function reads the data from the bus and converts it all 346 | // depending on the used VBus controller. 347 | bool VBusRead() { 348 | int F; 349 | char c; 350 | bool start,stop,quit; 351 | 352 | start = true; 353 | stop = false; 354 | quit = false; 355 | Bufferlength=0; 356 | lastTimeTimer = 0; 357 | lastTimeTimer = millis(); 358 | 359 | while ((!stop) and (!quit)) { 360 | if (Serial1.available()) { 361 | c=Serial1.read(); 362 | 363 | char sync1 = 0xAA; 364 | if (c == sync1) { 365 | 366 | #if DEBUG 367 | // Serial.println("Sync found"); 368 | #endif 369 | 370 | if (start) { 371 | start=false; 372 | Bufferlength=0; 373 | //#if DEBUG 374 | //#endif 375 | } else { 376 | if (Bufferlength<20) { 377 | lastTimeTimer = 0; 378 | lastTimeTimer = millis(); 379 | Bufferlength=0; 380 | } else 381 | stop=true; 382 | } 383 | } 384 | #if DEBUG 385 | // Serial.println(c, HEX); 386 | #endif 387 | if ((!start) and (!stop)) { 388 | Buffer[Bufferlength]=c; 389 | Bufferlength++; 390 | } 391 | } 392 | if ((timerInterval > 0) && (millis() - lastTimeTimer > timerInterval ) ) { 393 | quit=true; 394 | #if DEBUG 395 | // Serial.print("Timeout: "); 396 | // Serial.println(lastTimeTimer); 397 | #endif 398 | } 399 | } 400 | 401 | lastTimeTimer = 0; 402 | 403 | if (!quit) { 404 | Destination_address = Buffer[2] << 8; 405 | Destination_address |= Buffer[1]; 406 | Source_address = Buffer[4] << 8; 407 | Source_address |= Buffer[3]; 408 | ProtocolVersion = (Buffer[5]>>4) + (Buffer[5] &(1<<15)); 409 | 410 | Command = Buffer[7] << 8; 411 | Command |= Buffer[6]; 412 | Framecnt = Buffer[8]; 413 | Checksum = Buffer[9]; //TODO check if Checksum is OK 414 | #if DEBUG 415 | Serial.println("---------------"); 416 | Serial.print("Destination: "); 417 | Serial.println(Destination_address, HEX); 418 | Serial.print("Source: "); 419 | Serial.println(Source_address, HEX); 420 | Serial.print("Protocol Version: "); 421 | Serial.println(ProtocolVersion); 422 | Serial.print("Command: "); 423 | Serial.println(Command, HEX); 424 | Serial.print("Framecount: "); 425 | Serial.println(Framecnt); 426 | Serial.print("Checksum: "); 427 | Serial.println(Checksum); 428 | Serial.println("---------------"); 429 | 430 | #endif 431 | // Only analyse Commands 0x100 = Packet Contains data for slave 432 | // with correct length = 10 bytes for HEADER and 6 Bytes for each frame 433 | 434 | if ((Command==0x0100) and (Bufferlength==10+Framecnt*6)) { 435 | 436 | //Only decode the data from the correct source address 437 | //(There might be other VBus devices on the same bus). 438 | 439 | if (Source_address ==0x3271){ 440 | #if DEBUG 441 | Serial.println("---------------"); 442 | Serial.println("Now decoding for 0x3271"); 443 | Serial.println("---------------"); 444 | 445 | #endif 446 | 447 | // Frame info for the Resol ConergyDT5 448 | // check VBusprotocol specification for other products 449 | 450 | // This library is made for the ConergyDT5 (0x3271) 451 | 452 | //Offset Size Mask Name Factor Unit 453 | //0 2 Temperature sensor 1 0.1 �C 454 | //2 2 Temperature sensor 2 0.1 �C 455 | //4 2 Temperature sensor 3 0.1 �C 456 | //6 2 Temperature sensor 4 0.1 �C 457 | //8 1 Pump speed pump 1 1 458 | //9 1 Pump speed pump 2 1 459 | //10 1 Relay mask 1 460 | //11 1 Error mask 1 461 | //12 2 System time 1 462 | //14 1 Scheme 1 463 | //15 1 1 Option PostPulse 1 464 | //15 1 2 Option thermostat 1 465 | //15 1 4 Option HQM 1 466 | //16 2 Operating hours relay 1 1 467 | //18 2 Operating hours relay 2 1 468 | //20 2 Heat quantity 1 Wh 469 | //22 2 Heat quantity 1000 Wh 470 | //24 2 Heat quantity 1000000 Wh 471 | //26 2 Version 0.01 472 | // 473 | // Each frame has 6 bytes 474 | // byte 1 to 4 are data bytes -> MSB of each bytes 475 | // byte 5 is a septet and contains MSB of bytes 1 to 4 476 | // byte 6 is a checksum 477 | // 478 | //******************* Frame 1 ******************* 479 | 480 | F=FOffset; 481 | 482 | Septet=Buffer[F+FSeptet]; 483 | InjectSeptet(Buffer,F,4); 484 | 485 | // 'collector1' Temperatur Sensor 1, 15 bits, factor 0.1 in C 486 | Sensor1_temp =CalcTemp(Buffer[F+1], Buffer[F]); 487 | // 'store1' Temperature sensor 2, 15 bits, factor 0.1 in C 488 | Sensor2_temp =CalcTemp(Buffer[F+3], Buffer[F+2]); 489 | 490 | //******************* Frame 2 ******************* 491 | F=FOffset+FLength; 492 | 493 | Septet=Buffer[F+FSeptet]; 494 | InjectSeptet(Buffer,F,4); 495 | 496 | Sensor3_temp =CalcTemp(Buffer[F+1], Buffer[F]); 497 | Sensor4_temp =CalcTemp(Buffer[F+3], Buffer[F+2]); 498 | 499 | //******************* Frame 3 ******************* 500 | F=FOffset+FLength*2; 501 | 502 | Septet=Buffer[F+FSeptet]; 503 | InjectSeptet(Buffer,F,4); 504 | 505 | PumpSpeed1 = (Buffer[F] & 0X7F); 506 | PumpSpeed2 = (Buffer[F+1] & 0X7F); 507 | RelaisMask = Buffer[F+2]; 508 | ErrorMask = Buffer[F+3]; 509 | 510 | //******************* Frame 4 ******************* 511 | F=FOffset+FLength*3; 512 | 513 | Septet=Buffer[F+FSeptet]; 514 | InjectSeptet(Buffer,F,4); 515 | 516 | SystemTime = Buffer[F+1] << 8 | Buffer[F]; 517 | Scheme = Buffer[F+2]; 518 | 519 | OptionPostPulse = (Buffer[F+3] & 0x01); 520 | OptionThermostat = ((Buffer[F+3] & 0x02) >> 1); 521 | OptionHQM = ((Buffer[F+3] & 0x04) >> 2); 522 | 523 | //******************* Frame 5 ******************* 524 | F=FOffset+FLength*4; 525 | 526 | Septet=Buffer[F+FSeptet]; 527 | InjectSeptet(Buffer,F,4); 528 | 529 | OperatingHoursRelais1=Buffer[F+1] << 8 | Buffer[F]; 530 | OperatingHoursRelais2=Buffer[F+3] << 8| Buffer[F+2]; 531 | 532 | //******************* Frame 6 ******************* 533 | F=FOffset+FLength*5; 534 | 535 | Septet=Buffer[F+FSeptet]; 536 | InjectSeptet(Buffer,F,4); 537 | 538 | HeatQuantity=(Buffer[F+1] << 8 | Buffer[F])+(Buffer[F+3] << 8| Buffer[F+2])*1000; 539 | 540 | //******************* Frame 7 ******************* 541 | F=FOffset+FLength*6; 542 | 543 | Septet=Buffer[F+FSeptet]; 544 | InjectSeptet(Buffer,F,4); 545 | 546 | HeatQuantity=HeatQuantity+(Buffer[F+1] << 8 | Buffer[F])*1000000; 547 | Version=Buffer[F+3] << 8| Buffer[F+2]; 548 | 549 | ///******************* End of frames **************** 550 | 551 | }// end 0x3271 Conenergy DT5 552 | 553 | else if (Source_address ==0x5611){ 554 | #if DEBUG 555 | Serial.println("---------------"); 556 | Serial.println("Now decoding for 0x5611"); 557 | Serial.println("---------------"); 558 | 559 | #endif 560 | // Frame info for the Resol Deltatherm FK and Oranier Aquacontrol III 561 | // check VBusprotocol specification for other products 562 | 563 | // 564 | 565 | //Offset Size Mask Name Factor Unit 566 | // Frame 1 567 | //0 2 Temperature sensor 1 0.1 �C 568 | //2 2 Temperature sensor 2 0.1 �C 569 | // Frame 2 570 | //4 2 Temperature sensor 3 0.1 �C 571 | //6 2 Temperature sensor 4 0.1 �C 572 | // Frame 3 573 | //8 1 Relay 1 1 % 574 | //9 1 Relay 2 1 % 575 | //10 1 Mixer open 1 % 576 | //11 1 Mixer closed 1 % 577 | // Frame 4 578 | //12 4 System date 1 579 | // Frame 5 580 | //16 2 System time 1 581 | //18 1 System notification 1 582 | // 583 | // Each frame has 6 bytes 584 | // byte 1 to 4 are data bytes -> MSB of each bytes 585 | // byte 5 is a septet and contains MSB of bytes 1 to 4 586 | // byte 6 is a checksum 587 | // 588 | //******************* Frame 1 ******************* 589 | 590 | F=FOffset; 591 | 592 | Septet=Buffer[F+FSeptet]; 593 | InjectSeptet(Buffer,F,4); 594 | 595 | Sensor1_temp =CalcTemp(Buffer[F+1], Buffer[F]); 596 | Sensor2_temp =CalcTemp(Buffer[F+3], Buffer[F+2]); 597 | Serial.println( Buffer[F], HEX); 598 | Serial.println( Buffer[F+1], HEX); 599 | Serial.println( Buffer[F+2], HEX); 600 | Serial.println( Buffer[F+3], HEX); 601 | 602 | //******************* Frame 2 ******************* 603 | F=FOffset+FLength; 604 | 605 | Septet=Buffer[F+FSeptet]; 606 | InjectSeptet(Buffer,F,4); 607 | 608 | Sensor3_temp =CalcTemp(Buffer[F+1], Buffer[F]); 609 | Sensor4_temp =CalcTemp(Buffer[F+3], Buffer[F+2]); 610 | Serial.println( Buffer[F], HEX); 611 | Serial.println( Buffer[F+1], HEX); 612 | Serial.println( Buffer[F+2], HEX); 613 | Serial.println( Buffer[F+3], HEX); 614 | //******************* Frame 3 ******************* 615 | F=FOffset+FLength*2; 616 | 617 | Septet=Buffer[F+FSeptet]; 618 | InjectSeptet(Buffer,F,4); 619 | 620 | // Some of the values are 7 bit instead of 8. 621 | // Adding '& 0x7F' means you are only interested in the first 7 bits. 622 | // 0x7F = 0b1111111. 623 | // See: http://stackoverflow.com/questions/9552063/c-language-bitwise-trick 624 | Relay1 = (Buffer[F] & 0X7F); 625 | Relay2 = (Buffer[F+1] & 0X7F); 626 | MixerOpen = (Buffer[F+2] & 0X7F); 627 | MixerClosed = (Buffer[F+3] & 0X7F); 628 | //******************* Frame 4 ******************* 629 | F=FOffset+FLength*3; 630 | 631 | Septet=Buffer[F+FSeptet]; 632 | InjectSeptet(Buffer,F,4); 633 | 634 | // System date is not needed for Domoticz 635 | 636 | //******************* Frame 5 ******************* 637 | F=FOffset+FLength*4; 638 | 639 | Septet=Buffer[F+FSeptet]; 640 | InjectSeptet(Buffer,F,4); 641 | 642 | // System time is not needed for Domoticz 643 | 644 | // Status codes System Notification according to Resol: 645 | //0: no error / warning 646 | //1: S1 defect 647 | //2: S2 defect 648 | //3: S3 defect 649 | //4: VFD defect 650 | //5: Flow rate? 651 | //6: ΔT too high 652 | //7: Low water level 653 | 654 | SystemNotification = Buffer[F+2]; 655 | 656 | ///******************* End of frames **************** 657 | 658 | } //End 0x5611 Resol DeltaTherm FK 659 | 660 | 661 | else if (Source_address == 0x4212) { 662 | #if DEBUG 663 | Serial.println("---------------"); 664 | Serial.println("Now decoding for DeltaSol C 0x4212"); 665 | Serial.println("---------------"); 666 | 667 | #endif 668 | 669 | // Frame info for the Resol DeltaSol C (Joule) 670 | // check VBusprotocol specification for other products 671 | 672 | // This library is made for the Resol DeltaSol C (0x4212) 673 | 674 | 675 | //Offset Mask Name Factor Unit 676 | //0 Temperature S1 1.0 °C 677 | //1 Temperature S1 256.0 °C 678 | //2 Temperature S2 1.0 °C 679 | //3 Temperature S2 256.0 °C 680 | //4 Temperature S3 1.0 °C 681 | //5 Temperature S3 256.0 °C 682 | //6 Temperature S4 1.0 °C 683 | //7 Temperature S4 256.0 °C 684 | //8 Pump Speed R1 1 % 685 | //9 Pump Speed R2 1 % 686 | //10 Error Mask 1 687 | //11 Scheme 1 688 | //12 Operating Hours R1 1 h 689 | //13 Operating Hours R1 256 h 690 | //14 Operating Hours R2 1 h 691 | //15 Operating Hours R2 256 h 692 | //16 Heat Quantity 1 Wh 693 | //17 Heat Quantity 256 Wh 694 | //18 Heat Quantity 1000 Wh 695 | //19 Heat Quantity 256000 Wh 696 | //20 Heat Quantity 1000000 Wh 697 | //21 Heat Quantity 256000000 Wh 698 | //22 Minute of Day 1 699 | //23 Minute of Day 256 700 | 701 | // 702 | // Each frame has 6 bytes 703 | // byte 1 to 4 are data bytes -> MSB of each bytes 704 | // byte 5 is a septet and contains MSB of bytes 1 to 4 705 | // byte 6 is a checksum 706 | // 707 | //******************* Frame 1 ******************* 708 | 709 | F = FOffset; 710 | 711 | Septet = Buffer[F + FSeptet]; 712 | InjectSeptet(Buffer, F, 4); 713 | 714 | // 'collector1' Temperatur Sensor 1, 15 bits, factor 0.1 in C 715 | Sensor1_temp = CalcTemp(Buffer[F + 1], Buffer[F]); 716 | // 'store1' Temperature sensor 2, 15 bits, factor 0.1 in C 717 | Sensor2_temp = CalcTemp(Buffer[F + 3], Buffer[F + 2]); 718 | 719 | //******************* Frame 2 ******************* 720 | F = FOffset + FLength; 721 | 722 | Septet = Buffer[F + FSeptet]; 723 | InjectSeptet(Buffer, F, 4); 724 | 725 | Sensor3_temp = CalcTemp(Buffer[F + 1], Buffer[F]); 726 | Sensor4_temp = CalcTemp(Buffer[F + 3], Buffer[F + 2]); 727 | 728 | //******************* Frame 3 ******************* 729 | F = FOffset + FLength * 2; 730 | 731 | Septet = Buffer[F + FSeptet]; 732 | InjectSeptet(Buffer, F, 4); 733 | 734 | Relay1 = (Buffer[F] ); 735 | Relay2 = (Buffer[F + 1]); 736 | ErrorMask = Buffer[F + 2]; //This is the notification 737 | Scheme = Buffer[F + 3]; 738 | 739 | //******************* Frame 4 ******************* 740 | F = FOffset + FLength * 3; 741 | 742 | Septet = Buffer[F + FSeptet]; 743 | InjectSeptet(Buffer, F, 4); 744 | 745 | OperatingHoursRelais1 = Buffer[F + 1] << 8 | Buffer[F]; 746 | OperatingHoursRelais2 = Buffer[F + 3] << 8 | Buffer[F + 2];; 747 | 748 | //******************* Frame 5 ******************* 749 | F = FOffset + FLength * 4; 750 | 751 | Septet = Buffer[F + FSeptet]; 752 | InjectSeptet(Buffer, F, 4); 753 | 754 | HeatQuantity = (Buffer[F + 1] << 8 | Buffer[F]) + (Buffer[F + 3] << 8 | Buffer[F + 2]) * 1000; 755 | 756 | //******************* Frame 6 ******************* 757 | F = FOffset + FLength * 5; 758 | 759 | Septet = Buffer[F + FSeptet]; 760 | InjectSeptet(Buffer, F, 4); 761 | 762 | HeatQuantity = HeatQuantity + (Buffer[F + 1] << 8 | Buffer[F]) * 1000000; 763 | 764 | 765 | SystemTime = Buffer[F + 3] << 8 | Buffer[F + 2]; 766 | 767 | ///******************* End of frames **************** 768 | 769 | }// end 0x4212 DeltaSol C 770 | 771 | /* Add your own controller ID and code in the if statement below and uncomment 772 | else if (Source_address ==0x????){ 773 | } 774 | */ 775 | 776 | else { 777 | 778 | // Default temp 1-4 extraction 779 | // For most Resol controllers temp 1-4 are always available, so 780 | // even if you do not know the datagram format you can still see 781 | // these temps 1 to 4. 782 | 783 | // 784 | 785 | //Offset Size Mask Name Factor Unit 786 | // Frame 1 787 | //0 2 Temperature sensor 1 0.1 �C 788 | //2 2 Temperature sensor 2 0.1 �C 789 | // Frame 2 790 | //4 2 Temperature sensor 3 0.1 �C 791 | //6 2 Temperature sensor 4 0.1 �C 792 | // 793 | // Each frame has 6 bytes 794 | // byte 1 to 4 are data bytes -> MSB of each bytes 795 | // byte 5 is a septet and contains MSB of bytes 1 to 4 796 | // byte 6 is a checksum 797 | // 798 | //******************* Frame 1 ******************* 799 | 800 | F=FOffset; 801 | 802 | Septet=Buffer[F+FSeptet]; 803 | InjectSeptet(Buffer,F,4); 804 | 805 | // 'collector1' Temperatur Sensor 1, 15 bits, factor 0.1 in C 806 | Sensor1_temp =CalcTemp(Buffer[F+1], Buffer[F]); 807 | // 'store1' Temperature sensor 2, 15 bits, factor 0.1 in C 808 | Sensor2_temp =CalcTemp(Buffer[F+3], Buffer[F+2]); 809 | 810 | //******************* Frame 2 ******************* 811 | F=FOffset+FLength; 812 | 813 | Septet=Buffer[F+FSeptet]; 814 | InjectSeptet(Buffer,F,4); 815 | 816 | Sensor3_temp =CalcTemp(Buffer[F+1], Buffer[F]); 817 | Sensor4_temp =CalcTemp(Buffer[F+3], Buffer[F+2]); 818 | 819 | ///******************* End of frames **************** 820 | 821 | } //End of Default temp 1-4 extraction 822 | 823 | if (Sensor1_temp>Sensor1_temp_max) 824 | Sensor1_temp_max=Sensor1_temp; 825 | if (Sensor2_temp>Sensor2_temp_max) 826 | Sensor2_temp_max=Sensor2_temp; 827 | if (Sensor3_temp>Sensor3_temp_max) 828 | Sensor3_temp_max=Sensor3_temp; 829 | if (Sensor4_temp>Sensor4_temp_max) 830 | Sensor4_temp_max=Sensor4_temp; 831 | 832 | } // end if command 0x0100 833 | } // end !quit 834 | 835 | return !quit; 836 | } // end VBusRead() 837 | 838 | 839 | // This function converts 2 data bytes to a temperature value. 840 | float CalcTemp(int Byte1, int Byte2) { 841 | int v; 842 | v = Byte1 << 8 | Byte2; //bit shift 8 to left, bitwise OR 843 | 844 | if (Byte1 == 0x00){ 845 | v= v & 0xFF; 846 | } 847 | 848 | if (Byte1 == 0xFF) 849 | v = v - 0x10000; 850 | 851 | if (v==SENSORNOTCONNECTED) 852 | v=0; 853 | 854 | return (float)((float) v * 0.1); 855 | } 856 | 857 | //The part below contain all the httprequest functions. 858 | 859 | // Send a temperature value to Domoticz 860 | // /json.htm?type=command¶m=udevice&idx=IDX&nvalue=0&svalue=TEMP 861 | void httpRequestTemp(int IDX, float temp) { 862 | // if there's a successful connection: 863 | if (client.connect(domoticz, port)) { 864 | client.print( "GET /json.htm?type=command¶m=udevice&idx="); 865 | client.print(IDX); 866 | client.print("&nvalue=0&svalue="); 867 | client.print(temp); 868 | client.println( " HTTP/1.1"); 869 | client.println( "Host: 192.168.0.0"); //change to your IP 870 | client.println( "Connection: close"); 871 | client.println(); 872 | 873 | client.println(); 874 | client.stop(); 875 | delay(150); 876 | } 877 | else { 878 | client.stop(); 879 | } 880 | } 881 | 882 | // Send a switch command to Domoticz 883 | // /json.htm?type=command¶m=switchlight&idx=XX&switchcmd=On 884 | void httpRequestSwitch(int IDX, String status) { 885 | // if there's a successful connection: 886 | if (client.connect(domoticz, port)) { 887 | client.print( "GET /json.htm?type=command¶m=switchlight&idx="); 888 | client.print(IDX); 889 | client.print("&switchcmd="); 890 | client.print(status); 891 | client.println( " HTTP/1.1"); 892 | client.println( "Host: 192.168.0.0"); //change to your IP 893 | client.println( "Connection: close"); 894 | client.println(); 895 | 896 | client.println(); 897 | client.stop(); 898 | delay(150); 899 | } 900 | else { 901 | client.stop(); 902 | } 903 | } 904 | 905 | // Total heat value to Domoticz in Wh 906 | // Only send the total from start, not the daily or weekly! 907 | // Domoticz will calculate daily production by itself. 908 | // json.htm?type=command¶m=udevice&idx=IDX&svalue=COUNTER 909 | void httpRequestPower(int IDX, float counter) { 910 | // if there's a successful connection: 911 | if (client.connect(domoticz, port)) { 912 | client.print( "GET json.htm?type=command¶m=udevice&idx="); 913 | client.print(IDX); 914 | client.print("&svalue="); 915 | client.print(counter); 916 | client.println( " HTTP/1.1"); 917 | client.println( "Host: 192.168.0.0"); //change to your IP 918 | client.println( "Connection: close"); 919 | client.println(); 920 | 921 | client.println(); 922 | client.stop(); 923 | delay(150); 924 | } 925 | else { 926 | client.stop(); 927 | } 928 | } 929 | -------------------------------------------------------------------------------- /Documentation/README.md: -------------------------------------------------------------------------------- 1 | -Placeholder for the documentation folder- 2 | -------------------------------------------------------------------------------- /Documentation/VBus-Protokollspezification_en_270111.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbqkees/vbus-arduino-domoticz/d7564bfa92f93913122dc1a2532af1f9dd999d6c/Documentation/VBus-Protokollspezification_en_270111.pdf -------------------------------------------------------------------------------- /Documentation/resol-circuit-diagram-v3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbqkees/vbus-arduino-domoticz/d7564bfa92f93913122dc1a2532af1f9dd999d6c/Documentation/resol-circuit-diagram-v3.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vbus-arduino-domoticz 2 | Readout of VBus interface Protocol 1.0 by Arduino and transfer of data via HTTP GET requests to Domoticz home automation software (or to other systems). 3 | 4 | ## Goal: 5 | Readout the Resol VBus interface of an Oranier Aquacontrol III (= rebranded Resol DeltaTherm FK). 6 | 7 | Transfer of data via ethernet to Domoticz home automation software. 8 | 9 | ## What does it do? 10 | This sketch reads the VBus data and depending on the format of the controller decodes the data and puts it in variables. 11 | You can then send the values via HTTP GET requests to Domoticz or do whatever you want with it. 12 | Usage is not limited to Domoticz, you can extract the decoding part for other purposes. 13 | 14 | ## Is it stable? 15 | This sketch has been running continuously on an Arduino since October 2015 without any problem whatsoever. 16 | 17 | ## Controller support 18 | Currently supports the following controllers: 19 | * Resol DeltaTherm FK (0x5611) 20 | * Oranier Aquacontrol III (0x5611) 21 | * Conergy DT5 (0x3271) 22 | * Joule / Resol Deltasol C (0x4212) 23 | 24 | If it does not find any of the supported controllers, it will try to decode the first 2 frames which usually contain Temp 1 to 4. 25 | 26 | ## Hardware: 27 | * VBus RX interface circuit 28 | * Arduino Mega + Wiznet Ethernet Shield 29 | * Device running your Domoticz 30 | 31 | VBus is NOT RS485. So you need a specific converter circuit to make the VBus data readable for the Arduino UART. 32 | I used the following circuit from [Piamble](https://piamble.wordpress.com/tag/vbus/): 33 | ![VBUS schematic](https://github.com/bbqkees/vbus-arduino-domoticz/blob/master/Documentation/resol-circuit-diagram-v3.jpg?raw=true) 34 | I only removed the final voltage divider after the comparator and put a series resistor there instead because the Arduino has a 5V compatible UART. 35 | 36 | 37 | This sketch uses the Arduino Mega and the Wiznet 5100 ethernet shield. 38 | You can also use another Arduino like the Uno but that one only has one hardware serial port. 39 | 40 | Serial1 is used for the VBus module. 41 | Serial is used to debug the output to PC. 42 | Vbus serial works with 9600 Baudrate and 8N1. 43 | 44 | Arduino Mega: 45 | * Serial on pins 0 (RX) and 1 (TX), 46 | * Serial1 on pins 19 (RX) and 18 (TX), 47 | * Serial2 on pins 17 (RX) and 16 (TX), 48 | * Serial3 on pins 15 (RX) and 14 (TX). 49 | 50 | Arduino non-Mega: 51 | You can use f.i. AltSoftSerial() instead of Serial1. This sketch should work with AltSoftSerial. 52 | If you do use it, you need to put the VBus input on the correct softserial pin! 53 | If you do not need the debugging option, use 'normal' Serial. 54 | 55 | ### My controller is not in the list, how can I add it? 56 | Go to http://danielwippermann.github.io/resol-vbus/vbus-packets.html 57 | and find your controller. In the list you can see which information the controller sends. 58 | You need the controller ID and offset, bitsize and names of all the variables. 59 | Now use the examples for the DT5 and FK in VBusRead() to create a new entry for your own controller. 60 | This might be not that easy. 61 | Do not forget to properly declare your new variables too. 62 | 63 | If you have tested it and it works, please add a Pull request so I can integrate your controller here. 64 | 65 | ### Can I add your controller? 66 | No. First try it yourself. But if you fail, you can always ask. 67 | 68 | ### Is this sketch the pinnacle of proper programming? 69 | Not by any means, but it works. 70 | If you have any remark or improvement, let the author know. 71 | 72 | ### Should I have made a library instead of a complete sketch? 73 | Maybe, but this allows for better modification by myself or others. Also depending on the Arduino you need to set another Serial port. This is easier to do in the sketch.
74 | If you want to use it as a library have a look here [https://github.com/FatBeard/vbus-arduino-library].
75 | 76 | ### How about MQTT instead of HTTP GET requests? 77 | This is still on my to do list. 78 | I started with the HTTP GET request because I was unaware of MQTT and also at the time the MQTT support in Domoticz lacked proper documentation. 79 | But the HTTP GET requests are working just fine for about 2 years now. 80 | 81 | #### Additional credits 82 | Sketch is based on the VBus library from 'Willie' from the Mbed community. 83 | Also have a look at the [Github of Daniel Wippermann](https://github.com/danielwippermann), the lead software engineer of Resol. He has written lots of useful code for you to use in interfacing with Resol equipment. 84 | 85 | 86 | #### Which license applies here? 87 | The MIT License. This means that you can do whatever you want with my code! No need to ask me. 88 | It is always nice however to publish your version to Github to help others. 89 | 90 | #### Legal Notices 91 | RESOL, VBus, VBus.net and others are trademarks or registered trademarks of RESOL - Elektronische Regelungen GmbH. 92 | 93 | All other trademarks are the property of their respective owners. 94 | --------------------------------------------------------------------------------