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