diff --git a/include/rtl_433.h b/include/rtl_433.h index 303770fa..66405419 100755 --- a/include/rtl_433.h +++ b/include/rtl_433.h @@ -42,7 +42,7 @@ #define MINIMAL_BUF_LENGTH 512 #define MAXIMAL_BUF_LENGTH (256 * 16384) -#define MAX_PROTOCOLS 80 +#define MAX_PROTOCOLS 81 #define SIGNAL_GRABBER_BUFFER (12 * DEFAULT_BUF_LENGTH) /* Supported modulation types */ diff --git a/include/rtl_433_devices.h b/include/rtl_433_devices.h index 3ddb9661..6e1be192 100755 --- a/include/rtl_433_devices.h +++ b/include/rtl_433_devices.h @@ -83,7 +83,8 @@ DECL(vaillant_vrt340f) \ DECL(fineoffset_WH25) \ DECL(fineoffset_WH0530) \ - DECL(ibis_beacon) + DECL(ibis_beacon) \ + DECL(oil_standard) typedef struct { char name[256]; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7bc8ef32..fd8fa14e 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -93,6 +93,7 @@ add_executable(rtl_433 devices/lacrosse_tx35.c devices/vaillant_vrt340f.c devices/ibis_beacon.c + devices/oil_standard.c ) diff --git a/src/Makefile.am b/src/Makefile.am index d72c977a..1e4141e9 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -78,6 +78,7 @@ rtl_433_SOURCES = baseband.c \ devices/rftech.c \ devices/vaillant_vrt340f.c \ devices/wg_pb12v1.c \ - devices/ibis_beacon.c + devices/ibis_beacon.c \ + devices/oil_standard.c rtl_433_LDADD = $(LIBRTLSDR) $(LIBM) diff --git a/src/devices/oil_standard.c b/src/devices/oil_standard.c new file mode 100644 index 00000000..cbde020f --- /dev/null +++ b/src/devices/oil_standard.c @@ -0,0 +1,136 @@ +/* Oil tank monitor using manchester encoded FSK protocol + * + * Tested devices: + * APOLLO ULTRASONIC STANDARD (maybe also VISUAL but not SMART) + * Should apply to similar Watchman and Beckett devices too. + * + * Copyright (C) 2017 Christian W. Zuckschwerdt + * 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 "rtl_433.h" +#include "util.h" + +// 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 +// +// 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 const unsigned char preamble_pattern0[2] = { 0x55, 0x5D }; +static const unsigned char preamble_pattern1[2] = { 0x55, 0x62 }; + +// End of frame is the last half-bit repeated additional 4 times + +static int oil_standard_decode(bitbuffer_t *bitbuffer, unsigned row, unsigned bitpos) { + char time_str[LOCAL_TIME_BUFLEN]; + data_t *data; + uint8_t *b; + uint16_t unit_id; + uint16_t depth = 0; + uint16_t binding_countdown = 0; + uint8_t flags; + uint8_t alarm; + bitbuffer_t databits = {0}; + + local_time_str(0, time_str); + + bitpos = bitbuffer_manchester_decode(bitbuffer, row, bitpos, &databits, 33); + + if (databits.bits_per_row[0] != 32) + return 0; + + b = databits.bb[0]; + + // The unit ID changes when you rebind by holding a magnet to the + // sensor for long enough. + 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: (unkown toggle) + // 0x40: (unkown toggle) + // 0x10: (unkown toggle) + // 0x80: (always zero?) + flags = b[2] & ~0x0A; + alarm = (b[2] & 0x08) >> 3; + + 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]; + + data = data_make( + "time", "", DATA_STRING, time_str, + "model", "", DATA_STRING, "Oil Ultrasonic STANDARD", + "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); + data_acquired_handler(data); + + return 1; +}; + +static int oil_standard_callback(bitbuffer_t *bitbuffer) { + 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, (uint8_t *)&preamble_pattern0, 16)) + 78 <= + bitbuffer->bits_per_row[0]) { + events += oil_standard_decode(bitbuffer, 0, bitpos + 14); + bitpos += 2; + } + + bitpos = 0; + while ((bitpos = bitbuffer_search(bitbuffer, 0, bitpos, (uint8_t *)&preamble_pattern1, 16)) + 78 <= + bitbuffer->bits_per_row[0]) { + events += oil_standard_decode(bitbuffer, 0, bitpos + 14); + bitpos += 2; + } + return events; +} + +static char *output_fields[] = { + "time", + "model", + "id", + "flags", + "alarm", + "binding_countdown", + "depth_cm", + NULL +}; + +r_device oil_standard = { + .name = "Oil Ultrasonic STANDARD", + .modulation = FSK_PULSE_PCM, + .short_limit = 500, + .long_limit = 500, + .reset_limit = 2000, + .json_callback = &oil_standard_callback, + .disabled = 0, + .fields = output_fields, +};