Bugfixes
[rtl-433.git] / src / devices / oil_watchman.c
1 /* Oil tank monitor using Si4320 framed FSK protocol
2  *
3  * Tested devices:
4  * Sensor Systems Watchman Sonic
5  *
6  * Copyright © 2015 David Woodhouse
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  */
13 #include "rtl_433.h"
14 #include "pulse_demod.h"
15 #include "util.h"
16
17
18 // Start of frame preamble is 111000xx
19 static const unsigned char preamble_pattern = 0xe0;
20
21 // End of frame is 00xxxxxx or 11xxxxxx depending on final data bit
22 static const unsigned char postamble_pattern[2] = { 0x00, 0xc0 };
23
24 static int oil_watchman_callback(bitbuffer_t *bitbuffer) {
25         uint8_t *b;
26         uint32_t unit_id;
27         uint16_t depth = 0;
28         uint16_t binding_countdown = 0;
29         uint8_t flags;
30         uint8_t maybetemp;
31         double temperature;
32         char time_str[LOCAL_TIME_BUFLEN];
33         data_t *data;
34         unsigned bitpos = 0;
35         bitbuffer_t databits = {0};
36         int events = 0;
37
38         local_time_str(0, time_str);
39
40         // Find a preamble with enough bits after it that it could be a complete packet
41         while ((bitpos = bitbuffer_search(bitbuffer, 0, bitpos, &preamble_pattern, 6)) + 136 <=
42                bitbuffer->bits_per_row[0]) {
43
44                 // Skip the matched preamble bits to point to the data
45                 bitpos += 6;
46
47                 bitpos = bitbuffer_manchester_decode(bitbuffer, 0, bitpos, &databits, 64);
48                 if (databits.bits_per_row[0] != 64)
49                         continue;
50
51                 b = databits.bb[0];
52
53                 // Check for postamble, depending on last data bit
54                 if (bitbuffer_search(bitbuffer, 0, bitpos, &postamble_pattern[b[7] & 1], 2) != bitpos)
55                         continue;
56
57                 if (b[7] != crc8le(b, 7, 0x31, 0))
58                         continue;
59
60                 // The unit ID changes when you rebind by holding a magnet to the
61                 // sensor for long enough; it seems to be time-based.
62                 unit_id = (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3];
63
64                 // 0x01: Rebinding (magnet held to sensor)
65                 // 0x08: Leak/theft alarm
66                 // top three bits seem also to vary with temperature (independently of maybetemp)
67                 flags = b[4];
68
69                 // Not entirely sure what this is but it might be inversely
70                 // proportional to temperature.
71                 maybetemp = b[5] >> 2;
72                 temperature = (double)(145.0 - 5.0 * maybetemp) / 3.0;
73                 if (flags & 1)
74                         // When binding, the countdown counts up from 0x51 to 0x5a
75                         // (as long as you hold the magnet to it for long enough)
76                         // before the device ID changes. The receiver unit needs
77                         // to receive this *strongly* in order to change its
78                         // allegiance.
79                         binding_countdown = b[6];
80                 else
81                         // A depth reading of zero indicates no reading. Even with
82                         // the sensor flat down on a table, it still reads about 13.
83                         depth = ((b[5] & 3) << 8) | b[6];
84
85                 data = data_make("time", "", DATA_STRING, time_str,
86                                  "model", "", DATA_STRING, "Oil Watchman",
87                                  "id", "", DATA_FORMAT, "%06x", DATA_INT, unit_id,
88                                  "flags", "", DATA_FORMAT, "%02x", DATA_INT, flags,
89                                  "maybetemp", "", DATA_INT, maybetemp,
90                                  "temperature_C", "", DATA_DOUBLE, temperature,
91                                  "binding_countdown", "", DATA_INT, binding_countdown,
92                                  "depth", "", DATA_INT, depth,
93                                  NULL);
94                 data_acquired_handler(data);
95                 events++;
96         }
97         return events;
98 };
99
100 static char *output_fields[] = {
101         "time",
102         "model",
103         "id",
104         "flags",
105         "maybetemp",
106         "temperature_C",
107         "binding_countdown",
108         "depth",
109         NULL
110 };
111
112 r_device oil_watchman = {
113         .name                   = "Watchman Sonic / Apollo Ultrasonic / Beckett Rocket oil tank monitor",
114         .modulation             = FSK_PULSE_PCM,
115         .short_limit    = 1000,
116         .long_limit     = 1000, // NRZ
117         .reset_limit    = 4000,
118         .json_callback  = &oil_watchman_callback,
119         .disabled               = 0,
120         .fields                 = output_fields,
121 };