|
| 1 | +/** @file |
| 2 | + RainPoint HCS012ARF Rain Gauge sensor. |
| 3 | +
|
| 4 | + Copyright (C) 2021 Christian W. Zuckschwerdt <zany@triq.net> |
| 5 | + Copyright (C) 2025 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 | +RainPoint HCS012ARF Rain Gauge sensor. |
| 17 | +
|
| 18 | +Manufacturer: |
| 19 | +- Fujian Baldr Technology Co., Ltd |
| 20 | +
|
| 21 | +RF Information: |
| 22 | +- Seen on 433.92 Mhz. |
| 23 | +- FCC ID : 2AWDBHCS008FRF |
| 24 | +- RF Module used in other RainPoint / Fujian Baldr Technology Co., Ltd devices : HCS008FRF, HCS012ARF, HTV113FRF, HTV213FRF, TTC819, TCS008B |
| 25 | +
|
| 26 | +Description of the HCS012ARF Rain Gauge Sensor: |
| 27 | +- Rainfall Range: 0-9999mm , but 2 bytes identified, missing 1 bit MSB somewhere in the data layout flags |
| 28 | +- Accuracy: ±0.1mm |
| 29 | +- Data Reporting: Every 3 mins |
| 30 | +
|
| 31 | +A Transmission contains ten packets with Manchester coded data, reflected |
| 32 | +
|
| 33 | +Data layout: |
| 34 | +
|
| 35 | + Byte Index 0 1 2 3 4 5 6 7 8 9 |
| 36 | + Sample a5 08 54 03 04 61 03 00 00 c7 [Batt inserted] |
| 37 | + HH[II II II II FB FF RR RR]SS |
| 38 | +
|
| 39 | +- HH: {8} Header, fixed 0xa5 (or 0xa4 when MC Zero bit decoded) |
| 40 | +- ID: {32} Sensor ID, does not change with new battery, looks unique |
| 41 | +- FF: {6} Fixed value, 0x18, may contains 1 bit MSB Rain Gauge |
| 42 | +- B:{1} Low Battery flag = 1, Good Battery = 0 |
| 43 | +- B:{1} Powered on, batteries are inserted = 1, then always = 0 |
| 44 | +- FF:{8} Fixed value, 0x03, may contains 1 bit MSB Rain Gauge |
| 45 | +- RR:{16} little-endian rain gauge value, scale 10 (1 Tip = 0,1 mm), 2 bytes are not enough, max 0xFFFF = 6553.5 mm, 1 bit MSB somewhere in flags ? |
| 46 | +- SS:{8} Byte sum of previous bytes except header [value in the hooks, from ID to Rain gauge]. |
| 47 | +
|
| 48 | +Raw data: |
| 49 | +
|
| 50 | + rtl_433 -X 'n=HCS012ARF,m=OOK_PCM,s=320,l=320,r=1000,g=700,repeats>=5,unique' |
| 51 | +
|
| 52 | +
|
| 53 | +*/ |
| 54 | + |
| 55 | +static int rainpoint_hcs012arf_decode(r_device *decoder, bitbuffer_t *bitbuffer) |
| 56 | +{ |
| 57 | + |
| 58 | + // Find repeats |
| 59 | + int row = bitbuffer_find_repeated_row(bitbuffer, 4, 163); |
| 60 | + if (row < 0) |
| 61 | + return DECODE_ABORT_EARLY; |
| 62 | + |
| 63 | + if (bitbuffer->bits_per_row[row] > 163) |
| 64 | + return DECODE_ABORT_LENGTH; |
| 65 | + |
| 66 | + bitbuffer_t msg = {0}; |
| 67 | + bitbuffer_manchester_decode(bitbuffer, row, 0, &msg, 10 * 2 * 8); // including header |
| 68 | + bitbuffer_invert(&msg); |
| 69 | + |
| 70 | + uint8_t *b = msg.bb[0]; |
| 71 | + reflect_bytes(b, 10); |
| 72 | + |
| 73 | + if (b[0] != 0xa5) // header = 0xa5, could be 0xa4 when MC Zero bit decoded |
| 74 | + return DECODE_ABORT_EARLY; |
| 75 | + |
| 76 | + decoder_log_bitrow(decoder, 2, __func__, b, 10 * 8, "MC and Reflect decoded"); |
| 77 | + |
| 78 | + // Checksum |
| 79 | + int sum = add_bytes(&b[1], 8); // header not part of the sum |
| 80 | + if ((sum & 0xff) != b[9]) { |
| 81 | + decoder_logf(decoder, 2, __func__, "Checksum failed %04x vs %04x", b[9], sum); |
| 82 | + return DECODE_FAIL_MIC; |
| 83 | + } |
| 84 | + |
| 85 | + int id = (b[4] << 24) | (b[3] << 16) | (b[2] << 8) | b[1]; // just a guess, little-endian |
| 86 | + int flags1 = b[5]; // may contains 1 bit MSB for Rain Gauge |
| 87 | + int bat_low = (flags1 & 0x02) >> 1; |
| 88 | + //int bat_in = (flags1 & 0x01); // power up, battery inserted = 1, then always 0 |
| 89 | + int flags2 = b[6]; // may contains 1 bit MSB for Rain Gauge |
| 90 | + int rain_raw = (b[8] << 8) | b[7]; // little-endian |
| 91 | + float rain_mm = rain_raw * 0.1f; |
| 92 | + |
| 93 | + /* clang-format off */ |
| 94 | + data_t *data = data_make( |
| 95 | + "model", "", DATA_STRING, "RainPoint-HCS012ARF", |
| 96 | + "id", "", DATA_INT, id, // decimal value reported at Rainpoint application |
| 97 | + "flags1", "Flags 1", DATA_FORMAT, "%02x", DATA_INT, (flags1 >> 2), // remove battery flags |
| 98 | + "flags2", "Flags 2", DATA_FORMAT, "%02x", DATA_INT, flags2, |
| 99 | + "battery_ok", "Battery", DATA_INT, !bat_low, |
| 100 | + "rain_mm", "Total rainfall", DATA_FORMAT, "%.1f mm", DATA_DOUBLE, rain_mm, |
| 101 | + "mic", "Integrity", DATA_STRING, "CHECKSUM", |
| 102 | + NULL); |
| 103 | + /* clang-format on */ |
| 104 | + |
| 105 | + decoder_output_data(decoder, data); |
| 106 | + return 1; |
| 107 | +} |
| 108 | + |
| 109 | +static char const *const output_fields[] = { |
| 110 | + "model", |
| 111 | + "id", |
| 112 | + "flags1", |
| 113 | + "flags2", |
| 114 | + "battery_ok", |
| 115 | + "rain_mm", |
| 116 | + "mic", |
| 117 | + NULL, |
| 118 | +}; |
| 119 | + |
| 120 | +r_device const rainpoint_hcs012arf = { |
| 121 | + .name = "RainPoint HCS012ARF Rain Gauge sensor", |
| 122 | + .modulation = OOK_PULSE_PCM, |
| 123 | + .short_width = 320, |
| 124 | + .long_width = 320, |
| 125 | + .reset_limit = 1000, |
| 126 | + .gap_limit = 700, |
| 127 | + .decode_fn = &rainpoint_hcs012arf_decode, |
| 128 | + .fields = output_fields, |
| 129 | +}; |
0 commit comments