-
Notifications
You must be signed in to change notification settings - Fork 1.4k
/
Copy pathoil_standard.c
155 lines (128 loc) · 5.14 KB
/
oil_standard.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/** @file
Oil tank monitor using manchester encoded FSK/ASK protocol.
Copyright (C) 2017 Christian W. Zuckschwerdt <[email protected]>
based on code Copyright (C) 2015 David Woodhouse
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
*/
#include "decoder.h"
/**
Oil tank monitor using manchester encoded FSK/ASK protocol.
Tested devices:
- APOLLO ULTRASONIC STANDARD (maybe also VISUAL but not SMART, FSK)
- Tekelek TEK377E (E: European, A: American version)
- Beckett Rocket TEK377A (915MHz, ASK)
Should apply to similar Watchman, Beckett, and Apollo devices too.
The sensor sends a single packet once every hour or twice a second
for 11 minutes when in pairing/test mode (pairing needs 35 sec).
depth reading is in cm, lowest reading is ~3, highest is ~305, 0 is invalid
IIII IIII IIII IIII 0FFF L0OP DDDD DDDD
The TEK377E might send an additional 8 zero bits.
example packets are:
010101 01010101 01010111 01101001 10011010 10101001 10100101 10011010 01101010 10011001 10011010 0000
010101 01010101 01011000 10011010 01010110 01101010 10101010 10100101 01101010 10100110 10101001 1111
Start of frame full preamble is depending on first data bit either
01 0101 0101 0101 0101 0111 01
01 0101 0101 0101 0101 1000 10
*/
static int oil_standard_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned row, unsigned bitpos)
{
bitbuffer_t databits = {0};
bitbuffer_manchester_decode(bitbuffer, row, bitpos, &databits, 41);
if (databits.bits_per_row[0] < 32 || databits.bits_per_row[0] > 40 || (databits.bb[0][4] & 0xfe) != 0)
return 0; // TODO: fix calling code to handle negative return values
uint8_t *b = databits.bb[0];
// The unit ID changes when you rebind by holding a magnet to the
// sensor for long enough.
uint16_t unit_id = (b[0] << 8) | b[1];
// 0x01: Rebinding (magnet held to sensor)
// 0x02: High-bit for depth
// 0x04: (always zero?)
// 0x08: Leak/theft alarm
// 0x10: (unknown toggle)
// 0x20: (unknown toggle)
// 0x40: (unknown toggle)
// 0x80: (always zero?)
uint8_t flags = b[2] & ~0x0A;
uint8_t alarm = (b[2] & 0x08) >> 3;
uint16_t depth = 0;
uint16_t binding_countdown = 0;
if (flags & 1) {
// When binding, the countdown counts up from 0x40 to 0x4a
// (as long as you hold the magnet to it for long enough)
// before the device ID changes. The receiver unit needs
// to receive this *strongly* in order to change its
// allegiance.
binding_countdown = b[3];
}
else {
// A depth reading of zero indicates no reading.
depth = ((b[2] & 0x02) << 7) | b[3];
}
/* clang-format off */
data_t *data = data_make(
"model", "", DATA_STRING, "Oil-SonicStd",
"id", "", DATA_FORMAT, "%04x", DATA_INT, unit_id,
"flags", "", DATA_FORMAT, "%02x", DATA_INT, flags,
"alarm", "", DATA_INT, alarm,
"binding_countdown", "", DATA_INT, binding_countdown,
"depth_cm", "", DATA_INT, depth,
NULL);
/* clang-format on */
decoder_output_data(decoder, data);
return 1;
}
/**
Oil tank monitor using manchester encoded FSK/ASK protocol.
@sa oil_standard_decode()
*/
static int oil_standard_callback(r_device *decoder, bitbuffer_t *bitbuffer)
{
uint8_t const preamble_pattern0[2] = {0x55, 0x5D};
uint8_t const preamble_pattern1[2] = {0x55, 0x62};
// End of frame is the last half-bit repeated additional 4 times
unsigned bitpos = 0;
int events = 0;
// Find a preamble with enough bits after it that it could be a complete packet
while ((bitpos = bitbuffer_search(bitbuffer, 0, bitpos, preamble_pattern0, 16)) + 78 <=
bitbuffer->bits_per_row[0]) {
events += oil_standard_decode(decoder, bitbuffer, 0, bitpos + 14);
bitpos += 2;
}
bitpos = 0;
while ((bitpos = bitbuffer_search(bitbuffer, 0, bitpos, preamble_pattern1, 16)) + 78 <=
bitbuffer->bits_per_row[0]) {
events += oil_standard_decode(decoder, bitbuffer, 0, bitpos + 14);
bitpos += 2;
}
return events;
}
static char const *const output_fields[] = {
"model",
"id",
"flags",
"alarm",
"binding_countdown",
"depth_cm",
NULL,
};
r_device const oil_standard = {
.name = "Oil Ultrasonic STANDARD FSK",
.modulation = FSK_PULSE_PCM,
.short_width = 500,
.long_width = 500,
.reset_limit = 2000,
.decode_fn = &oil_standard_callback,
.fields = output_fields,
};
r_device const oil_standard_ask = {
.name = "Oil Ultrasonic STANDARD ASK",
.modulation = OOK_PULSE_PCM,
.short_width = 500,
.long_width = 500,
.reset_limit = 2000,
.decode_fn = &oil_standard_callback,
.fields = output_fields,
};