1 /** @file
2     WT450 wireless weather sensors protocol.
3 
4     Tested devices:
5     WT260H
6     WT405H
7 
8     Copyright (C) 2015 Tommy Vestermark
9     Copyright (C) 2015 Ladislav Foldyna
10 
11     This program is free software; you can redistribute it and/or modify
12     it under the terms of the GNU General Public License as published by
13     the Free Software Foundation; either version 2 of the License, or
14     (at your option) any later version.
15  */
16 
17 /**
18 source from:
19 http://ala-paavola.fi/jaakko/doku.php?id=wt450h
20 
21 - The signal is FM encoded with clock cycle around 2000 µs
22 - No level shift within the clock cycle translates to a logic 0
23 - One level shift within the clock cycle translates to a logic 1
24 - Each clock cycle begins with a level shift
25 - My timing constants defined below are those observed by my program
26 
27     +---+   +---+   +-------+       +  high
28     |   |   |   |   |       |       |
29     |   |   |   |   |       |       |
30     +   +---+   +---+       +-------+  low
31     ^       ^       ^       ^       ^  clock cycle
32     |   1   |   1   |   0   |   0   |  translates as
33 
34 Each transmission is 36 bits long (i.e. 72 ms).
35 
36 Data is transmitted in pure binary values, NOT BCD-coded.
37 
38 Outdoor sensor transmits data temperature, humidity.
39 Transmissions also include channel code and house code. The sensor transmits
40 every 60 seconds 3 packets.
41 
42     1100 0001 | 0011 0011 | 1000 0011 | 1011 0011 | 0001
43     xxxx ssss | ccxx bhhh | hhhh tttt | tttt tttt | sseo
44 
45 - x: constant
46 - s: House code
47 - c: Channel
48 - b: battery low indicator (0=>OK, 1=>LOW)
49 - h: Humidity
50 - t: Temperature, 12 bit, offset 50, scale 16
51 - s: sequence number of message repeat
52 - e: parity of all even bits
53 - o: parity of all odd bits
54 */
55 
56 #include "decoder.h"
57 
wt450_callback(r_device * decoder,bitbuffer_t * bitbuffer)58 static int wt450_callback(r_device *decoder, bitbuffer_t *bitbuffer)
59 {
60     uint8_t *b = bitbuffer->bb[0];
61     uint8_t humidity;
62     uint8_t temp_whole;
63     uint8_t temp_fraction;
64     uint8_t house_code;
65     uint8_t channel;
66     uint8_t battery_low;
67     int seq;
68     float temp;
69     uint8_t parity;
70     data_t *data;
71 
72     if (bitbuffer->bits_per_row[0] != 36) {
73         if (decoder->verbose)
74             fprintf(stderr, "%s: wrong size of bit per row %d\n", __func__,
75                     bitbuffer->bits_per_row[0]);
76         return DECODE_ABORT_LENGTH;
77     }
78 
79     if (b[0]>>4 != 0xC) {
80         if (decoder->verbose)
81             bitbuffer_printf(bitbuffer, "%s: wrong preamble\n", __func__);
82         return DECODE_ABORT_EARLY;
83     }
84 
85     parity = xor_bytes(b, 5);
86     parity ^= (parity >> 4);
87     parity ^= (parity >> 2);
88     parity &= 0x3;
89 
90     if (parity) {
91         if (decoder->verbose)
92             bitbuffer_printf(bitbuffer, "%s: wrong parity (%x)\n", __func__, parity);
93         return DECODE_FAIL_MIC;
94     }
95 
96     house_code    = b[0] & 0xF;
97     channel       = (b[1] >> 6) + 1;
98     battery_low   = b[1] & 0x8;
99     humidity      = ((b[1] & 0x7) << 4) | (b[2] >> 4);
100     temp_whole    = (b[2] << 4) | (b[3] >> 4);
101     temp_fraction = (b[3] & 0xF);
102     temp          = (temp_whole - 50) + (temp_fraction / 16.0);
103     seq           = (b[4] >> 6);
104 
105     /* clang-format off */
106     data = data_make(
107             "model",            "",             DATA_STRING, "WT450-TH",
108             "id",               "House Code",   DATA_INT,    house_code,
109             "channel",          "Channel",      DATA_INT,    channel,
110             "battery_ok",       "Battery",      DATA_INT,    !battery_low,
111             "temperature_C",    "Temperature",  DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp,
112             "humidity",         "Humidity",     DATA_FORMAT, "%u %%", DATA_INT, humidity,
113             "seq",              "Sequence",     DATA_INT,    seq,
114             NULL);
115     /* clang-format on */
116 
117     decoder_output_data(decoder, data);
118     return 1;
119 }
120 
121 static char *output_fields[] = {
122         "model",
123         "id",
124         "channel",
125         "battery_ok",
126         "temperature_C",
127         "humidity",
128         "seq",
129         NULL,
130 };
131 
132 r_device wt450 = {
133         .name        = "WT450, WT260H, WT405H",
134         .modulation  = OOK_PULSE_DMC,
135         .short_width = 976,  // half-bit width 976 us
136         .long_width  = 1952, // bit width 1952 us
137         .reset_limit = 18000,
138         .tolerance   = 100, // us
139         .decode_fn   = &wt450_callback,
140         .disabled    = 0,
141         .fields      = output_fields,
142 };
143