Add Inkbird ITH-20R support

Author: Dmitriy Kozyrev
Provides decoding for the Inkbird ITH-20R temperature and humidity sensors.
These sensors have an exceptionally long preamble which required increasing the size of the bitbuffer and the FSK pulse detection max length substantially.
This commit is contained in:
Eric Hagan 2020-08-07 21:18:37 -05:00 committed by Christian W. Zuckschwerdt
parent c34ae3df67
commit 367e121ef4
7 changed files with 173 additions and 0 deletions

View file

@ -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

View file

@ -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")

View file

@ -201,6 +201,7 @@
DECL(markisol) \
DECL(govee) \
DECL(cmr113) \
DECL(inkbird_ith20r) \
/* Add new decoders here. */

View file

@ -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

View file

@ -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,
};

View file

@ -240,6 +240,7 @@ COPY ..\..\libusb\MS64\dll\libusb*.dll $(TargetDir)</Command>
<ClCompile Include="..\src\devices\ibis_beacon.c" />
<ClCompile Include="..\src\devices\ikea_sparsnas.c" />
<ClCompile Include="..\src\devices\infactory.c" />
<ClCompile Include="..\src\devices\inkbird_ith20r.c" />
<ClCompile Include="..\src\devices\inovalley-kw9015b.c" />
<ClCompile Include="..\src\devices\insteon.c" />
<ClCompile Include="..\src\devices\interlogix.c" />

View file

@ -457,6 +457,9 @@
<ClCompile Include="..\src\devices\infactory.c">
<Filter>Source Files\devices</Filter>
</ClCompile>
<ClCompile Include="..\src\devices\inkbird_ith20r.c">
<Filter>Source Files\devices</Filter>
</ClCompile>
<ClCompile Include="..\src\devices\inovalley-kw9015b.c">
<Filter>Source Files\devices</Filter>
</ClCompile>