|
| 1 | +/** @file |
| 2 | + Mueller Hot Rod water meter. |
| 3 | +
|
| 4 | + Copyright (C) 2024 Christian W. Zuckschwerdt <[email protected]> |
| 5 | + Copyright (C) 2024 Bruno OCTAU (ProfBoc75) |
| 6 | +
|
| 7 | + This program is free software; you can redistribute it and/or modify |
| 8 | + it under the terms of the GNU General Public License as published by |
| 9 | + the Free Software Foundation; either version 2 of the License, or |
| 10 | + (at your option) any later version. |
| 11 | +*/ |
| 12 | + |
| 13 | +#include "decoder.h" |
| 14 | + |
| 15 | +/** |
| 16 | +Mueller Hot Rod water meter. |
| 17 | +
|
| 18 | +S.a. #2719 Decoder desired for Mueller Systems Hot Rod transmitter (water meter), open by "howardtopher", related to Hod Rod v2 transmitter |
| 19 | +S.a. #2752 Decoder for Mueller Hot Rod V1 Water Meter Transmitter, open by "dolai1", related to Hod Rod v1 transmitter |
| 20 | +
|
| 21 | +Both version v1 and v2 protocols look same format. |
| 22 | +
|
| 23 | +Flex decoder: |
| 24 | +
|
| 25 | + rtl_433 -X 'n=hotrod,m=FSK_PCM,s=26,l=26,r=2500,preamble=feb100' |
| 26 | +
|
| 27 | +Raw RF Signal: |
| 28 | +
|
| 29 | + {136}ffffffffffd62002884cc2c092f1201f80 |
| 30 | + {135}fff555555fd62002884cc2c092f1201f80 |
| 31 | + {135}ffeaaaaabfac40051099858125e2403f00 |
| 32 | + {134}000002aabfac40051099858125e54015c0 |
| 33 | + {134}00000000000040051099858125e54015c0 |
| 34 | +
|
| 35 | +The preamble is not stable because of the GFSK encoding not well handle by rtl_433. |
| 36 | +
|
| 37 | +Data layout: |
| 38 | + PP PP PP YY YY YY 0 1 2 3 4 5 6 7 8 9 10 11 ... |
| 39 | + aa aa aa fe b1 00 II II II II GG GG GG GF CC ?? ?? ?? ... |
| 40 | +
|
| 41 | +- PP: {xx} Preamble 0xaaaaa but not stable, see RF samples above. |
| 42 | +- YY: {24} Sync word 0xfeb100 |
| 43 | +- II: {32} Device ID |
| 44 | +- GG: {28} 7 niblles BCD water cumulative volume, US liquid gallon |
| 45 | +- FF: {4} Flag, protocol version, battery_low ??? to be confirmed later. |
| 46 | +- CC: {8} CRC-8/UTI, poly 0x07, init 0x00, xorout 0x55 |
| 47 | +- ??: extra trailing bit not used, related to GFSK/FSK encoding. |
| 48 | +
|
| 49 | +*/ |
| 50 | + |
| 51 | +static int mueller_hotrod_decode(r_device *decoder, bitbuffer_t *bitbuffer) |
| 52 | +{ |
| 53 | + uint8_t const preamble_pattern[] = {0xfe, 0xb1, 0x00}; |
| 54 | + |
| 55 | + if (bitbuffer->num_rows != 1) { |
| 56 | + decoder_log(decoder, 2, __func__, "Row check failed"); |
| 57 | + return DECODE_ABORT_EARLY; |
| 58 | + } |
| 59 | + |
| 60 | + // 3 byte for the sync word + 9 byte for data = 96 bits in total, too short if less |
| 61 | + if (bitbuffer->bits_per_row[0] < 96) { |
| 62 | + decoder_log(decoder, 2, __func__, "Len before preamble check failed"); |
| 63 | + return DECODE_ABORT_LENGTH; |
| 64 | + } |
| 65 | + |
| 66 | + // Find the preamble |
| 67 | + unsigned pos = bitbuffer_search(bitbuffer, 0, 0, preamble_pattern, sizeof(preamble_pattern) * 8); |
| 68 | + if ((pos + 9 * 8) >= bitbuffer->bits_per_row[0]) { |
| 69 | + decoder_log(decoder, 2, __func__, "Len after preamble check failed"); |
| 70 | + return DECODE_ABORT_EARLY; |
| 71 | + } |
| 72 | + |
| 73 | + uint8_t b[9]; |
| 74 | + bitbuffer_extract_bytes(bitbuffer, 0, pos + 24, b, 72); // 9 x 8 bit |
| 75 | + decoder_log_bitrow(decoder, 1, __func__, b, sizeof(b) * 8, "MSG"); |
| 76 | + |
| 77 | + // poly 0x07, init 0x00, xorout 0x55 |
| 78 | + int crc_calc = crc8(b, 8, 0x07, 0x00) ^ 0x55; |
| 79 | + if (crc_calc != b[8]) { |
| 80 | + decoder_logf(decoder, 2, __func__, "CRC check failed : %0x %0x", b[8], crc_calc); |
| 81 | + return 0; |
| 82 | + } |
| 83 | + |
| 84 | + char id_str[16]; |
| 85 | + snprintf(id_str, sizeof(id_str), "%02x%02x%02x%02x", b[0], b[1], b[2], b[3]); |
| 86 | + |
| 87 | + // 7 nibbles BCD (28 bit) = volume_gal |
| 88 | + int volume = ((b[4] & 0xf0) >> 4)*1000000+(b[4] & 0x0f)*100000+((b[5] & 0xf0) >> 4)*10000+(b[5] & 0x0f)*1000+((b[6] & 0xf0) >> 4)*100+(b[6] & 0x0f)*10+((b[7] & 0xf0) >> 4); |
| 89 | + int flag = b[7] & 0x0f; |
| 90 | + |
| 91 | + /* clang-format off */ |
| 92 | + data_t *data = data_make( |
| 93 | + "model", "", DATA_STRING, "Mueller-HotRod", |
| 94 | + "id", "", DATA_STRING, id_str, |
| 95 | + "volume_gal", "Volume", DATA_FORMAT, "%u gal", DATA_INT, volume, |
| 96 | + "flag", "Flag", DATA_FORMAT, "%x" , DATA_INT, flag, |
| 97 | + "mic", "Integrity", DATA_STRING, "CRC", |
| 98 | + NULL); |
| 99 | + /* clang-format on */ |
| 100 | + decoder_output_data(decoder, data); |
| 101 | + return 1; |
| 102 | +} |
| 103 | + |
| 104 | +static char const *const output_fields[] = { |
| 105 | + "model", |
| 106 | + "id", |
| 107 | + "volume_gal", |
| 108 | + "flag", |
| 109 | + "mic", |
| 110 | + NULL, |
| 111 | +}; |
| 112 | + |
| 113 | +r_device const mueller_hotrod = { |
| 114 | + .name = "Mueller Hot Rod water meter", |
| 115 | + .modulation = FSK_PULSE_PCM, |
| 116 | + .short_width = 26, |
| 117 | + .long_width = 26, |
| 118 | + .reset_limit = 2500, |
| 119 | + .decode_fn = &mueller_hotrod_decode, |
| 120 | + .fields = output_fields, |
| 121 | +}; |
0 commit comments