1 /** @file
2     Inovalley kw9015b rain and Temperature weather station.
3 
4     Copyright (C) 2015 Alexandre Coffignal
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 Inovalley kw9015b rain and Temperature weather station.
13 
14 Also TFA-Dostmann rain-sensor 30.3161 (see #1531) with a 0.45mm rain per tip.
15 
16 Data layout:
17 
18     IIII??RR BRRPtttt TTTTTTTT rrrrrrrr CCCC
19 
20 - I : 4-bit ID
21 - ? : 2-bit unknown always 00
22 - T : 12-bit Temp in C, signed, scaled by 10
23 - R : 12-bit Rain
24 - B : 1-bit battery (0 means battery ok, 1 means low battery)
25 - P : 1-bit power up (when batteries are inserted is 1, then always 0)
26 - C : 4-bit Checksum (nibble sum)
27 */
28 
29 #include "decoder.h"
30 
kw9015b_callback(r_device * decoder,bitbuffer_t * bitbuffer)31 static int kw9015b_callback(r_device *decoder, bitbuffer_t *bitbuffer)
32 {
33     data_t *data;
34     int row;
35     uint8_t *b;
36     int temp_raw, rain, device;
37     unsigned char chksum;
38     float temp_c;
39 
40     row = bitbuffer_find_repeated_row(bitbuffer, 3, 36);
41     if (row < 0)
42         return DECODE_ABORT_EARLY;
43 
44     if (bitbuffer->bits_per_row[row] > 36)
45         return DECODE_ABORT_LENGTH;
46 
47     b = bitbuffer->bb[row];
48 
49     device   = (reverse8(b[0]) & 0x0f);
50     temp_raw = (int16_t)((reverse8(b[2]) << 8) | (reverse8(b[1]) & 0xf0)); // sign-extend
51     temp_c   = (temp_raw >> 4) * 0.1f;
52     rain     = ((reverse8(b[0]) & 0xc0) << 4) | ((reverse8(b[1]) & 0x06) << 7) | reverse8(b[3]);
53     chksum   = ((reverse8(b[0]) >> 4) + (reverse8(b[0]) & 0x0f) +
54               (reverse8(b[1]) >> 4) + (reverse8(b[1]) & 0x0f) +
55               (reverse8(b[2]) >> 4) + (reverse8(b[2]) & 0x0f) +
56               (reverse8(b[3]) >> 4) + (reverse8(b[3]) & 0x0f));
57     int battery_low = b[1] >> 7;
58 
59     if ((chksum & 0x0f) != (reverse8(b[4]) & 0x0f))
60         return DECODE_FAIL_MIC;
61 
62     /* clang-format off */
63     data = data_make(
64             "model",            "",             DATA_STRING, "Inovalley-kw9015b",
65             "id",               "",             DATA_INT,    device,
66             "battery_ok",       "Battery",      DATA_INT,    !battery_low,
67             "temperature_C",    "Temperature",  DATA_FORMAT, "%.1f C", DATA_DOUBLE, temp_c,
68             "rain",             "Rain Count",   DATA_INT,    rain, // TODO: remove this
69             "rain_mm",          "Rain Total",   DATA_DOUBLE, rain * 0.45f,
70             NULL);
71     /* clang-format on */
72 
73     decoder_output_data(decoder, data);
74     return 1;
75 }
76 
77 static char *kw9015b_csv_output_fields[] = {
78         "model",
79         "id",
80         "battery_ok",
81         "temperature_C",
82         "rain", // TODO: remove this
83         "rain_mm",
84         NULL,
85 };
86 
87 r_device kw9015b = {
88         .name        = "Inovalley kw9015b, TFA Dostmann 30.3161 (Rain and temperature sensor)",
89         .modulation  = OOK_PULSE_PPM,
90         .short_width = 2000,
91         .long_width  = 4000,
92         .gap_limit   = 4800,
93         .reset_limit = 10000,
94         .decode_fn   = &kw9015b_callback,
95         .disabled    = 1,
96         .fields      = kw9015b_csv_output_fields,
97 };
98