1 /** @file
2 Vaillant VRT 340f (calorMatic 340f) central heating control.
3
4 Copyright (C) 2017 Reinhold Kainhofer <reinhold@kainhofer.com>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 */
11 /**
12 Vaillant VRT 340f (calorMatic 340f) central heating control.
13
14 http://wiki.kainhofer.com/hardware/vaillantvrt340f
15
16 The data is sent differential Manchester encoded
17 with bit-stuffing (after five 1 bits an extra 0 bit is inserted)
18
19 All bytes are sent with least significant bit FIRST (1000 0111 = 0xE1)
20
21 0x00 00 7E | 6D F6 | 00 20 00 | 00 | 80 | B4 | 00 | FD 49 | FF 00
22 SYNC+HD. | DevID | CONST? |Rep.|Wtr.|Htg.|Btr.|Checksm| EPILOGUE
23
24 - CONST? ... Unknown, but constant in all observed signals
25 - Rep. ... Repeat indicator: 0x00=original signal, 0x01=first repeat
26 - Wtr. ... pre-heated Water: 0x80=ON, 0x88=OFF (bit 8 is always set)
27 - Htg. ... Heating: 0x00=OFF, 0xB4=ON (2-point), 0x01-0x7F=target heating water temp
28 (bit 8 indicates 2-point heating mode, bits 1-7 the heating water temp)
29 - Btr. ... Battery: 0x00=OK, 0x01=LOW
30 - Checksm... Checksum (2-byte signed int): = -sum(bytes 4-12)
31
32 */
33
34
35 #include "decoder.h"
36
validate_checksum(r_device * decoder,uint8_t * b,int from,int to,int cs_from,int cs_to)37 static int validate_checksum(r_device *decoder, uint8_t *b, int from, int to, int cs_from, int cs_to)
38 {
39 // Fields cs_from and cs_to hold the 2-byte checksum as signed int
40 int expected = (b[cs_from] << 8) | b[cs_to];
41 int calculated = add_bytes(&b[from], to-from+1);
42 int chk = (calculated + expected) & 0xffff;
43
44 if (chk) {
45 if (decoder->verbose) {
46 fprintf(stderr, "Checksum error in Vaillant VRT340f. Expected: %04x Calculated: %04x\n", expected, calculated);
47 bitrow_printf(&b[from], (to - from + 1) * 8, "Message (data content of bytes %d-%d): ", from, to);
48 }
49 }
50 return !chk;
51 }
52
vaillant_vrt340_callback(r_device * decoder,bitbuffer_t * bitbuffer)53 static int vaillant_vrt340_callback(r_device *decoder, bitbuffer_t *bitbuffer)
54 {
55 uint8_t *b = bitbuffer->bb[0];
56
57 // TODO: Use repeat signal for error checking / correction!
58
59 // each row needs to have at least 128 bits (plus a few more due to bit stuffing)
60 if (bitbuffer->bits_per_row[0] < 128)
61 return DECODE_ABORT_LENGTH;
62
63 // The protocol uses bit-stuffing => remove 0 bit after five consecutive 1 bits
64 // Also, each byte is represented with least significant bit first -> swap them!
65 bitbuffer_t bits = {0};
66 int ones = 0;
67 for (uint16_t k = 0; k < bitbuffer->bits_per_row[0]; k++) {
68 int bit = bitrow_get_bit(b, k);
69 if (bit == 1) {
70 bitbuffer_add_bit(&bits, 1);
71 ones++;
72 } else {
73 if (ones != 5) { // Ignore a 0 bit after five consecutive 1 bits:
74 bitbuffer_add_bit(&bits, 0);
75 }
76 ones = 0;
77 }
78 }
79
80 b = bits.bb[0];
81 uint16_t bitcount = bits.bits_per_row[0];
82
83 // Change to least-significant-bit last (protocol uses least-significant-bit first)
84 reflect_bytes(b, (bitcount - 1) / 8);
85
86 // A correct message has 128 bits plus potentially two extra bits for clock sync at the end
87 if (!(128 <= bitcount && bitcount <= 131) && !(168 <= bitcount && bitcount <= 171))
88 return DECODE_ABORT_LENGTH;
89
90 // "Normal package":
91 if ((b[0] == 0x00) && (b[1] == 0x00) && (b[2] == 0x7e) && (128 <= bitcount && bitcount <= 131)) {
92
93 if (!validate_checksum(decoder, b, /* Data from-to: */3, 11, /*Checksum from-to:*/12, 13)) {
94 return DECODE_FAIL_MIC;
95 }
96
97 // Device ID starts at byte 4:
98 int device_id = (b[3] << 8) | b[4];
99 int heating_mode = (b[10] >> 7); // highest bit indicates automatic (2-point) / analogue mode
100 int target_temperature = (b[10] & 0x7f); // highest bit indicates auto(2-point) / analogue mode
101 int water_preheated = (b[9] & 8) == 0; // bit 4 indicates water: 1=Pre-heat, 0=no pre-heated water
102 int battery_low = b[11] != 0; // if not zero, battery is low
103
104 /* clang-format off */
105 data_t *data = data_make(
106 "model", "", DATA_STRING, "Vaillant-VRT340f",
107 "id", "Device ID", DATA_FORMAT, "0x%04X", DATA_INT, device_id,
108 "heating", "Heating Mode", DATA_STRING, (heating_mode == 0 && target_temperature == 0) ? "OFF" : heating_mode ? "ON (2-point)" : "ON (analogue)",
109 "heating_temp", "Heating Water Temp.", DATA_FORMAT, "%d", DATA_INT, target_temperature,
110 "water", "Pre-heated Water", DATA_STRING, water_preheated ? "ON" : "off",
111 "battery_ok", "Battery", DATA_INT, !battery_low,
112 NULL);
113 /* clang-format on */
114 decoder_output_data(decoder, data);
115
116 return 1;
117 }
118
119 // "RF detection package":
120 if ((b[0] == 0x00) && (b[1] == 0x00) && (b[2] == 0x7E) && (168 <= bitcount && bitcount <= 171)) {
121
122 if (!validate_checksum(decoder, b, /* Data from-to: */ 3, 16, /*Checksum from-to:*/ 17, 18)) {
123 return DECODE_FAIL_MIC;
124 }
125
126 // Device ID starts at byte 12:
127 int device_id = (b[11] << 8) | b[12];
128
129 /* clang-format off */
130 data_t *data = data_make(
131 "model", "", DATA_STRING, "Vaillant-VRT340f",
132 "id", "Device ID", DATA_INT, device_id,
133 NULL);
134 /* clang-format on */
135 decoder_output_data(decoder, data);
136
137 return 1;
138 }
139
140 return DECODE_FAIL_SANITY;
141 }
142
143 static char *output_fields[] = {
144 "model",
145 "id",
146 "heating",
147 "heating_temp",
148 "water",
149 "battery_ok",
150 NULL,
151 };
152
153 r_device vaillant_vrt340f = {
154 .name = "Vaillant calorMatic VRT340f Central Heating Control",
155 .modulation = OOK_PULSE_DMC,
156 .short_width = 836, // half-bit width 836 us
157 .long_width = 1648, // bit width 1648 us
158 .reset_limit = 4000,
159 .tolerance = 120, // us
160 .decode_fn = &vaillant_vrt340_callback,
161 .disabled = 0,
162 .fields = output_fields,
163 };
164