diff --git a/README.md b/README.md index ff37ebc4..2a801f6c 100644 --- a/README.md +++ b/README.md @@ -277,6 +277,7 @@ See [CONTRIBUTING.md](./docs/CONTRIBUTING.md). [191] Markisol, E-Motion, BOFU, Rollerhouse, BF-30x, BF-415 curtain remote [192] Govee Water Leak Dectector H5054, Door Contact Sensor B5023 [193] Clipsal CMR113 Cent-a-meter power meter + [194] Inkbird ITH-20R temperature humidity sensor * Disabled by default, use -R n or -G diff --git a/conf/rtl_433.example.conf b/conf/rtl_433.example.conf index 6f57458b..86785a46 100644 --- a/conf/rtl_433.example.conf +++ b/conf/rtl_433.example.conf @@ -414,6 +414,7 @@ stop_after_successful_events false protocol 191 # Markisol, E-Motion, BOFU, Rollerhouse, BF-30x, BF-415 curtain remote protocol 192 # Govee Water Leak Dectector H5054, Door Contact Sensor B5023 protocol 193 # Clipsal CMR113 Cent-a-meter power meter + protocol 194 # Inkbird ITH-20R temperature humidity sensor ## Flex devices (command line option "-X") diff --git a/include/rtl_433_devices.h b/include/rtl_433_devices.h index ef295623..2d59579b 100644 --- a/include/rtl_433_devices.h +++ b/include/rtl_433_devices.h @@ -201,6 +201,7 @@ DECL(markisol) \ DECL(govee) \ DECL(cmr113) \ + DECL(inkbird_ith20r) \ /* Add new decoders here. */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 213916d7..d6589898 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -111,6 +111,7 @@ add_library(r_433 STATIC devices/ibis_beacon.c devices/ikea_sparsnas.c devices/infactory.c + devices/inkbird_ith20r.c devices/inovalley-kw9015b.c devices/insteon.c devices/interlogix.c diff --git a/src/devices/inkbird_ith20r.c b/src/devices/inkbird_ith20r.c new file mode 100644 index 00000000..2af2efd9 --- /dev/null +++ b/src/devices/inkbird_ith20r.c @@ -0,0 +1,165 @@ +/** @file + Decoder for Inkbird ITH-20R. + + Copyright (C) 2020 Dmitriy Kozyrev + + 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. +*/ + +/** +Decoder for Inkbird ITH-20R. + +https://www.ink-bird.com/products-data-logger-ith20r.html + +The compact 3-in-1 multifunction outdoor sensor transmits the data on 433.92 MHz. +The device uses FSK-PCM encoding, +The device sends a transmission every ~80 sec. + +Decoding borrowed from https://groups.google.com/forum/#!topic/rtl_433/oeExmwoBI0w + +- Total packet length 14563 bits: +- Preamble: aa aa aa ... aa aa (14400 on-off sync bits) +- Sync Word (16 bits): 2DD4 +- Data (147 bits): +- Byte Sample Comment +- 0-2 D3910F Always the same across devices, a device type? +- 3 00 00 - normal work , 40 - unlink sensor (button pressed 5s), 80 - battery replaced +- 4 01 Changes from 1 to 2 if external sensor present +- 5-6 0301 Unknown (also seen 0201), sw version? +- 7 58 Battery % 0-100 +- 8-9 A221 Device id, always the same for a sensor but each sensor is different +- 10-11 D600 Temperature in C * 10, little endian, so 0xD200 is 210, 21.0C or 69.8F +- 12-13 F400 Temperature C * 10 for the external sensor, 0x1405 if not connected +- 14-15 D301 Relative humidity % * 10, little endian, so 0xC501 is 453 or 45.3% +- 16-17 38FB CRC16 +- 18 0 Unknown 3 bits (seen 0 and 2) + +CRC16 (bytes 0-15), without sync word): +poly=0x8005 init=0x2f61 refin=true refout=true xorout=0x0000 check=0x3583 residue=0x0000 + +To look at unknown data fields run with -vv key. + +Decoder written by Dmitriy Kozyrev, 2020 +*/ + +#include "decoder.h" + +static const uint8_t preamble_pattern[] = { 0xaa, 0xaa, 0xaa, 0x2d, 0xd4 }; + +#define INKBIRD_ITH20R_CRC_POLY 0xA001 // reflected 0x8005 +#define INKBIRD_ITH20R_CRC_INIT 0x86F4 // reflected 0x2f61 + + +static int inkbird_ith20r_callback(r_device *decoder, bitbuffer_t *bitbuffer) +{ + data_t *data; + uint8_t msg[19]; + + if ( (bitbuffer->num_rows != 1) + || (bitbuffer->bits_per_row[0] < 187) + /*|| (bitbuffer->bits_per_row[0] > 14563)*/ ) { + if (decoder->verbose > 1) { + fprintf(stderr, "%s bit_per_row %u out of range\n", __func__, bitbuffer->bits_per_row[0]); + } + return DECODE_ABORT_LENGTH; // Unrecognized data + } + + unsigned start_pos = bitbuffer_search(bitbuffer, 0, 0, + preamble_pattern, sizeof (preamble_pattern) * 8); + + if (start_pos == bitbuffer->bits_per_row[0]) { + return DECODE_FAIL_SANITY; // Not found preamble + } + + start_pos += sizeof (preamble_pattern) * 8; + unsigned len = bitbuffer->bits_per_row[0] - start_pos; + + if (decoder->verbose > 1) { + fprintf(stderr, "%s start_pos=%u\n", __func__, start_pos); + fprintf(stderr, "%s len=%u\n", __func__, len); + } + + if (((len + 7) / 8) < sizeof (msg)) { + if (decoder->verbose) { + fprintf(stderr, "%s %u too short\n", __func__, len); + } + return DECODE_ABORT_LENGTH; // Message too short + } + // truncate any excessive bits + len = MIN(len, sizeof (msg) * 8); + + bitbuffer_extract_bytes(bitbuffer, 0, start_pos, msg, len); + + // CRC check + uint16_t crc_calculated = crc16lsb(msg, 16, INKBIRD_ITH20R_CRC_POLY, INKBIRD_ITH20R_CRC_INIT); + uint16_t crc_received = msg[17] << 8 | msg[16]; + + if (decoder->verbose > 1) { + fprintf(stderr, "%s CRC 0x%04X = 0x%04X\n", __func__, crc_calculated, crc_received); + } + + if (crc_received != crc_calculated) { + if (decoder->verbose) { + fprintf(stderr, "%s CRC check failed (0x%04X != 0x%04X)\n", __func__, crc_calculated, crc_received); + } + return DECODE_FAIL_MIC; + } + + uint32_t subtype = (msg[3] << 24 | msg[2] << 16 | msg[1] << 8 | msg[0]); + int sensor_num = msg[4]; + uint16_t word56 = (msg[6] << 8 | msg[5]); + int battery = msg[7]; + uint16_t sensor_id = (msg[9] << 8 | msg[8]); + float temperature = (float)((int16_t)(msg[11] << 8 | msg[10])) / 10.0; + float temperature_ext = (float)((int16_t)(msg[13] << 8 | msg[12])) / 10.0; + float humidity = (float)(msg[15] << 8 | msg[14]) / 10.0; + uint8_t word18 = msg[18]; + + if (decoder->verbose) { + fprintf(stderr, "%s dword0-3= 0x%08X\n", __func__, subtype); + fprintf(stderr, "%s word5-6= 0x%04X\n", __func__, word56); + fprintf(stderr, "%s byte18= 0x%02X\n", __func__, word18); + } + + data = data_make( + "model", "", DATA_STRING, "Inkbird ITH-20R", + "id", "", DATA_INT, sensor_id, + "battery", "Battery", DATA_INT, battery, + "sensor_num", "", DATA_INT, sensor_num, + "mic", "Integrity", DATA_STRING, "CRC", + "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temperature, + "temperature2_C", "Temperature2", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temperature_ext, + "humidity", "Humidity", DATA_FORMAT, "%.1f %%", DATA_DOUBLE, humidity, + NULL); + + decoder_output_data(decoder, data); + + return 1; +} + +static char *output_fields[] = { + "model", + "id", + "battery", + "sensor_num", + "mic", + "temperature_C", + "temperature2_C", + "humidity", + NULL +}; + +r_device inkbird_ith20r = { + .name = "Inkbird ITH-20R temperature humidity sensor", + .modulation = FSK_PULSE_PCM, + .sync_width = 0, // No sync bit used + .short_width = 100, // Width of a '0' gap + .long_width = 100, // Width of a '1' gap + .reset_limit = 4000, // Maximum gap size before End Of Message [us] + .decode_fn = &inkbird_ith20r_callback, + .disabled = 0, + .fields = output_fields, +}; diff --git a/vs15/rtl_433.vcxproj b/vs15/rtl_433.vcxproj index 9af50dfa..c929d8cd 100644 --- a/vs15/rtl_433.vcxproj +++ b/vs15/rtl_433.vcxproj @@ -240,6 +240,7 @@ COPY ..\..\libusb\MS64\dll\libusb*.dll $(TargetDir) + diff --git a/vs15/rtl_433.vcxproj.filters b/vs15/rtl_433.vcxproj.filters index efe34635..2b8d8888 100644 --- a/vs15/rtl_433.vcxproj.filters +++ b/vs15/rtl_433.vcxproj.filters @@ -457,6 +457,9 @@ Source Files\devices + + Source Files\devices + Source Files\devices